diff options
author | Simon Hausmann <simon.hausmann@digia.com> | 2014-03-17 21:26:04 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-03-18 09:05:57 +0100 |
commit | fa24ef3d721a7b94d0c5abbc6c9558e74bdb0f3d (patch) | |
tree | 37bac24bafc1d2b879d76ac225272912538d072d /src/qml/compiler/qqmlcodegenerator.cpp | |
parent | 3f7951c04ef474f81eda2134b67c4e4020fe39d1 (diff) |
Cleanup, part 2
* QQmlCodeGenerator -> QQmlIR::IRBuilder (it doesn't generate code, it
generates the Object/Property/Signal/etc. IR of the .qml file, that's
going to get transformed to QV4::CompiledData::*)
* ParsedQML -> QQmlIR::Document
Change-Id: I329e858487b66e1ae528d44316761f5dd34b79f4
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'src/qml/compiler/qqmlcodegenerator.cpp')
-rw-r--r-- | src/qml/compiler/qqmlcodegenerator.cpp | 1818 |
1 files changed, 0 insertions, 1818 deletions
diff --git a/src/qml/compiler/qqmlcodegenerator.cpp b/src/qml/compiler/qqmlcodegenerator.cpp deleted file mode 100644 index c826049ef7..0000000000 --- a/src/qml/compiler/qqmlcodegenerator.cpp +++ /dev/null @@ -1,1818 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlcodegenerator_p.h" - -#include <private/qv4compileddata_p.h> -#include <private/qqmljsparser_p.h> -#include <private/qqmljslexer_p.h> -#include <private/qqmlcompiler_p.h> -#include <private/qqmlglobal_p.h> -#include <QCoreApplication> - -#ifdef CONST -#undef CONST -#endif - -QT_USE_NAMESPACE - -static const quint32 emptyStringIndex = 0; - -DEFINE_BOOL_CONFIG_OPTION(lookupHints, QML_LOOKUP_HINTS); - -using namespace QmlIR; - -#define COMPILE_EXCEPTION(location, desc) \ - { \ - recordError(location, desc); \ - return false; \ - } - -void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int id, const QQmlJS::AST::SourceLocation &loc) -{ - inheritedTypeNameIndex = typeNameIndex; - - location.line = loc.startLine; - location.column = loc.startColumn; - - idIndex = id; - indexOfDefaultProperty = -1; - properties = pool->New<PoolList<Property> >(); - qmlSignals = pool->New<PoolList<Signal> >(); - bindings = pool->New<PoolList<Binding> >(); - functions = pool->New<PoolList<Function> >(); - functionsAndExpressions = pool->New<PoolList<CompiledFunctionOrExpression> >(); - runtimeFunctionIndices = 0; - declarationsOverride = 0; -} - -QString Object::sanityCheckFunctionNames(const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation) -{ - QSet<int> functionNames; - for (Function *f = functions->first; f; f = f->next) { - QQmlJS::AST::FunctionDeclaration *function = f->functionDeclaration; - Q_ASSERT(function); - *errorLocation = function->identifierToken; - if (functionNames.contains(f->nameIndex)) - return tr("Duplicate method name"); - functionNames.insert(f->nameIndex); - - for (QmlIR::Signal *s = qmlSignals->first; s; s = s->next) { - if (s->nameIndex == f->nameIndex) - return tr("Duplicate method name"); - } - - const QString name = function->name.toString(); - if (name.at(0).isUpper()) - return tr("Method names cannot begin with an upper case letter"); - if (illegalNames.contains(name)) - return tr("Illegal method name"); - } - return QString(); // no error -} - -QString Object::appendSignal(Signal *signal) -{ - Object *target = declarationsOverride; - if (!target) - target = this; - - for (Signal *s = qmlSignals->first; s; s = s->next) { - if (s->nameIndex == signal->nameIndex) - return tr("Duplicate signal name"); - } - - target->qmlSignals->append(signal); - return QString(); // no error -} - -QString Object::appendProperty(Property *prop, const QString &propertyName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation) -{ - Object *target = declarationsOverride; - if (!target) - target = this; - - for (Property *p = target->properties->first; p; p = p->next) - if (p->nameIndex == prop->nameIndex) - return tr("Duplicate property name"); - - if (propertyName.constData()->isUpper()) - return tr("Property names cannot begin with an upper case letter"); - - const int index = target->properties->append(prop); - if (isDefaultProperty) { - if (target->indexOfDefaultProperty != -1) { - *errorLocation = defaultToken; - return tr("Duplicate default property"); - } - target->indexOfDefaultProperty = index; - } - return QString(); // no error -} - -void Object::appendFunction(QmlIR::Function *f) -{ - Object *target = declarationsOverride; - if (!target) - target = this; - target->functions->append(f); -} - -QString Object::appendBinding(Binding *b, bool isListBinding) -{ - const bool bindingToDefaultProperty = (b->propertyNameIndex == 0); - if (!isListBinding && !bindingToDefaultProperty - && b->type != QV4::CompiledData::Binding::Type_GroupProperty - && b->type != QV4::CompiledData::Binding::Type_AttachedProperty - && !(b->flags & QV4::CompiledData::Binding::IsOnAssignment)) { - Binding *existing = findBinding(b->propertyNameIndex); - if (existing && existing->isValueBinding() == b->isValueBinding() && !(existing->flags & QV4::CompiledData::Binding::IsOnAssignment)) - return tr("Property value set multiple times"); - } - if (bindingToDefaultProperty) - insertSorted(b); - else - bindings->prepend(b); - return QString(); // no error -} - -Binding *Object::findBinding(quint32 nameIndex) const -{ - for (Binding *b = bindings->first; b; b = b->next) - if (b->propertyNameIndex == nameIndex) - return b; - return 0; -} - -void Object::insertSorted(Binding *b) -{ - Binding *insertionPoint = bindings->findSortedInsertionPoint<QV4::CompiledData::Location, QV4::CompiledData::Binding, &QV4::CompiledData::Binding::valueLocation>(b); - bindings->insertAfter(insertionPoint, b); -} - -QStringList Signal::parameterStringList(const QStringList &stringPool) const -{ - QStringList result; - result.reserve(parameters->count); - for (SignalParameter *param = parameters->first; param; param = param->next) - result << stringPool.at(param->nameIndex); - return result; -} - -QQmlCodeGenerator::QQmlCodeGenerator(const QSet<QString> &illegalNames) - : illegalNames(illegalNames) - , _object(0) - , _propertyDeclaration(0) - , jsGenerator(0) -{ -} - -bool QQmlCodeGenerator::generateFromQml(const QString &code, const QUrl &url, const QString &urlString, ParsedQML *output) -{ - this->url = url; - QQmlJS::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 QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) { - - if (m.isWarning()) { - qWarning("%s:%d : %s", qPrintable(urlString), m.loc.startLine, qPrintable(m.message)); - continue; - } - - recordError(m.loc, m.message); - } - return false; - } - program = parser.ast(); - Q_ASSERT(program); - } - - output->code = code; - output->program = program; - - qSwap(_imports, output->imports); - qSwap(_pragmas, output->pragmas); - qSwap(_objects, output->objects); - qSwap(_typeReferences, output->typeReferences); - this->pool = output->jsParserEngine.pool(); - this->jsGenerator = &output->jsGenerator; - - Q_ASSERT(registerString(QString()) == emptyStringIndex); - - sourceCode = code; - - accept(program->headers); - - if (program->members->next) { - QQmlError error; - QQmlJS::AST::SourceLocation loc = program->members->next->firstSourceLocation(); - recordError(loc, QCoreApplication::translate("QQmlParser", "Unexpected object definition")); - return false; - } - - QQmlJS::AST::UiObjectDefinition *rootObject = QQmlJS::AST::cast<QQmlJS::AST::UiObjectDefinition*>(program->members->member); - Q_ASSERT(rootObject); - if (defineQMLObject(&output->indexOfRootObject, rootObject)) - collectTypeReferences(); - - qSwap(_imports, output->imports); - qSwap(_pragmas, output->pragmas); - qSwap(_objects, output->objects); - qSwap(_typeReferences, output->typeReferences); - return errors.isEmpty(); -} - -bool QQmlCodeGenerator::isSignalPropertyName(const QString &name) -{ - if (name.length() < 3) return false; - if (!name.startsWith(QStringLiteral("on"))) return false; - int ns = name.length(); - for (int i = 2; i < ns; ++i) { - const QChar curr = name.at(i); - if (curr.unicode() == '_') continue; - if (curr.isUpper()) return true; - return false; - } - return false; // consists solely of underscores - invalid. -} - -bool QQmlCodeGenerator::visit(QQmlJS::AST::UiArrayMemberList *ast) -{ - return QQmlJS::AST::Visitor::visit(ast); -} - -bool QQmlCodeGenerator::visit(QQmlJS::AST::UiProgram *) -{ - Q_ASSERT(!"should not happen"); - return false; -} - -bool QQmlCodeGenerator::visit(QQmlJS::AST::UiObjectDefinition *node) -{ - // The grammar can't distinguish between two different definitions here: - // Item { ... } - // versus - // font { ... } - // The former is a new binding with no property name and "Item" as type name, - // and the latter is a binding to the font property with no type name but - // only initializer. - - QQmlJS::AST::UiQualifiedId *lastId = node->qualifiedTypeNameId; - while (lastId->next) - lastId = lastId->next; - bool isType = lastId->name.unicode()->isUpper(); - if (isType) { - int idx = 0; - if (!defineQMLObject(&idx, node)) - return false; - const QQmlJS::AST::SourceLocation nameLocation = node->qualifiedTypeNameId->identifierToken; - appendBinding(nameLocation, nameLocation, emptyStringIndex, idx); - } else { - int idx = 0; - if (!defineQMLObject(&idx, /*qualfied type name id*/0, node->qualifiedTypeNameId->firstSourceLocation(), node->initializer, /*declarations should go here*/_object)) - return false; - appendBinding(node->qualifiedTypeNameId, idx); - } - return false; -} - -bool QQmlCodeGenerator::visit(QQmlJS::AST::UiObjectBinding *node) -{ - int idx = 0; - if (!defineQMLObject(&idx, node->qualifiedTypeNameId, node->qualifiedTypeNameId->firstSourceLocation(), node->initializer)) - return false; - appendBinding(node->qualifiedId, idx, node->hasOnToken); - return false; -} - -bool QQmlCodeGenerator::visit(QQmlJS::AST::UiScriptBinding *node) -{ - appendBinding(node->qualifiedId, node->statement); - return false; -} - -bool QQmlCodeGenerator::visit(QQmlJS::AST::UiArrayBinding *node) -{ - const QQmlJS::AST::SourceLocation qualifiedNameLocation = node->qualifiedId->identifierToken; - Object *object = 0; - QQmlJS::AST::UiQualifiedId *name = node->qualifiedId; - if (!resolveQualifiedId(&name, &object)) - return false; - - qSwap(_object, object); - - const int propertyNameIndex = registerString(name->name.toString()); - - if (bindingsTarget()->findBinding(propertyNameIndex) != 0) { - recordError(name->identifierToken, tr("Property value set multiple times")); - return false; - } - - QVarLengthArray<QQmlJS::AST::UiArrayMemberList *, 16> memberList; - QQmlJS::AST::UiArrayMemberList *member = node->members; - while (member) { - memberList.append(member); - member = member->next; - } - for (int i = memberList.count() - 1; i >= 0; --i) { - member = memberList.at(i); - QQmlJS::AST::UiObjectDefinition *def = QQmlJS::AST::cast<QQmlJS::AST::UiObjectDefinition*>(member->member); - - int idx = 0; - if (!defineQMLObject(&idx, def)) - return false; - appendBinding(qualifiedNameLocation, name->identifierToken, propertyNameIndex, idx, /*isListItem*/ true); - } - - qSwap(_object, object); - return false; -} - -bool QQmlCodeGenerator::visit(QQmlJS::AST::UiHeaderItemList *list) -{ - return QQmlJS::AST::Visitor::visit(list); -} - -bool QQmlCodeGenerator::visit(QQmlJS::AST::UiObjectInitializer *ast) -{ - return QQmlJS::AST::Visitor::visit(ast); -} - -bool QQmlCodeGenerator::visit(QQmlJS::AST::UiObjectMemberList *ast) -{ - return QQmlJS::AST::Visitor::visit(ast); -} - -bool QQmlCodeGenerator::visit(QQmlJS::AST::UiParameterList *ast) -{ - return QQmlJS::AST::Visitor::visit(ast); -} - -bool QQmlCodeGenerator::visit(QQmlJS::AST::UiQualifiedId *id) -{ - return QQmlJS::AST::Visitor::visit(id); -} - -void QQmlCodeGenerator::accept(QQmlJS::AST::Node *node) -{ - QQmlJS::AST::Node::acceptChild(node, this); -} - -bool QQmlCodeGenerator::defineQMLObject(int *objectIndex, QQmlJS::AST::UiQualifiedId *qualifiedTypeNameId, const QQmlJS::AST::SourceLocation &location, QQmlJS::AST::UiObjectInitializer *initializer, Object *declarationsOverride) -{ - if (QQmlJS::AST::UiQualifiedId *lastName = qualifiedTypeNameId) { - while (lastName->next) - lastName = lastName->next; - if (!lastName->name.constData()->isUpper()) { - recordError(lastName->identifierToken, tr("Expected type name")); - return false; - } - } - - Object *obj = New<Object>(); - _objects.append(obj); - *objectIndex = _objects.size() - 1; - qSwap(_object, obj); - - _object->init(pool, registerString(asString(qualifiedTypeNameId)), emptyStringIndex, location); - _object->declarationsOverride = declarationsOverride; - - // A new object is also a boundary for property declarations. - Property *declaration = 0; - qSwap(_propertyDeclaration, declaration); - - accept(initializer); - - qSwap(_propertyDeclaration, declaration); - - qSwap(_object, obj); - - if (!errors.isEmpty()) - return false; - - QQmlJS::AST::SourceLocation loc; - QString error = obj->sanityCheckFunctionNames(illegalNames, &loc); - if (!error.isEmpty()) { - recordError(loc, error); - return false; - } - - return true; -} - -bool QQmlCodeGenerator::visit(QQmlJS::AST::UiImport *node) -{ - QString uri; - QV4::CompiledData::Import *import = New<QV4::CompiledData::Import>(); - - if (!node->fileName.isNull()) { - uri = node->fileName.toString(); - - if (uri.endsWith(QLatin1String(".js"))) { - import->type = QV4::CompiledData::Import::ImportScript; - } else { - import->type = QV4::CompiledData::Import::ImportFile; - } - } else { - import->type = QV4::CompiledData::Import::ImportLibrary; - uri = asString(node->importUri); - } - - import->qualifierIndex = emptyStringIndex; - - // Qualifier - if (!node->importId.isNull()) { - QString qualifier = node->importId.toString(); - if (!qualifier.at(0).isUpper()) { - recordError(node->importIdToken, QCoreApplication::translate("QQmlParser","Invalid import qualifier ID")); - return false; - } - if (qualifier == QLatin1String("Qt")) { - recordError(node->importIdToken, QCoreApplication::translate("QQmlParser","Reserved name \"Qt\" cannot be used as an qualifier")); - 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)) { - recordError(node->importIdToken, QCoreApplication::translate("QQmlParser","Script import qualifiers must be unique.")); - return false; - } - } - - } else if (import->type == QV4::CompiledData::Import::ImportScript) { - recordError(node->fileNameToken, QCoreApplication::translate("QQmlParser","Script import requires a qualifier")); - return false; - } - - if (node->versionToken.isValid()) { - extractVersion(textRefAt(node->versionToken), &import->majorVersion, &import->minorVersion); - } else if (import->type == QV4::CompiledData::Import::ImportLibrary) { - recordError(node->importIdToken, QCoreApplication::translate("QQmlParser","Library import requires a version")); - return false; - } else { - // For backward compatibility in how the imports are loaded we - // must otherwise initialize the major and minor version to -1. - import->majorVersion = -1; - import->minorVersion = -1; - } - - import->location.line = node->importToken.startLine; - import->location.column = node->importToken.startColumn; - - import->uriIndex = registerString(uri); - - _imports.append(import); - - return false; -} - -bool QQmlCodeGenerator::visit(QQmlJS::AST::UiPragma *node) -{ - Pragma *pragma = New<Pragma>(); - - // For now the only valid pragma is Singleton, so lets validate the input - if (!node->pragmaType->name.isNull()) - { - if (QLatin1String("Singleton") == node->pragmaType->name) - { - pragma->type = Pragma::PragmaSingleton; - } else { - recordError(node->pragmaToken, QCoreApplication::translate("QQmlParser","Pragma requires a valid qualifier")); - return false; - } - } else { - recordError(node->pragmaToken, QCoreApplication::translate("QQmlParser","Pragma requires a valid qualifier")); - return false; - } - - pragma->location.line = node->pragmaToken.startLine; - pragma->location.column = node->pragmaToken.startColumn; - _pragmas.append(pragma); - - return false; -} - -static QStringList astNodeToStringList(QQmlJS::AST::Node *node) -{ - if (node->kind == QQmlJS::AST::Node::Kind_IdentifierExpression) { - QString name = - static_cast<QQmlJS::AST::IdentifierExpression *>(node)->name.toString(); - return QStringList() << name; - } else if (node->kind == QQmlJS::AST::Node::Kind_FieldMemberExpression) { - QQmlJS::AST::FieldMemberExpression *expr = static_cast<QQmlJS::AST::FieldMemberExpression *>(node); - - QStringList rv = astNodeToStringList(expr->base); - if (rv.isEmpty()) - return rv; - rv.append(expr->name.toString()); - return rv; - } - return QStringList(); -} - -bool QQmlCodeGenerator::visit(QQmlJS::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 == QQmlJS::AST::UiPublicMember::Signal) { - Signal *signal = New<Signal>(); - QString signalName = node->name.toString(); - signal->nameIndex = registerString(signalName); - - QQmlJS::AST::SourceLocation loc = node->typeToken; - signal->location.line = loc.startLine; - signal->location.column = loc.startColumn; - - signal->parameters = New<PoolList<SignalParameter> >(); - - QQmlJS::AST::UiParameterList *p = node->parameters; - while (p) { - const QStringRef &memberType = p->type; - - if (memberType.isEmpty()) { - recordError(node->typeToken, QCoreApplication::translate("QQmlParser","Expected parameter type")); - 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, static_cast<int>(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 { - QString errStr = QCoreApplication::translate("QQmlParser","Invalid signal parameter type: "); - errStr.append(memberType.toString()); - recordError(node->typeToken, errStr); - return false; - } - } else { - // the parameter is a known basic type - param->type = type->type; - param->customTypeNameIndex = emptyStringIndex; - } - - param->nameIndex = registerString(p->name.toString()); - param->location.line = p->identifierToken.startLine; - param->location.column = p->identifierToken.startColumn; - signal->parameters->append(param); - p = p->next; - } - - if (signalName.at(0).isUpper()) - COMPILE_EXCEPTION(node->identifierToken, tr("Signal names cannot begin with an upper case letter")); - - if (illegalNames.contains(signalName)) - COMPILE_EXCEPTION(node->identifierToken, tr("Illegal signal name")); - - QString error = _object->appendSignal(signal); - if (!error.isEmpty()) { - recordError(node->identifierToken, error); - return false; - } - } else { - const QStringRef &memberType = node->memberType; - const QStringRef &name = node->name; - - bool typeFound = false; - QV4::CompiledData::Property::Type type; - - if ((unsigned)memberType.length() == strlen("alias") && - QHashedString::compare(memberType.constData(), "alias", static_cast<int>(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, static_cast<int>(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", static_cast<int>(strlen("list")))) { - type = QV4::CompiledData::Property::CustomList; - } else { - recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Invalid property type modifier")); - return false; - } - typeFound = true; - } else if (!node->typeModifier.isNull()) { - recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Unexpected property type modifier")); - return false; - } - - if (!typeFound) { - recordError(node->typeToken, QCoreApplication::translate("QQmlParser","Expected property type")); - return false; - } - - Property *property = New<Property>(); - property->flags = 0; - if (node->isReadonlyMember) - property->flags |= QV4::CompiledData::Property::IsReadOnly; - property->type = type; - if (type >= QV4::CompiledData::Property::Custom) - property->customTypeNameIndex = registerString(memberType.toString()); - else - property->customTypeNameIndex = emptyStringIndex; - - const QString propName = name.toString(); - property->nameIndex = registerString(propName); - - QQmlJS::AST::SourceLocation loc = node->firstSourceLocation(); - property->location.line = loc.startLine; - property->location.column = loc.startColumn; - - property->aliasPropertyValueIndex = emptyStringIndex; - - if (type == QV4::CompiledData::Property::Alias) { - if (!node->statement && !node->binding) - COMPILE_EXCEPTION(loc, tr("No property alias location")); - - QQmlJS::AST::SourceLocation rhsLoc; - if (node->binding) - rhsLoc = node->binding->firstSourceLocation(); - else if (node->statement) - rhsLoc = node->statement->firstSourceLocation(); - else - rhsLoc = node->semicolonToken; - property->aliasLocation.line = rhsLoc.startLine; - property->aliasLocation.column = rhsLoc.startColumn; - - QStringList alias; - - if (QQmlJS::AST::ExpressionStatement *stmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement*>(node->statement)) { - alias = astNodeToStringList(stmt->expression); - if (alias.isEmpty()) { - if (isStatementNodeScript(node->statement)) { - COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>")); - } else { - COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias location")); - } - } - } else { - COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>")); - } - - if (alias.count() < 1 || alias.count() > 3) - COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>")); - - property->aliasIdValueIndex = registerString(alias.first()); - - QString propertyValue = alias.value(1); - if (alias.count() == 3) { - propertyValue += QLatin1Char('.'); - propertyValue += alias.at(2); - } - property->aliasPropertyValueIndex = registerString(propertyValue); - } - QQmlJS::AST::SourceLocation errorLocation; - QString error; - - if (illegalNames.contains(propName)) - error = tr("Illegal property name"); - else - error = _object->appendProperty(property, propName, node->isDefaultMember, node->defaultToken, &errorLocation); - - if (!error.isEmpty()) { - if (errorLocation.startLine == 0) - errorLocation = node->identifierToken; - - recordError(errorLocation, error); - return false; - } - - qSwap(_propertyDeclaration, property); - if (node->binding) { - // process QML-like initializers (e.g. property Object o: Object {}) - QQmlJS::AST::Node::accept(node->binding, this); - } else if (node->statement && type != QV4::CompiledData::Property::Alias) { - appendBinding(node->identifierToken, node->identifierToken, _propertyDeclaration->nameIndex, node->statement); - } - qSwap(_propertyDeclaration, property); - } - - return false; -} - -bool QQmlCodeGenerator::visit(QQmlJS::AST::UiSourceElement *node) -{ - if (QQmlJS::AST::FunctionDeclaration *funDecl = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration *>(node->sourceElement)) { - CompiledFunctionOrExpression *foe = New<CompiledFunctionOrExpression>(); - foe->node = funDecl; - foe->nameIndex = registerString(funDecl->name.toString()); - foe->disableAcceleratedLookups = false; - const int index = _object->functionsAndExpressions->append(foe); - - Function *f = New<Function>(); - f->functionDeclaration = funDecl; - QQmlJS::AST::SourceLocation loc = funDecl->identifierToken; - f->location.line = loc.startLine; - f->location.column = loc.startColumn; - f->index = index; - f->nameIndex = registerString(funDecl->name.toString()); - _object->appendFunction(f); - } else { - recordError(node->firstSourceLocation(), QCoreApplication::translate("QQmlParser","JavaScript declaration outside Script element")); - } - return false; -} - -QString QQmlCodeGenerator::asString(QQmlJS::AST::UiQualifiedId *node) -{ - QString s; - - for (QQmlJS::AST::UiQualifiedId *it = node; it; it = it->next) { - s.append(it->name); - - if (it->next) - s.append(QLatin1Char('.')); - } - - return s; -} - -QStringRef QQmlCodeGenerator::asStringRef(QQmlJS::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.toInt(); - *min = 0; - } else { - *maj = string.left(dot).toInt(); - *min = string.mid(dot + 1).toInt(); - } - } -} - -QStringRef QQmlCodeGenerator::textRefAt(const QQmlJS::AST::SourceLocation &first, const QQmlJS::AST::SourceLocation &last) const -{ - return QStringRef(&sourceCode, first.offset, last.offset + last.length - first.offset); -} - -void QQmlCodeGenerator::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement) -{ - QQmlJS::AST::SourceLocation loc = statement->firstSourceLocation(); - binding->valueLocation.line = loc.startLine; - binding->valueLocation.column = loc.startColumn; - binding->type = QV4::CompiledData::Binding::Type_Invalid; - if (_propertyDeclaration && (_propertyDeclaration->flags & QV4::CompiledData::Property::IsReadOnly)) - binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration; - - QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement); - if (exprStmt) { - QQmlJS::AST::ExpressionNode * const expr = exprStmt->expression; - if (QQmlJS::AST::StringLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(expr)) { - binding->type = QV4::CompiledData::Binding::Type_String; - binding->stringIndex = registerString(lit->value.toString()); - } else if (expr->kind == QQmlJS::AST::Node::Kind_TrueLiteral) { - binding->type = QV4::CompiledData::Binding::Type_Boolean; - binding->value.b = true; - } else if (expr->kind == QQmlJS::AST::Node::Kind_FalseLiteral) { - binding->type = QV4::CompiledData::Binding::Type_Boolean; - binding->value.b = false; - } else if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(expr)) { - binding->type = QV4::CompiledData::Binding::Type_Number; - binding->value.d = lit->value; - } else { - - if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) { - if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) { - binding->type = QV4::CompiledData::Binding::Type_Number; - binding->value.d = -lit->value; - } - } - } - } - - // Do binding instead - if (binding->type == QV4::CompiledData::Binding::Type_Invalid) { - binding->type = QV4::CompiledData::Binding::Type_Script; - - CompiledFunctionOrExpression *expr = New<CompiledFunctionOrExpression>(); - expr->node = statement; - expr->nameIndex = registerString(QStringLiteral("expression for ") + stringAt(binding->propertyNameIndex)); - expr->disableAcceleratedLookups = false; - const int index = bindingsTarget()->functionsAndExpressions->append(expr); - binding->value.compiledScriptIndex = index; - - QQmlJS::AST::Node *nodeForString = statement; - if (exprStmt) - nodeForString = exprStmt->expression; - binding->stringIndex = registerString(asStringRef(nodeForString).toString()); - } -} - -void QQmlCodeGenerator::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value) -{ - const QQmlJS::AST::SourceLocation qualifiedNameLocation = name->identifierToken; - Object *object = 0; - if (!resolveQualifiedId(&name, &object)) - return; - qSwap(_object, object); - appendBinding(qualifiedNameLocation, name->identifierToken, registerString(name->name.toString()), value); - qSwap(_object, object); -} - -void QQmlCodeGenerator::appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment) -{ - const QQmlJS::AST::SourceLocation qualifiedNameLocation = name->identifierToken; - Object *object = 0; - if (!resolveQualifiedId(&name, &object, isOnAssignment)) - return; - qSwap(_object, object); - appendBinding(qualifiedNameLocation, name->identifierToken, registerString(name->name.toString()), objectIndex, /*isListItem*/false, isOnAssignment); - qSwap(_object, object); -} - -void QQmlCodeGenerator::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value) -{ - if (stringAt(propertyNameIndex) == QStringLiteral("id")) { - setId(nameLocation, value); - return; - } - - Binding *binding = New<Binding>(); - binding->propertyNameIndex = propertyNameIndex; - binding->location.line = nameLocation.startLine; - binding->location.column = nameLocation.startColumn; - binding->flags = 0; - setBindingValue(binding, value); - QString error = bindingsTarget()->appendBinding(binding, /*isListBinding*/false); - if (!error.isEmpty()) { - recordError(qualifiedNameLocation, error); - } -} - -void QQmlCodeGenerator::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem, bool isOnAssignment) -{ - if (stringAt(propertyNameIndex) == QStringLiteral("id")) { - recordError(nameLocation, tr("Invalid component id specification")); - return; - } - - Binding *binding = New<Binding>(); - binding->propertyNameIndex = propertyNameIndex; - binding->location.line = nameLocation.startLine; - binding->location.column = nameLocation.startColumn; - - const Object *obj = _objects.at(objectIndex); - binding->valueLocation = obj->location; - - binding->flags = 0; - - if (_propertyDeclaration && (_propertyDeclaration->flags & QV4::CompiledData::Property::IsReadOnly)) - binding->flags |= QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration; - - // No type name on the initializer means it must be a group property - if (_objects.at(objectIndex)->inheritedTypeNameIndex == emptyStringIndex) - binding->type = QV4::CompiledData::Binding::Type_GroupProperty; - else - binding->type = QV4::CompiledData::Binding::Type_Object; - - if (isOnAssignment) - binding->flags |= QV4::CompiledData::Binding::IsOnAssignment; - if (isListItem) - binding->flags |= QV4::CompiledData::Binding::IsListItem; - - binding->value.objectIndex = objectIndex; - QString error = bindingsTarget()->appendBinding(binding, isListItem); - if (!error.isEmpty()) { - recordError(qualifiedNameLocation, error); - } -} - -Object *QQmlCodeGenerator::bindingsTarget() const -{ - if (_propertyDeclaration && _object->declarationsOverride) - return _object->declarationsOverride; - return _object; -} - -bool QQmlCodeGenerator::setId(const QQmlJS::AST::SourceLocation &idLocation, QQmlJS::AST::Statement *value) -{ - QQmlJS::AST::SourceLocation loc = value->firstSourceLocation(); - QStringRef str; - - QQmlJS::AST::Node *node = value; - if (QQmlJS::AST::ExpressionStatement *stmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(node)) { - if (QQmlJS::AST::StringLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(stmt->expression)) { - str = lit->value; - node = 0; - } else - node = stmt->expression; - } - - if (node && str.isEmpty()) - str = asStringRef(node); - - if (str.isEmpty()) - COMPILE_EXCEPTION(loc, tr( "Invalid empty ID")); - - QChar ch = str.at(0); - if (ch.isLetter() && !ch.isLower()) - COMPILE_EXCEPTION(loc, tr( "IDs cannot start with an uppercase letter")); - - QChar u(QLatin1Char('_')); - if (!ch.isLetter() && ch != u) - COMPILE_EXCEPTION(loc, tr( "IDs must start with a letter or underscore")); - - for (int ii = 1; ii < str.count(); ++ii) { - ch = str.at(ii); - if (!ch.isLetterOrNumber() && ch != u) - COMPILE_EXCEPTION(loc, tr( "IDs must contain only letters, numbers, and underscores")); - } - - QString idQString(str.toString()); - if (illegalNames.contains(idQString)) - COMPILE_EXCEPTION(loc, tr( "ID illegally masks global JavaScript property")); - - if (_object->idIndex != emptyStringIndex) - COMPILE_EXCEPTION(idLocation, tr("Property value set multiple times")); - - _object->idIndex = registerString(idQString); - _object->locationOfIdProperty.line = idLocation.startLine; - _object->locationOfIdProperty.column = idLocation.startColumn; - - return true; -} - -bool QQmlCodeGenerator::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, Object **object, bool onAssignment) -{ - QQmlJS::AST::UiQualifiedId *qualifiedIdElement = *nameToResolve; - - if (qualifiedIdElement->name == QStringLiteral("id") && qualifiedIdElement->next) - COMPILE_EXCEPTION(qualifiedIdElement->identifierToken, tr( "Invalid use of id property")); - - // If it's a namespace, prepend the qualifier and we'll resolve it later to the correct type. - QString currentName = qualifiedIdElement->name.toString(); - if (qualifiedIdElement->next) { - foreach (QV4::CompiledData::Import* import, _imports) - if (import->qualifierIndex != emptyStringIndex - && stringAt(import->qualifierIndex) == currentName) { - qualifiedIdElement = qualifiedIdElement->next; - currentName += QLatin1Char('.'); - currentName += qualifiedIdElement->name; - - if (!qualifiedIdElement->name.unicode()->isUpper()) - COMPILE_EXCEPTION(qualifiedIdElement->firstSourceLocation(), tr("Expected type name")); - - break; - } - } - - *object = _object; - while (qualifiedIdElement->next) { - const quint32 propertyNameIndex = registerString(currentName); - const bool isAttachedProperty = qualifiedIdElement->name.unicode()->isUpper(); - - Binding *binding = (*object)->findBinding(propertyNameIndex); - if (binding) { - if (isAttachedProperty) { - if (!binding->isAttachedProperty()) - binding = 0; - } else if (!binding->isGroupProperty()) { - binding = 0; - } - } - if (!binding) { - binding = New<Binding>(); - binding->propertyNameIndex = propertyNameIndex; - binding->location.line = qualifiedIdElement->identifierToken.startLine; - binding->location.column = qualifiedIdElement->identifierToken.startColumn; - binding->valueLocation.line = qualifiedIdElement->next->identifierToken.startLine; - binding->valueLocation.column = qualifiedIdElement->next->identifierToken.startColumn; - binding->flags = 0; - - if (onAssignment) - binding->flags |= QV4::CompiledData::Binding::IsOnAssignment; - - if (isAttachedProperty) - binding->type = QV4::CompiledData::Binding::Type_AttachedProperty; - else - binding->type = QV4::CompiledData::Binding::Type_GroupProperty; - - int objIndex = 0; - if (!defineQMLObject(&objIndex, 0, QQmlJS::AST::SourceLocation(), 0, 0)) - return false; - binding->value.objectIndex = objIndex; - - QString error = (*object)->appendBinding(binding, /*isListBinding*/false); - if (!error.isEmpty()) { - recordError(qualifiedIdElement->identifierToken, error); - return false; - } - *object = _objects.at(objIndex); - } else { - Q_ASSERT(binding->isAttachedProperty() || binding->isGroupProperty()); - *object = _objects.at(binding->value.objectIndex); - } - - qualifiedIdElement = qualifiedIdElement->next; - if (qualifiedIdElement) - currentName = qualifiedIdElement->name.toString(); - } - *nameToResolve = qualifiedIdElement; - return true; -} - -void QQmlCodeGenerator::recordError(const QQmlJS::AST::SourceLocation &location, const QString &description) -{ - QQmlError error; - error.setUrl(url); - error.setLine(location.startLine); - error.setColumn(location.startColumn); - error.setDescription(description); - errors << error; -} - -void QQmlCodeGenerator::collectTypeReferences() -{ - foreach (Object *obj, _objects) { - if (obj->inheritedTypeNameIndex != emptyStringIndex) { - QV4::CompiledData::TypeReference &r = _typeReferences.add(obj->inheritedTypeNameIndex, obj->location); - r.needsCreation = true; - r.errorWhenNotFound = true; - } - - for (const Property *prop = obj->firstProperty(); prop; prop = prop->next) { - if (prop->type >= QV4::CompiledData::Property::Custom) { - // ### FIXME: We could report the more accurate location here by using prop->location, but the old - // compiler can't and the tests expect it to be the object location right now. - QV4::CompiledData::TypeReference &r = _typeReferences.add(prop->customTypeNameIndex, obj->location); - r.needsCreation = true; - r.errorWhenNotFound = true; - } - } - - for (const Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) - _typeReferences.add(binding->propertyNameIndex, binding->location); - } - } -} - -QQmlScript::LocationSpan QQmlCodeGenerator::location(QQmlJS::AST::SourceLocation start, QQmlJS::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; -} - -bool QQmlCodeGenerator::isStatementNodeScript(QQmlJS::AST::Statement *statement) -{ - if (QQmlJS::AST::ExpressionStatement *stmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement)) { - QQmlJS::AST::ExpressionNode *expr = stmt->expression; - if (QQmlJS::AST::cast<QQmlJS::AST::StringLiteral *>(expr)) - return false; - else if (expr->kind == QQmlJS::AST::Node::Kind_TrueLiteral) - return false; - else if (expr->kind == QQmlJS::AST::Node::Kind_FalseLiteral) - return false; - else if (QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(expr)) - return false; - else { - - if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) { - if (QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) { - return false; - } - } - } - } - - return true; -} - -QV4::CompiledData::QmlUnit *QmlUnitGenerator::generate(ParsedQML &output) -{ - jsUnitGenerator = &output.jsGenerator; - 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<Object*, quint32> objectOffsets; - - int objectsSize = 0; - foreach (Object *o, output.objects) { - objectOffsets.insert(o, unitSize + importSize + objectOffsetTableSize + objectsSize); - objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->signalCount(), o->bindingCount()); - - int signalTableSize = 0; - for (const Signal *s = o->firstSignal(); 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 (Object *o, output.objects) { - *objectTable++ = objectOffsets.value(o); - - QV4::CompiledData::Object *objectToWrite = reinterpret_cast<QV4::CompiledData::Object*>(objectPtr); - objectToWrite->inheritedTypeNameIndex = o->inheritedTypeNameIndex; - objectToWrite->indexOfDefaultProperty = o->indexOfDefaultProperty; - objectToWrite->idIndex = o->idIndex; - objectToWrite->location = o->location; - objectToWrite->locationOfIdProperty = o->locationOfIdProperty; - - quint32 nextOffset = sizeof(QV4::CompiledData::Object); - - objectToWrite->nFunctions = o->functionCount(); - objectToWrite->offsetToFunctions = nextOffset; - nextOffset += objectToWrite->nFunctions * sizeof(quint32); - - objectToWrite->nProperties = o->propertyCount(); - objectToWrite->offsetToProperties = nextOffset; - nextOffset += objectToWrite->nProperties * sizeof(QV4::CompiledData::Property); - - objectToWrite->nSignals = o->signalCount(); - objectToWrite->offsetToSignals = nextOffset; - nextOffset += objectToWrite->nSignals * sizeof(quint32); - - objectToWrite->nBindings = o->bindingCount(); - objectToWrite->offsetToBindings = nextOffset; - nextOffset += objectToWrite->nBindings * sizeof(QV4::CompiledData::Binding); - - quint32 *functionsTable = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToFunctions); - for (const Function *f = o->firstFunction(); f; f = f->next) - *functionsTable++ = o->runtimeFunctionIndices->at(f->index); - - char *propertiesPtr = objectPtr + objectToWrite->offsetToProperties; - for (const Property *p = o->firstProperty(); p; p = p->next) { - QV4::CompiledData::Property *propertyToWrite = reinterpret_cast<QV4::CompiledData::Property*>(propertiesPtr); - *propertyToWrite = *p; - propertiesPtr += sizeof(QV4::CompiledData::Property); - } - - char *bindingPtr = objectPtr + objectToWrite->offsetToBindings; - bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isValueBindingNoAlias); - bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isSignalHandler); - bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isAttachedProperty); - bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isGroupProperty); - bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isValueBindingToAlias); - Q_ASSERT((bindingPtr - objectToWrite->offsetToBindings - objectPtr) / sizeof(QV4::CompiledData::Binding) == unsigned(o->bindingCount())); - - quint32 *signalOffsetTable = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToSignals); - quint32 signalTableSize = 0; - char *signalPtr = objectPtr + nextOffset; - for (const Signal *s = o->firstSignal(); s; s = s->next) { - *signalOffsetTable++ = signalPtr - objectPtr; - QV4::CompiledData::Signal *signalToWrite = reinterpret_cast<QV4::CompiledData::Signal*>(signalPtr); - - signalToWrite->nameIndex = s->nameIndex; - signalToWrite->location = s->location; - signalToWrite->nParameters = s->parameters->count; - - QV4::CompiledData::Parameter *parameterToWrite = reinterpret_cast<QV4::CompiledData::Parameter*>(signalPtr + sizeof(*signalToWrite)); - for (SignalParameter *param = s->parameters->first; param; param = param->next, ++parameterToWrite) - *parameterToWrite = *param; - - int size = QV4::CompiledData::Signal::calculateSize(s->parameters->count); - signalTableSize += size; - signalPtr += size; - } - - objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->signalCount(), o->bindingCount()); - objectPtr += signalTableSize; - } - - // enable flag if we encountered pragma Singleton - foreach (Pragma *p, output.pragmas) { - if (p->type == Pragma::PragmaSingleton) { - qmlUnit->header.flags |= QV4::CompiledData::Unit::IsSingleton; - break; - } - } - - return qmlUnit; -} - -char *QmlUnitGenerator::writeBindings(char *bindingPtr, Object *o, BindingFilter filter) const -{ - for (const Binding *b = o->firstBinding(); b; b = b->next) { - if (!(b->*(filter))()) - continue; - QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast<QV4::CompiledData::Binding*>(bindingPtr); - *bindingToWrite = *b; - if (b->type == QV4::CompiledData::Binding::Type_Script) - bindingToWrite->value.compiledScriptIndex = o->runtimeFunctionIndices->at(b->value.compiledScriptIndex); - bindingPtr += sizeof(QV4::CompiledData::Binding); - } - return bindingPtr; -} - -int QmlUnitGenerator::getStringId(const QString &str) const -{ - return jsUnitGenerator->getStringId(str); -} - -JSCodeGen::JSCodeGen(const QString &fileName, const QString &sourceCode, QV4::IR::Module *jsModule, QQmlJS::Engine *jsEngine, - QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports, const QStringList &stringPool) - : QQmlJS::Codegen(/*strict mode*/false) - , sourceCode(sourceCode) - , jsEngine(jsEngine) - , qmlRoot(qmlRoot) - , imports(imports) - , stringPool(stringPool) - , _disableAcceleratedLookups(false) - , _contextObject(0) - , _scopeObject(0) - , _contextObjectTemp(-1) - , _scopeObjectTemp(-1) - , _importedScriptsTemp(-1) - , _idArrayTemp(-1) -{ - _module = jsModule; - _module->setFileName(fileName); - _fileNameIsUrl = true; -} - -void JSCodeGen::beginContextScope(const JSCodeGen::ObjectIdMapping &objectIds, QQmlPropertyCache *contextObject) -{ - _idObjects = objectIds; - _contextObject = contextObject; - _scopeObject = 0; -} - -void JSCodeGen::beginObjectScope(QQmlPropertyCache *scopeObject) -{ - _scopeObject = scopeObject; -} - -QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions) -{ - QVector<int> runtimeFunctionIndices(functions.size()); - - ScanFunctions scan(this, sourceCode, GlobalCode); - scan.enterEnvironment(0, QmlBinding); - scan.enterQmlScope(qmlRoot, QStringLiteral("context scope")); - foreach (const CompiledFunctionOrExpression &f, functions) { - Q_ASSERT(f.node != qmlRoot); - QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(f.node); - - if (function) - scan.enterQmlFunction(function); - else - scan.enterEnvironment(f.node, QmlBinding); - - scan(function ? function->body : f.node); - scan.leaveEnvironment(); - } - scan.leaveEnvironment(); - scan.leaveEnvironment(); - - _env = 0; - _function = _module->functions.at(defineFunction(QStringLiteral("context scope"), qmlRoot, 0, 0)); - - for (int i = 0; i < functions.count(); ++i) { - const CompiledFunctionOrExpression &qmlFunction = functions.at(i); - QQmlJS::AST::Node *node = qmlFunction.node; - Q_ASSERT(node != qmlRoot); - - QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(node); - - QString name; - if (function) - name = function->name.toString(); - else if (qmlFunction.nameIndex != 0) - name = stringPool.value(qmlFunction.nameIndex); - else - name = QStringLiteral("%qml-expression-entry"); - - QQmlJS::AST::SourceElements *body; - if (function) - body = function->body ? function->body->elements : 0; - else { - // Synthesize source elements. - QQmlJS::MemoryPool *pool = jsEngine->pool(); - - QQmlJS::AST::Statement *stmt = node->statementCast(); - if (!stmt) { - Q_ASSERT(node->expressionCast()); - QQmlJS::AST::ExpressionNode *expr = node->expressionCast(); - stmt = new (pool) QQmlJS::AST::ExpressionStatement(expr); - } - QQmlJS::AST::SourceElement *element = new (pool) QQmlJS::AST::StatementSourceElement(stmt); - body = new (pool) QQmlJS::AST::SourceElements(element); - body = body->finish(); - } - - _disableAcceleratedLookups = qmlFunction.disableAcceleratedLookups; - int idx = defineFunction(name, node, - function ? function->formals : 0, - body); - runtimeFunctionIndices[i] = idx; - } - - qDeleteAll(_envMap); - _envMap.clear(); - return runtimeFunctionIndices; -} - -QQmlPropertyData *JSCodeGen::lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name, bool *propertyExistsButForceNameLookup) -{ - if (propertyExistsButForceNameLookup) - *propertyExistsButForceNameLookup = false; - QQmlPropertyData *pd = cache->property(name, /*object*/0, /*context*/0); - - // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time - if (pd && pd->isFunction()) { - if (propertyExistsButForceNameLookup) - *propertyExistsButForceNameLookup = true; - pd = 0; - } - - if (pd && !cache->isAllowedInRevision(pd)) - pd = 0; - - // Return a copy allocated from our memory pool. Property data pointers can change - // otherwise when the QQmlPropertyCache changes later in the QML type compilation process. - if (pd) { - QQmlPropertyData *copy = pd; - pd = _function->New<QQmlPropertyData>(); - *pd = *copy; - } - return pd; -} - -static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject); - -enum MetaObjectResolverFlags { - AllPropertiesAreFinal = 0x1, - LookupsIncludeEnums = 0x2, - LookupsExcludeProperties = 0x4, - ResolveTypeInformationOnly = 0x8 -}; - -static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject); - -static QV4::IR::Type resolveQmlType(QQmlEnginePrivate *qmlEngine, QV4::IR::MemberExpressionResolver *resolver, QV4::IR::Member *member) -{ - QV4::IR::Type result = QV4::IR::VarType; - - QQmlType *type = static_cast<QQmlType*>(resolver->data); - - if (member->name->constData()->isUpper()) { - bool ok = false; - int value = type->enumValue(*member->name, &ok); - if (ok) { - member->setEnumValue(value); - resolver->clear(); - return QV4::IR::SInt32Type; - } - } - - if (type->isCompositeSingleton()) { - QQmlTypeData *tdata = qmlEngine->typeLoader.getType(type->singletonInstanceInfo()->url); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - initMetaObjectResolver(resolver, qmlEngine->propertyCacheForType(tdata->compiledData()->metaTypeId)); - tdata->release(); - resolver->flags |= AllPropertiesAreFinal; - return resolver->resolveMember(qmlEngine, resolver, member); - } else if (const QMetaObject *attachedMeta = type->attachedPropertiesType()) { - QQmlPropertyCache *cache = qmlEngine->cache(attachedMeta); - initMetaObjectResolver(resolver, cache); - member->setAttachedPropertiesId(type->attachedPropertiesId()); - return resolver->resolveMember(qmlEngine, resolver, member); - } - - resolver->clear(); - return result; -} - -static void initQmlTypeResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlType *qmlType) -{ - resolver->resolveMember = &resolveQmlType; - resolver->data = qmlType; - resolver->extraData = 0; - resolver->flags = 0; -} - -static QV4::IR::Type resolveImportNamespace(QQmlEnginePrivate *, QV4::IR::MemberExpressionResolver *resolver, QV4::IR::Member *member) -{ - QV4::IR::Type result = QV4::IR::VarType; - QQmlTypeNameCache *typeNamespace = static_cast<QQmlTypeNameCache*>(resolver->extraData); - void *importNamespace = resolver->data; - - QQmlTypeNameCache::Result r = typeNamespace->query(*member->name, importNamespace); - if (r.isValid()) { - member->freeOfSideEffects = true; - if (r.scriptIndex != -1) { - // TODO: remember the index and replace with subscript later. - result = QV4::IR::VarType; - } else if (r.type) { - // TODO: Propagate singleton information, so that it is loaded - // through the singleton getter in the run-time. Until then we - // can't accelerate access :( - if (!r.type->isSingleton()) { - initQmlTypeResolver(resolver, r.type); - return QV4::IR::QObjectType; - } - } else { - Q_ASSERT(false); // How can this happen? - } - } - - resolver->clear(); - return result; -} - -static void initImportNamespaceResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlTypeNameCache *imports, const void *importNamespace) -{ - resolver->resolveMember = &resolveImportNamespace; - resolver->data = const_cast<void*>(importNamespace); - resolver->extraData = imports; - resolver->flags = 0; -} - -static QV4::IR::Type resolveMetaObjectProperty(QQmlEnginePrivate *qmlEngine, QV4::IR::MemberExpressionResolver *resolver, QV4::IR::Member *member) -{ - QV4::IR::Type result = QV4::IR::VarType; - QQmlPropertyCache *metaObject = static_cast<QQmlPropertyCache*>(resolver->data); - - if (member->name->constData()->isUpper() && (resolver->flags & LookupsIncludeEnums)) { - const QMetaObject *mo = metaObject->createMetaObject(); - QByteArray enumName = member->name->toUtf8(); - for (int ii = mo->enumeratorCount() - 1; ii >= 0; --ii) { - QMetaEnum metaEnum = mo->enumerator(ii); - bool ok; - int value = metaEnum.keyToValue(enumName.constData(), &ok); - if (ok) { - member->setEnumValue(value); - resolver->clear(); - return QV4::IR::SInt32Type; - } - } - } - - if (qmlEngine && !(resolver->flags & LookupsExcludeProperties)) { - QQmlPropertyData *property = member->property; - if (!property && metaObject) { - if (QQmlPropertyData *candidate = metaObject->property(*member->name, /*object*/0, /*context*/0)) { - const bool isFinalProperty = (candidate->isFinal() || (resolver->flags & AllPropertiesAreFinal)) - && !candidate->isFunction(); - - if (lookupHints() - && !(resolver->flags & AllPropertiesAreFinal) - && !candidate->isFinal() - && !candidate->isFunction() - && candidate->isDirect()) { - qWarning() << "Hint: Access to property" << *member->name << "of" << metaObject->className() << "could be accelerated if it was marked as FINAL"; - } - - if (isFinalProperty && metaObject->isAllowedInRevision(candidate)) { - property = candidate; - member->inhibitTypeConversionOnWrite = true; - if (!(resolver->flags & ResolveTypeInformationOnly)) - member->property = candidate; // Cache for next iteration and isel needs it. - } - } - } - - if (property) { - // Enums cannot be mapped to IR types, they need to go through the run-time handling - // of accepting strings that will then be converted to the right values. - if (property->isEnum()) - return QV4::IR::VarType; - - switch (property->propType) { - case QMetaType::Bool: result = QV4::IR::BoolType; break; - case QMetaType::Int: result = QV4::IR::SInt32Type; break; - case QMetaType::Double: result = QV4::IR::DoubleType; break; - case QMetaType::QString: result = QV4::IR::StringType; break; - default: - if (property->isQObject()) { - if (QQmlPropertyCache *cache = qmlEngine->propertyCacheForType(property->propType)) { - initMetaObjectResolver(resolver, cache); - return QV4::IR::QObjectType; - } - } else if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(property->propType)) { - if (QQmlPropertyCache *cache = qmlEngine->cache(valueType->metaObject())) { - initMetaObjectResolver(resolver, cache); - resolver->flags |= ResolveTypeInformationOnly; - return QV4::IR::QObjectType; - } - } - break; - } - } - } - resolver->clear(); - return result; -} - -static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject) -{ - resolver->resolveMember = &resolveMetaObjectProperty; - resolver->data = metaObject; - resolver->flags = 0; - resolver->isQObjectResolver = true; -} - -void JSCodeGen::beginFunctionBodyHook() -{ - _contextObjectTemp = _block->newTemp(); - _scopeObjectTemp = _block->newTemp(); - _importedScriptsTemp = _block->newTemp(); - _idArrayTemp = _block->newTemp(); - - QV4::IR::Temp *temp = _block->TEMP(_contextObjectTemp); - initMetaObjectResolver(&temp->memberResolver, _contextObject); - move(temp, _block->NAME(QV4::IR::Name::builtin_qml_context_object, 0, 0)); - - temp = _block->TEMP(_scopeObjectTemp); - initMetaObjectResolver(&temp->memberResolver, _scopeObject); - move(temp, _block->NAME(QV4::IR::Name::builtin_qml_scope_object, 0, 0)); - - move(_block->TEMP(_importedScriptsTemp), _block->NAME(QV4::IR::Name::builtin_qml_imported_scripts_object, 0, 0)); - move(_block->TEMP(_idArrayTemp), _block->NAME(QV4::IR::Name::builtin_qml_id_array, 0, 0)); -} - -QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int col) -{ - if (_disableAcceleratedLookups) - return 0; - - Q_UNUSED(line) - Q_UNUSED(col) - // Implement QML lookup semantics in the current file context. - // - // Note: We do not check if properties of the qml scope object or context object - // are final. That's because QML tries to get as close as possible to lexical scoping, - // which means in terms of properties that only those visible at compile time are chosen. - // I.e. access to a "foo" property declared within the same QML component as "property int foo" - // will always access that instance and as integer. If a sub-type implements its own property string foo, - // then that one is not chosen for accesses from within this file, because it wasn't visible at compile - // time. This corresponds to the logic in QQmlPropertyCache::findProperty to find the property associated - // with the correct QML context. - - // Look for IDs first. - foreach (const IdMapping &mapping, _idObjects) - if (name == mapping.name) { - _function->idObjectDependencies.insert(mapping.idIndex); - QV4::IR::Expr *s = subscript(_block->TEMP(_idArrayTemp), _block->CONST(QV4::IR::SInt32Type, mapping.idIndex)); - QV4::IR::Temp *result = _block->TEMP(_block->newTemp()); - _block->MOVE(result, s); - result = _block->TEMP(result->index); - if (mapping.type) { - initMetaObjectResolver(&result->memberResolver, mapping.type); - result->memberResolver.flags |= AllPropertiesAreFinal; - } - result->isReadOnly = true; // don't allow use as lvalue - return result; - } - - { - QQmlTypeNameCache::Result r = imports->query(name); - if (r.isValid()) { - if (r.scriptIndex != -1) { - return subscript(_block->TEMP(_importedScriptsTemp), _block->CONST(QV4::IR::SInt32Type, r.scriptIndex)); - } else if (r.type) { - QV4::IR::Name *typeName = _block->NAME(name, line, col); - // Make sure the run-time loads this through the more efficient singleton getter. - typeName->qmlSingleton = r.type->isCompositeSingleton(); - typeName->freeOfSideEffects = true; - QV4::IR::Temp *result = _block->TEMP(_block->newTemp()); - _block->MOVE(result, typeName); - - result = _block->TEMP(result->index); - initQmlTypeResolver(&result->memberResolver, r.type); - return result; - } else { - Q_ASSERT(r.importNamespace); - QV4::IR::Name *namespaceName = _block->NAME(name, line, col); - namespaceName->freeOfSideEffects = true; - QV4::IR::Temp *result = _block->TEMP(_block->newTemp()); - initImportNamespaceResolver(&result->memberResolver, imports, r.importNamespace); - - _block->MOVE(result, namespaceName); - return _block->TEMP(result->index); - } - } - } - - if (_scopeObject) { - bool propertyExistsButForceNameLookup = false; - QQmlPropertyData *pd = lookupQmlCompliantProperty(_scopeObject, name, &propertyExistsButForceNameLookup); - if (propertyExistsButForceNameLookup) - return 0; - if (pd) { - QV4::IR::Temp *base = _block->TEMP(_scopeObjectTemp); - initMetaObjectResolver(&base->memberResolver, _scopeObject); - return _block->MEMBER(base, _function->newString(name), pd, QV4::IR::Member::MemberOfQmlScopeObject); - } - } - - if (_contextObject) { - bool propertyExistsButForceNameLookup = false; - QQmlPropertyData *pd = lookupQmlCompliantProperty(_contextObject, name, &propertyExistsButForceNameLookup); - if (propertyExistsButForceNameLookup) - return 0; - if (pd) { - QV4::IR::Temp *base = _block->TEMP(_contextObjectTemp); - initMetaObjectResolver(&base->memberResolver, _contextObject); - return _block->MEMBER(base, _function->newString(name), pd, QV4::IR::Member::MemberOfQmlContextObject); - } - } - - // fall back to name lookup at run-time. - return 0; -} - -QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, QObject *object, QQmlContextData *context) -{ - if (notInRevision) *notInRevision = false; - - QQmlPropertyData *d = cache->property(name, object, context); - - // Find the first property - while (d && d->isFunction()) - d = cache->overrideData(d); - - if (d && !cache->isAllowedInRevision(d)) { - if (notInRevision) *notInRevision = true; - return 0; - } else { - return d; - } -} - - -QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision, QObject *object, QQmlContextData *context) -{ - if (notInRevision) *notInRevision = false; - - QQmlPropertyData *d = cache->property(name, object, context); - if (notInRevision) *notInRevision = false; - - while (d && !(d->isFunction())) - d = cache->overrideData(d); - - if (d && !cache->isAllowedInRevision(d)) { - if (notInRevision) *notInRevision = true; - return 0; - } else if (d && d->isSignal()) { - return d; - } - - if (name.endsWith(QStringLiteral("Changed"))) { - QString propName = name.mid(0, name.length() - static_cast<int>(strlen("Changed"))); - - d = property(propName, notInRevision, object, context); - if (d) - return cache->signal(d->notifyIndex); - } - - return 0; -} |