diff options
author | Simon Hausmann <simon.hausmann@digia.com> | 2014-03-06 16:55:09 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-03-07 15:33:19 +0100 |
commit | 99efe4309379482fce5c231885883e359bf85290 (patch) | |
tree | dbb9333a547fa1939bd63bd64136611e9fe82968 /src/qml/qml/qqmlcompiler.cpp | |
parent | 9fc17c08e5635cf112c6194e6c24af2a9c7caf00 (diff) |
Remove old compiler and VME
This removes the bulk of the code. A few smaller cleanups remain, to be
done in smaller changes as they move code around.
Additionally the "optimize" option of qqmlbundle was removed. It called QQmlScript::Parser::preparseData,
which however was not implemented and always returned an empty QByteArray. Therefore "optimize" would not
do anything and the class is gone now :)
Change-Id: I0c265e756704cb53c5250be1f69e4a3e1b6e64d5
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'src/qml/qml/qqmlcompiler.cpp')
-rw-r--r-- | src/qml/qml/qqmlcompiler.cpp | 3922 |
1 files changed, 0 insertions, 3922 deletions
diff --git a/src/qml/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp deleted file mode 100644 index aefd3018c9..0000000000 --- a/src/qml/qml/qqmlcompiler.cpp +++ /dev/null @@ -1,3922 +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 QtQml module 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 "qqmlcompiler_p.h" - -#include "qqmlpropertyvaluesource.h" -#include "qqmlcomponent.h" -#include <private/qmetaobjectbuilder_p.h> -#include "qqmlstringconverters_p.h" -#include "qqmlengine_p.h" -#include "qqmlengine.h" -#include "qqmlcontext.h" -#include "qqmlmetatype_p.h" -#include "qqmlcustomparser_p_p.h" -#include "qqmlcontext_p.h" -#include "qqmlcomponent_p.h" -#include <private/qqmljsast_p.h> -#include <private/qqmljsparser_p.h> -#include <private/qqmljsmemorypool_p.h> -#include "qqmlvmemetaobject_p.h" -#include "qqmlexpression_p.h" -#include "qqmlproperty_p.h" -#include "qqmlscriptstring.h" -#include "qqmlglobal_p.h" -#include "qqmlbinding_p.h" -#include "qqmlabstracturlinterceptor.h" - -#include <QDebug> -#include <QPointF> -#include <QSizeF> -#include <QRectF> -#include <QAtomicInt> -#include <QtCore/qdebug.h> -#include <QtCore/qdatetime.h> -#include <QtCore/qvarlengtharray.h> - -Q_DECLARE_METATYPE(QList<int>) -Q_DECLARE_METATYPE(QList<qreal>) -Q_DECLARE_METATYPE(QList<bool>) -Q_DECLARE_METATYPE(QList<QString>) -Q_DECLARE_METATYPE(QList<QUrl>) - -QT_BEGIN_NAMESPACE - -DEFINE_BOOL_CONFIG_OPTION(compilerDump, QML_COMPILER_DUMP); -DEFINE_BOOL_CONFIG_OPTION(compilerStatDump, QML_COMPILER_STATS); - -using namespace QQmlJS; -using namespace QQmlScript; -using namespace QQmlCompilerTypes; - -static QString id_string(QLatin1String("id")); -static QString on_string(QLatin1String("on")); -static QString Changed_string(QLatin1String("Changed")); -static QString Component_string(QLatin1String("Component")); -static QString Component_module_string(QLatin1String("QML")); -static QString qsTr_string(QLatin1String("qsTr")); -static QString qsTrId_string(QLatin1String("qsTrId")); - -/*! - Instantiate a new QQmlCompiler. -*/ -QQmlCompiler::QQmlCompiler(QQmlPool *pool) -: compileState(0), pool(pool), output(0), engine(0), enginePrivate(0), unitRoot(0), unit(0), cachedComponentTypeRef(-1), - cachedTranslationContextIndex(-1), componentStats(0) -{ - if (compilerStatDump()) - componentStats = pool->New<ComponentStats>(); -} - -/*! - Returns true if the last call to compile() caused errors. - - \sa errors() -*/ -bool QQmlCompiler::isError() const -{ - return !exceptions.isEmpty(); -} - -/*! - Return the list of errors from the last call to compile(), or an empty list - if there were no errors. -*/ -QList<QQmlError> QQmlCompiler::errors() const -{ - return exceptions; -} - -/*! - Returns true if \a name refers to an attached property, false otherwise. - - Attached property names are those that start with a capital letter. -*/ -bool QQmlCompiler::isAttachedPropertyName(const QString &name) -{ - return isAttachedPropertyName(QHashedStringRef(&name)); -} - -bool QQmlCompiler::isAttachedPropertyName(const QHashedStringRef &name) -{ - return !name.isEmpty() && name.at(0).isUpper(); -} - -/*! - Returns true if \a name refers to a signal property, false otherwise. - - Signal property names are those that start with "on", followed by a first - character which is either a capital letter or one or more underscores followed - by a capital letter, which is then followed by other allowed characters. - - Note that although ECMA-262r3 supports dollarsigns and escaped unicode - character codes in property names, for simplicity and performance reasons - QML only supports letters, numbers and underscores. -*/ -bool QQmlCompiler::isSignalPropertyName(const QString &name) -{ - return isSignalPropertyName(QStringRef(&name)); -} - -bool QQmlCompiler::isSignalPropertyName(const QHashedStringRef &name) -{ - if (name.length() < 3) return false; - if (!name.startsWith(on_string)) 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. -} - -/*! - \macro COMPILE_EXCEPTION - \internal - Inserts an error into the QQmlCompiler error list, and returns false - (failure). - - \a token is used to source the error line and column, and \a desc is the - error itself. \a desc can be an expression that can be piped into QDebug. - - For example: - - \code - COMPILE_EXCEPTION(property, tr("Error for property \"%1\"").arg(property->name)); - \endcode -*/ -#define COMPILE_EXCEPTION_LOCATION(line, column, desc) \ - { \ - QQmlError error; \ - error.setUrl(output->url); \ - error.setLine(line); \ - error.setColumn(column); \ - error.setDescription(desc.trimmed()); \ - exceptions << error; \ - return false; \ - } - -#define COMPILE_EXCEPTION(token, desc) \ - COMPILE_EXCEPTION_LOCATION((token)->location.start.line, (token)->location.start.column, desc) - -/*! - \macro COMPILE_CHECK - \internal - Returns false if \a is false, otherwise does nothing. -*/ -#define COMPILE_CHECK(a) \ - { \ - if (!a) return false; \ - } - -/*! - Returns true if literal \a v can be assigned to property \a prop, otherwise - false. - - This test corresponds to action taken by genLiteralAssignment(). Any change - made here, must have a corresponding action in genLiteralAssigment(). -*/ -bool QQmlCompiler::testLiteralAssignment(QQmlScript::Property *prop, QQmlScript::Value *v) -{ - const QQmlScript::Variant &value = v->value; - - if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration) - COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); - - if (prop->core.isEnum()) { - QMetaProperty p = prop->parent->metatype->firstCppMetaObject()->property(prop->index); - int enumValue; - bool ok; - if (p.isFlagType()) { - enumValue = p.enumerator().keysToValue(value.asString().toUtf8().constData(), &ok); - } else - enumValue = p.enumerator().keyToValue(value.asString().toUtf8().constData(), &ok); - - if (!ok) - COMPILE_EXCEPTION(v, tr("Invalid property assignment: unknown enumeration")); - - v->value = QQmlScript::Variant((double)enumValue); - return true; - } - - int type = prop->type; - - switch(type) { - case QMetaType::QVariant: - break; - case QVariant::String: - if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: string expected")); - break; - case QVariant::StringList: // we expect a string literal. A string list is not a literal assignment. - if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: string or string list expected")); - break; - case QVariant::ByteArray: - if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: byte array expected")); - break; - case QVariant::Url: - if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: url expected")); - break; - case QVariant::RegExp: - COMPILE_EXCEPTION(v, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); - break; - case QVariant::UInt: - { - bool ok = v->value.isNumber(); - if (ok) { - double n = v->value.asNumber(); - if (double(uint(n)) != n) - ok = false; - } - if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: unsigned int expected")); - } - break; - case QVariant::Int: - { - bool ok = v->value.isNumber(); - if (ok) { - double n = v->value.asNumber(); - if (double(int(n)) != n) - ok = false; - } - if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: int expected")); - } - break; - case QMetaType::Float: - if (!v->value.isNumber()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: number expected")); - break; - case QVariant::Double: - if (!v->value.isNumber()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: number expected")); - break; - case QVariant::Color: - { - bool ok; - QQmlStringConverters::colorFromString(value.asString(), &ok); - if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: color expected")); - } - break; -#ifndef QT_NO_DATESTRING - case QVariant::Date: - { - bool ok; - QQmlStringConverters::dateFromString(value.asString(), &ok); - if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: date expected")); - } - break; - case QVariant::Time: - { - bool ok; - QQmlStringConverters::timeFromString(value.asString(), &ok); - if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: time expected")); - } - break; - case QVariant::DateTime: - { - bool ok; - QQmlStringConverters::dateTimeFromString(value.asString(), &ok); - if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: datetime expected")); - } - break; -#endif // QT_NO_DATESTRING - case QVariant::Point: - case QVariant::PointF: - { - bool ok; - QQmlStringConverters::pointFFromString(value.asString(), &ok); - if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: point expected")); - } - break; - case QVariant::Size: - case QVariant::SizeF: - { - bool ok; - QQmlStringConverters::sizeFFromString(value.asString(), &ok); - if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: size expected")); - } - break; - case QVariant::Rect: - case QVariant::RectF: - { - bool ok; - QQmlStringConverters::rectFFromString(value.asString(), &ok); - if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: rect expected")); - } - break; - case QVariant::Bool: - { - if (!v->value.isBoolean()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: boolean expected")); - } - break; - case QVariant::Vector3D: - { - QQmlInstruction::instr_storeVector3D::QVector3D v3; - if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, value.asString(), &v3, sizeof(v3))) - COMPILE_EXCEPTION(v, tr("Invalid property assignment: 3D vector expected")); - } - break; - case QVariant::Vector4D: - { - QQmlInstruction::instr_storeVector4D::QVector4D v4; - if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, value.asString(), &v4, sizeof(v4))) - COMPILE_EXCEPTION(v, tr("Invalid property assignment: 4D vector expected")); - } - break; - default: - { - // check if assigning a literal value to a list property. - // in each case, check the singular, since an Array of the specified type - // will not go via this literal assignment codepath. - if (type == qMetaTypeId<QList<qreal> >()) { - if (!v->value.isNumber()) { - COMPILE_EXCEPTION(v, tr("Invalid property assignment: real or array of reals expected")); - } - break; - } else if (type == qMetaTypeId<QList<int> >()) { - bool ok = v->value.isNumber(); - if (ok) { - double n = v->value.asNumber(); - if (double(int(n)) != n) - ok = false; - } - if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: int or array of ints expected")); - break; - } else if (type == qMetaTypeId<QList<bool> >()) { - if (!v->value.isBoolean()) { - COMPILE_EXCEPTION(v, tr("Invalid property assignment: bool or array of bools expected")); - } - break; - } else if (type == qMetaTypeId<QList<QString> >()) { // we expect a string literal. A string list is not a literal assignment. - if (!v->value.isString()) { - COMPILE_EXCEPTION(v, tr("Invalid property assignment: string or array of strings expected")); - } - break; - } else if (type == qMetaTypeId<QList<QUrl> >()) { - if (!v->value.isString()) { - COMPILE_EXCEPTION(v, tr("Invalid property assignment: url or array of urls expected")); - } - break; - } else if (type == qMetaTypeId<QJSValue>()) { - break; - } - - // otherwise, check for existence of string converter to custom type - QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(type); - if (!converter) - COMPILE_EXCEPTION(v, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(type)))); - } - break; - } - return true; -} - -/*! - Generate a store instruction for assigning literal \a v to property \a prop. - - Any literal assignment that is approved in testLiteralAssignment() must have - a corresponding action in this method. -*/ -void QQmlCompiler::genLiteralAssignment(QQmlScript::Property *prop, - QQmlScript::Value *v) -{ - if (prop->core.isEnum()) { - Q_ASSERT(v->value.isNumber()); - // Preresolved value - int value = (int)v->value.asNumber(); - - Instruction::StoreInteger instr; - instr.propertyIndex = prop->index; - instr.value = value; - output->addInstruction(instr); - return; - } - - int type = prop->type; - switch(type) { - case QMetaType::QVariant: - { - if (v->value.isNumber()) { - double n = v->value.asNumber(); - if (double(int(n)) == n) { - if (prop->core.isVarProperty()) { - Instruction::StoreVarInteger instr; - instr.propertyIndex = prop->index; - instr.value = int(n); - output->addInstruction(instr); - } else { - Instruction::StoreVariantInteger instr; - instr.propertyIndex = prop->index; - instr.value = int(n); - output->addInstruction(instr); - } - } else { - if (prop->core.isVarProperty()) { - Instruction::StoreVarDouble instr; - instr.propertyIndex = prop->index; - instr.value = n; - output->addInstruction(instr); - } else { - Instruction::StoreVariantDouble instr; - instr.propertyIndex = prop->index; - instr.value = n; - output->addInstruction(instr); - } - } - } else if (v->value.isBoolean()) { - if (prop->core.isVarProperty()) { - Instruction::StoreVarBool instr; - instr.propertyIndex = prop->index; - instr.value = v->value.asBoolean(); - output->addInstruction(instr); - } else { - Instruction::StoreVariantBool instr; - instr.propertyIndex = prop->index; - instr.value = v->value.asBoolean(); - output->addInstruction(instr); - } - } else { - if (prop->core.isVarProperty()) { - Instruction::StoreVar instr; - instr.propertyIndex = prop->index; - instr.value = output->indexForString(v->value.asString()); - output->addInstruction(instr); - } else { - Instruction::StoreVariant instr; - instr.propertyIndex = prop->index; - instr.value = output->indexForString(v->value.asString()); - output->addInstruction(instr); - } - } - } - break; - case QVariant::String: - { - Instruction::StoreString instr; - instr.propertyIndex = prop->index; - instr.value = output->indexForString(v->value.asString()); - output->addInstruction(instr); - } - break; - case QVariant::StringList: - { - Instruction::StoreStringList instr; - instr.propertyIndex = prop->index; - instr.value = output->indexForString(v->value.asString()); - output->addInstruction(instr); - } - break; - case QVariant::ByteArray: - { - Instruction::StoreByteArray instr; - instr.propertyIndex = prop->index; - instr.value = output->indexForByteArray(v->value.asString().toLatin1()); - output->addInstruction(instr); - } - break; - case QVariant::Url: - { - Instruction::StoreUrl instr; - QString string = v->value.asString(); - // Encoded dir-separators defeat QUrl processing - decode them first - string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive); - QUrl u = string.isEmpty() ? QUrl() : output->url.resolved(QUrl(string)); - // Apply URL interceptor - if (engine->urlInterceptor()) - u = engine->urlInterceptor()->intercept(u, - QQmlAbstractUrlInterceptor::UrlString); - instr.propertyIndex = prop->index; - instr.value = output->indexForUrl(u); - output->addInstruction(instr); - } - break; - case QVariant::UInt: - { - Instruction::StoreInteger instr; - instr.propertyIndex = prop->index; - instr.value = uint(v->value.asNumber()); - output->addInstruction(instr); - } - break; - case QVariant::Int: - { - Instruction::StoreInteger instr; - instr.propertyIndex = prop->index; - instr.value = int(v->value.asNumber()); - output->addInstruction(instr); - } - break; - case QMetaType::Float: - { - Instruction::StoreFloat instr; - instr.propertyIndex = prop->index; - instr.value = float(v->value.asNumber()); - output->addInstruction(instr); - } - break; - case QVariant::Double: - { - Instruction::StoreDouble instr; - instr.propertyIndex = prop->index; - instr.value = v->value.asNumber(); - output->addInstruction(instr); - } - break; - case QVariant::Color: - { - Instruction::StoreColor instr; - instr.propertyIndex = prop->index; - instr.value = QQmlStringConverters::rgbaFromString(v->value.asString()); - output->addInstruction(instr); - } - break; -#ifndef QT_NO_DATESTRING - case QVariant::Date: - { - Instruction::StoreDate instr; - QDate d = QQmlStringConverters::dateFromString(v->value.asString()); - instr.propertyIndex = prop->index; - instr.value = d.toJulianDay(); - output->addInstruction(instr); - } - break; - case QVariant::Time: - { - Instruction::StoreTime instr; - QTime time = QQmlStringConverters::timeFromString(v->value.asString()); - instr.propertyIndex = prop->index; - instr.time = time.msecsSinceStartOfDay(); - output->addInstruction(instr); - } - break; - case QVariant::DateTime: - { - Instruction::StoreDateTime instr; - QDateTime dateTime = QQmlStringConverters::dateTimeFromString(v->value.asString()); - QTime time = dateTime.time(); - instr.propertyIndex = prop->index; - instr.date = dateTime.date().toJulianDay(); - instr.time = time.msecsSinceStartOfDay(); - output->addInstruction(instr); - } - break; -#endif // QT_NO_DATESTRING - case QVariant::Point: - { - Instruction::StorePoint instr; - bool ok; - QPoint point = QQmlStringConverters::pointFFromString(v->value.asString(), &ok).toPoint(); - instr.propertyIndex = prop->index; - instr.point.xp = point.x(); - instr.point.yp = point.y(); - output->addInstruction(instr); - } - break; - case QVariant::PointF: - { - Instruction::StorePointF instr; - bool ok; - QPointF point = QQmlStringConverters::pointFFromString(v->value.asString(), &ok); - instr.propertyIndex = prop->index; - instr.point.xp = point.x(); - instr.point.yp = point.y(); - output->addInstruction(instr); - } - break; - case QVariant::Size: - { - Instruction::StoreSize instr; - bool ok; - QSize size = QQmlStringConverters::sizeFFromString(v->value.asString(), &ok).toSize(); - instr.propertyIndex = prop->index; - instr.size.wd = size.width(); - instr.size.ht = size.height(); - output->addInstruction(instr); - } - break; - case QVariant::SizeF: - { - Instruction::StoreSizeF instr; - bool ok; - QSizeF size = QQmlStringConverters::sizeFFromString(v->value.asString(), &ok); - instr.propertyIndex = prop->index; - instr.size.wd = size.width(); - instr.size.ht = size.height(); - output->addInstruction(instr); - } - break; - case QVariant::Rect: - { - Instruction::StoreRect instr; - bool ok; - QRect rect = QQmlStringConverters::rectFFromString(v->value.asString(), &ok).toRect(); - instr.propertyIndex = prop->index; - instr.rect.x1 = rect.left(); - instr.rect.y1 = rect.top(); - instr.rect.x2 = rect.right(); - instr.rect.y2 = rect.bottom(); - output->addInstruction(instr); - } - break; - case QVariant::RectF: - { - Instruction::StoreRectF instr; - bool ok; - QRectF rect = QQmlStringConverters::rectFFromString(v->value.asString(), &ok); - instr.propertyIndex = prop->index; - instr.rect.xp = rect.left(); - instr.rect.yp = rect.top(); - instr.rect.w = rect.width(); - instr.rect.h = rect.height(); - output->addInstruction(instr); - } - break; - case QVariant::Bool: - { - Instruction::StoreBool instr; - bool b = v->value.asBoolean(); - instr.propertyIndex = prop->index; - instr.value = b; - output->addInstruction(instr); - } - break; - case QVariant::Vector3D: - { - Instruction::StoreVector3D instr; - instr.propertyIndex = prop->index; - QQmlStringConverters::createFromString(QMetaType::QVector3D, v->value.asString(), &instr.vector, sizeof(instr.vector)); - output->addInstruction(instr); - } - break; - case QVariant::Vector4D: - { - Instruction::StoreVector4D instr; - instr.propertyIndex = prop->index; - QQmlStringConverters::createFromString(QMetaType::QVector4D, v->value.asString(), &instr.vector, sizeof(instr.vector)); - output->addInstruction(instr); - } - break; - default: - { - // generate single literal value assignment to a list property if required - if (type == qMetaTypeId<QList<qreal> >()) { - Instruction::StoreDoubleQList instr; - instr.propertyIndex = prop->index; - instr.value = v->value.asNumber(); - output->addInstruction(instr); - break; - } else if (type == qMetaTypeId<QList<int> >()) { - Instruction::StoreIntegerQList instr; - instr.propertyIndex = prop->index; - instr.value = int(v->value.asNumber()); - output->addInstruction(instr); - break; - } else if (type == qMetaTypeId<QList<bool> >()) { - Instruction::StoreBoolQList instr; - bool b = v->value.asBoolean(); - instr.propertyIndex = prop->index; - instr.value = b; - output->addInstruction(instr); - break; - } else if (type == qMetaTypeId<QList<QUrl> >()) { - Instruction::StoreUrlQList instr; - QString string = v->value.asString(); - QUrl u = string.isEmpty() ? QUrl() : output->url.resolved(QUrl(string)); - instr.propertyIndex = prop->index; - instr.value = output->indexForUrl(u); - output->addInstruction(instr); - break; - } else if (type == qMetaTypeId<QList<QString> >()) { - Instruction::StoreStringQList instr; - instr.propertyIndex = prop->index; - instr.value = output->indexForString(v->value.asString()); - output->addInstruction(instr); - break; - } else if (type == qMetaTypeId<QJSValue>()) { - if (v->value.isBoolean()) { - Instruction::StoreJSValueBool instr; - instr.propertyIndex = prop->index; - instr.value = v->value.asBoolean(); - output->addInstruction(instr); - } else if (v->value.isNumber()) { - double n = v->value.asNumber(); - if (double(int(n)) == n) { - Instruction::StoreJSValueInteger instr; - instr.propertyIndex = prop->index; - instr.value = int(n); - output->addInstruction(instr); - } else { - Instruction::StoreJSValueDouble instr; - instr.propertyIndex = prop->index; - instr.value = n; - output->addInstruction(instr); - } - } else { - Instruction::StoreJSValueString instr; - instr.propertyIndex = prop->index; - instr.value = output->indexForString(v->value.asString()); - output->addInstruction(instr); - } - break; - } - - // otherwise, generate custom type literal assignment - Instruction::AssignCustomType instr; - instr.propertyIndex = prop->index; - instr.primitive = output->indexForString(v->value.asString()); - instr.type = type; - output->addInstruction(instr); - } - break; - } -} - -/*! - Resets data by clearing the lists that the QQmlCompiler modifies. -*/ -void QQmlCompiler::reset(QQmlCompiledData *data) -{ - data->types.clear(); - data->primitives.clear(); - data->datas.clear(); - data->bytecode.resize(0); -} - -/*! - Compile \a unit, and store the output in \a out. \a engine is the QQmlEngine - with which the QQmlCompiledData will be associated. - - Returns true on success, false on failure. On failure, the compile errors - are available from errors(). - - If the environment variant QML_COMPILER_DUMP is set - (eg. QML_COMPILER_DUMP=1) the compiled instructions will be dumped to stderr - on a successful compiler. -*/ -bool QQmlCompiler::compile(QQmlEngine *engine, - QQmlTypeData *unit, - QQmlCompiledData *out) -{ - exceptions.clear(); - - Q_ASSERT(out); - reset(out); - - QQmlScript::Object *root = unit->parser().tree(); - Q_ASSERT(root); - - this->engine = engine; - this->enginePrivate = QQmlEnginePrivate::get(engine); - this->unit = unit; - this->unitRoot = root; - this->output = out; - this->jsModule.reset(new IR::Module(enginePrivate->v4engine()->debugger)); - this->jsModule->isQmlModule = true; - - // Compile types - const QList<QQmlTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes(); - QList<QQmlScript::TypeReference *> referencedTypes = unit->parser().referencedTypes(); - - for (int ii = 0; ii < resolvedTypes.count(); ++ii) { - QQmlCompiledData::TypeReference ref; - - const QQmlTypeData::TypeReference &tref = resolvedTypes.at(ii); - QQmlScript::TypeReference *parserRef = referencedTypes.at(ii); - - if (tref.typeData) { //QML-based type - if (tref.type->isCompositeSingleton()) { - QString err = tr( "Composite Singleton Type %1 is not creatable.").arg(tref.type->qmlTypeName()); - COMPILE_EXCEPTION(parserRef->firstUse, err); - } - ref.component = tref.typeData->compiledData(); - ref.component->addref(); - } else if (tref.type) {//C++-based type - ref.type = tref.type; - if (!ref.type->isCreatable()) { - QString err = ref.type->noCreationReason(); - if (err.isEmpty()) - err = tr( "Element is not creatable."); - COMPILE_EXCEPTION(parserRef->firstUse, err); - } - - if (ref.type->containsRevisionedAttributes()) { - QQmlError cacheError; - ref.typePropertyCache = enginePrivate->cache(ref.type, - resolvedTypes.at(ii).minorVersion, - cacheError); - if (!ref.typePropertyCache) - COMPILE_EXCEPTION(parserRef->firstUse, cacheError.description()); - ref.typePropertyCache->addref(); - } - } - - ref.doDynamicTypeCheck(); - - out->types << ref; - } - - compileTree(root); - - if (!isError()) { - if (compilerDump()) - out->dumpInstructions(); - if (componentStats) - dumpStats(); - Q_ASSERT(out->rootPropertyCache); - - // Any QQmlPropertyMap instances for example need to have their property cache removed, - // because the class is too dynamic and allows adding properties at any point at run-time. - for (int i = 0; i < output->types.count(); ++i) { - QQmlCompiledData::TypeReference &tr = output->types[i]; - if (!tr.typePropertyCache) - continue; - - if (tr.isFullyDynamicType) { - tr.typePropertyCache->release(); - tr.typePropertyCache = 0; - } - } - } else { - reset(out); - } - - compileState = 0; - output = 0; - this->engine = 0; - this->enginePrivate = 0; - this->unit = 0; - this->cachedComponentTypeRef = -1; - this->cachedTranslationContextIndex = -1; - this->unitRoot = 0; - - return !isError(); -} - -void QQmlCompiler::compileTree(QQmlScript::Object *tree) -{ - compileState = pool->New<ComponentCompileState>(); - - compileState->root = tree; - if (componentStats) - componentStats->componentStat.lineNumber = tree->location.start.line; - - // We generate the importCache before we build the tree so that - // it can be used in the binding compiler. Given we "expect" the - // QML compilation to succeed, this isn't a waste. - output->importCache = new QQmlTypeNameCache(); - foreach (const QString &ns, unit->namespaces()) { - output->importCache->add(ns); - } - - // Add any Composite Singletons that were used to the import cache - for (int i = 0; i < unit->compositeSingletons().count(); ++i) { - output->importCache->add(unit->compositeSingletons().at(i).type->qmlTypeName(), - unit->compositeSingletons().at(i).type->sourceUrl(), unit->compositeSingletons().at(i).prefix); - } - - int scriptIndex = 0; - foreach (const QQmlTypeData::ScriptReference &script, unit->resolvedScripts()) { - QString qualifier = script.qualifier; - QString enclosingNamespace; - - const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.')); - if (lastDotIndex != -1) { - enclosingNamespace = qualifier.left(lastDotIndex); - qualifier = qualifier.mid(lastDotIndex+1); - } - - output->importCache->add(qualifier, scriptIndex++, enclosingNamespace); - } - - unit->imports().populateCache(output->importCache); - - if (!buildObject(tree, BindingContext()) || !completeComponentBuild()) - return; - - if (!jsModule->functions.isEmpty()) { - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); - QV4::Compiler::JSUnitGenerator jsUnitGenerator(jsModule.data()); - QScopedPointer<EvalInstructionSelection> isel(v4->iselFactory->create(enginePrivate, v4->executableAllocator, jsModule.data(), &jsUnitGenerator)); - isel->setUseFastLookups(false); - QV4::CompiledData::CompilationUnit *jsUnit = isel->compile(/*generated unit data*/true); - output->compilationUnit = jsUnit; - output->compilationUnit->ref(); - } - - Instruction::Init init; - init.bindingsSize = compileState->totalBindingsCount; - init.parserStatusSize = compileState->parserStatusCount; - init.contextCache = genContextCache(); - init.objectStackSize = compileState->objectDepth.maxDepth(); - init.listStackSize = compileState->listDepth.maxDepth(); - if (compileState->compiledBindingData.isEmpty()) - init.compiledBinding = -1; - else - init.compiledBinding = output->indexForByteArray(compileState->compiledBindingData); - output->addInstruction(init); - - foreach (const QQmlTypeData::ScriptReference &script, unit->resolvedScripts()) { - Instruction::StoreImportedScript import; - import.value = output->scripts.count(); - - QQmlScriptData *scriptData = script.script->scriptData(); - scriptData->addref(); - output->scripts << scriptData; - output->addInstruction(import); - } - - genObject(tree); - - Instruction::SetDefault def; - output->addInstruction(def); - - Instruction::Done done; - output->addInstruction(done); - - Q_ASSERT(tree->metatype); - if (!tree->synthdata.isEmpty()) { - enginePrivate->registerInternalCompositeType(output); - } else if (output->types.at(tree->type).component) { - output->metaTypeId = output->types.at(tree->type).component->metaTypeId; - output->listMetaTypeId = output->types.at(tree->type).component->listMetaTypeId; - } else { - Q_ASSERT(output->types.at(tree->type).type); - output->metaTypeId = output->types.at(tree->type).type->typeId(); - output->listMetaTypeId = output->types.at(tree->type).type->qListTypeId(); - } - if (!tree->synthdata.isEmpty()) - enginePrivate->registerInternalCompositeType(output); -} - -static bool QStringList_contains(const QStringList &list, const QHashedStringRef &string) -{ - for (int ii = 0; ii < list.count(); ++ii) - if (string == list.at(ii)) - return true; - - return false; -} - -bool QQmlCompiler::buildObject(QQmlScript::Object *obj, const BindingContext &ctxt) -{ - if (componentStats) - componentStats->componentStat.objects++; - - Q_ASSERT (obj->type != -1); - QQmlCompiledData::TypeReference &tr = output->types[obj->type]; - obj->metatype = tr.createPropertyCache(engine); - - // This object is a "Component" element. - if (tr.type && obj->metatype->metaObject() == &QQmlComponent::staticMetaObject) { - COMPILE_CHECK(buildComponent(obj, ctxt)); - return true; - } - - if (tr.component) { - typedef QQmlInstruction I; - const I *init = ((const I *)tr.component->bytecode.constData()); - Q_ASSERT(init && tr.component->instructionType(init) == QQmlInstruction::Init); - - // Adjust stack depths to include nested components - compileState->objectDepth.pushPop(init->init.objectStackSize); - compileState->listDepth.pushPop(init->init.listStackSize); - compileState->parserStatusCount += init->init.parserStatusSize; - compileState->totalBindingsCount += init->init.bindingsSize; - } - - compileState->objectDepth.push(); - - // Object instantiations reset the binding context - BindingContext objCtxt(obj); - - // Create the synthesized meta object, ignoring aliases - COMPILE_CHECK(checkDynamicMeta(obj)); - COMPILE_CHECK(mergeDynamicMetaProperties(obj)); - COMPILE_CHECK(buildDynamicMeta(obj, Normal)); - - // Find the native type and check for the QQmlParserStatus interface - QQmlType *type = toQmlType(obj); - Q_ASSERT(type); - obj->parserStatusCast = type->parserStatusCast(); - if (obj->parserStatusCast != -1) - compileState->parserStatusCount++; - - // Check if this is a custom parser type. Custom parser types allow - // assignments to non-existent properties. These assignments are then - // compiled by the type. - bool isCustomParser = output->types.at(obj->type).type && - output->types.at(obj->type).type->customParser() != 0; - QList<QQmlCustomParserProperty> customProps; - - // Fetch the list of deferred properties - QStringList deferredList = deferredProperties(obj); - - // Must do id property first. This is to ensure that the id given to any - // id reference created matches the order in which the objects are - // instantiated - for (QQmlScript::Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) { - if (prop->name() == id_string) { - COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); - break; - } - } - - // Merge - QQmlScript::Property *defaultProperty = 0; - QQmlScript::Property *skipProperty = 0; - if (obj->defaultProperty) { - defaultProperty = obj->defaultProperty; - - QQmlScript::Property *explicitProperty = 0; - - QString defaultPropertyName = obj->metatype->defaultPropertyName(); - if (!defaultPropertyName.isEmpty()) { - QString *s = pool->NewString(defaultPropertyName); - QHashedStringRef r(*s); - - if (obj->propertiesHashField.test(r.hash())) { - for (QQmlScript::Property *ep = obj->properties.first(); ep; ep = obj->properties.next(ep)) { - if (ep->name() == r) { - explicitProperty = ep; - break; - } - } - } - - if (!explicitProperty) - defaultProperty->setName(r); - } - - if (explicitProperty && !explicitProperty->value && !explicitProperty->values.isEmpty()) { - - skipProperty = explicitProperty; // We merge the values into defaultProperty - - // Find the correct insertion point - QQmlScript::Value *insertPos = 0; - - for (QQmlScript::Value *v = defaultProperty->values.first(); v; v = QQmlScript::Property::ValueList::next(v)) { - if (!(v->location.start < explicitProperty->values.first()->location.start)) - break; - insertPos = v; - } - - defaultProperty->values.insertAfter(insertPos, explicitProperty->values); - } - } - - QQmlCustomParser *cp = 0; - if (isCustomParser) - cp = output->types.at(obj->type).type->customParser(); - - // Build all explicit properties specified - for (QQmlScript::Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) { - - if (prop == skipProperty) - continue; - if (prop->name() == id_string) - continue; - - bool canDefer = false; - if (isCustomParser) { - if (doesPropertyExist(prop, obj) && - (!(cp->flags() & QQmlCustomParser::AcceptsAttachedProperties) || - !isAttachedPropertyName(prop->name()))) { - int ids = compileState->ids.count(); - COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); - canDefer = ids == compileState->ids.count(); - } else if (isSignalPropertyName(prop->name()) && - (cp->flags() & QQmlCustomParser::AcceptsSignalHandlers)) { - COMPILE_CHECK(buildSignal(prop,obj,objCtxt)); - } else { - customProps << QQmlCustomParserNodePrivate::fromProperty(prop); - } - } else { - if (isSignalPropertyName(prop->name())) { - COMPILE_CHECK(buildSignal(prop,obj,objCtxt)); - } else { - int ids = compileState->ids.count(); - COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); - canDefer = ids == compileState->ids.count(); - } - } - - if (canDefer && !deferredList.isEmpty() && QStringList_contains(deferredList, prop->name())) - prop->isDeferred = true; - - } - - // Build the default property - if (defaultProperty) { - QQmlScript::Property *prop = defaultProperty; - - bool canDefer = false; - if (isCustomParser) { - if (doesPropertyExist(prop, obj)) { - int ids = compileState->ids.count(); - COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); - canDefer = ids == compileState->ids.count(); - } else { - customProps << QQmlCustomParserNodePrivate::fromProperty(prop); - } - } else { - int ids = compileState->ids.count(); - COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); - canDefer = ids == compileState->ids.count(); - } - - if (canDefer && !deferredList.isEmpty() && QStringList_contains(deferredList, prop->name())) - prop->isDeferred = true; - } - - // Compile custom parser parts - if (isCustomParser && !customProps.isEmpty()) { - cp->clearErrors(); - cp->compiler = this; - cp->object = obj; - obj->custom = cp->compile(customProps); - cp->compiler = 0; - cp->object = 0; - foreach (QQmlError err, cp->errors()) { - err.setUrl(output->url); - exceptions << err; - } - } - - compileState->objectDepth.pop(); - - return true; -} - -void QQmlCompiler::genObject(QQmlScript::Object *obj, bool parentToSuper) -{ - QQmlCompiledData::TypeReference &tr = output->types[obj->type]; - if (tr.type && obj->metatype->metaObject() == &QQmlComponent::staticMetaObject) { - genComponent(obj); - return; - } - - // Create the object - if (obj->custom.isEmpty() && output->types.at(obj->type).type && - !output->types.at(obj->type).type->isExtendedType() && obj != compileState->root) { - - Instruction::CreateSimpleObject create; - create.create = output->types.at(obj->type).type->createFunction(); - create.typeSize = output->types.at(obj->type).type->createSize(); - create.type = obj->type; - create.line = obj->location.start.line; - create.column = obj->location.start.column; - create.parentToSuper = parentToSuper; - output->addInstruction(create); - - } else { - - if (output->types.at(obj->type).type) { - Instruction::CreateCppObject create; - create.line = obj->location.start.line; - create.column = obj->location.start.column; - create.data = -1; - if (!obj->custom.isEmpty()) - create.data = output->indexForByteArray(obj->custom); - create.type = obj->type; - create.isRoot = (compileState->root == obj); - create.parentToSuper = parentToSuper; - output->addInstruction(create); - } else { - Instruction::CreateQMLObject create; - create.type = obj->type; - create.isRoot = (compileState->root == obj); - - if (!obj->bindingBitmask.isEmpty()) { - Q_ASSERT(obj->bindingBitmask.size() % 4 == 0); - create.bindingBits = output->indexForByteArray(obj->bindingBitmask); - } else { - create.bindingBits = -1; - } - output->addInstruction(create); - - Instruction::CompleteQMLObject complete; - complete.line = obj->location.start.line; - complete.column = obj->location.start.column; - complete.isRoot = (compileState->root == obj); - output->addInstruction(complete); - } - } - - // Setup the synthesized meta object if necessary - if (!obj->synthdata.isEmpty()) { - Q_ASSERT(!output->types.at(obj->type).isFullyDynamicType); - Instruction::StoreMetaObject meta; - meta.aliasData = output->indexForByteArray(obj->synthdata); - meta.propertyCache = output->propertyCaches.count(); - - QQmlPropertyCache *propertyCache = obj->synthCache; - Q_ASSERT(propertyCache); - propertyCache->addref(); - - if (obj == unitRoot) { - propertyCache->addref(); - output->rootPropertyCache = propertyCache; - } - - output->propertyCaches << propertyCache; - output->addInstruction(meta); - } else if (obj == unitRoot) { - output->rootPropertyCache = tr.createPropertyCache(engine); - output->rootPropertyCache->addref(); - } - - // Set the object id - if (!obj->id.isEmpty()) { - Instruction::SetId id; - id.value = output->indexForString(obj->id); - id.index = obj->idIndex; - output->addInstruction(id); - } - - // Begin the class - if (tr.type && obj->parserStatusCast != -1) { - Instruction::BeginObject begin; - begin.castValue = obj->parserStatusCast; - output->addInstruction(begin); - } - - genObjectBody(obj); -} - -void QQmlCompiler::genObjectBody(QQmlScript::Object *obj) -{ - for (QQmlScript::Property *prop = obj->scriptStringProperties.first(); prop; prop = QQmlScript::Object::PropertyList::next(prop)) { - Q_ASSERT(prop->scriptStringScope != -1); - const QString &script = prop->values.first()->value.asScript(); - Instruction::StoreScriptString ss; - ss.propertyIndex = prop->index; - ss.value = output->indexForString(script); - ss.scope = prop->scriptStringScope; - ss.bindingId = output->indexForString(prop->values.first()->value.asScript()); - ss.line = prop->location.start.line; - ss.column = prop->location.start.column; - ss.isStringLiteral = prop->values.first()->value.isString(); - ss.isNumberLiteral = prop->values.first()->value.isNumber(); - ss.numberValue = prop->values.first()->value.asNumber(); - output->addInstruction(ss); - } - - bool seenDefer = false; - for (QQmlScript::Property *prop = obj->valueProperties.first(); prop; prop = QQmlScript::Object::PropertyList::next(prop)) { - if (prop->isDeferred) { - seenDefer = true; - continue; - } - if (!prop->isAlias) - genValueProperty(prop, obj); - } - if (seenDefer) { - Instruction::Defer defer; - defer.deferCount = 0; - int deferIdx = output->addInstruction(defer); - int nextInstructionIndex = output->nextInstructionIndex(); - - Instruction::DeferInit dinit; - // XXX - these are now massive over allocations - dinit.bindingsSize = compileState->totalBindingsCount; - dinit.parserStatusSize = compileState->parserStatusCount; - dinit.objectStackSize = compileState->objectDepth.maxDepth(); - dinit.listStackSize = compileState->listDepth.maxDepth(); - output->addInstruction(dinit); - - for (QQmlScript::Property *prop = obj->valueProperties.first(); prop; prop = QQmlScript::Object::PropertyList::next(prop)) { - if (!prop->isDeferred) - continue; - genValueProperty(prop, obj); - } - - Instruction::Done done; - output->addInstruction(done); - - output->instruction(deferIdx)->defer.deferCount = output->nextInstructionIndex() - nextInstructionIndex; - } - - for (QQmlScript::Property *prop = obj->signalProperties.first(); prop; prop = QQmlScript::Object::PropertyList::next(prop)) { - - QQmlScript::Value *v = prop->values.first(); - - if (v->type == QQmlScript::Value::SignalObject) { - - genObject(v->object); - - Instruction::AssignSignalObject assign; - assign.line = v->location.start.line; - assign.column = v->location.start.column; - assign.signal = output->indexForString(prop->name().toString()); - output->addInstruction(assign); - - } else if (v->type == QQmlScript::Value::SignalExpression) { - - Instruction::StoreSignal store; - store.runtimeFunctionIndex = compileState->jsCompileData[v->signalData.signalScopeObject].runtimeFunctionIndices.at(v->signalData.functionIndex); - store.handlerName = output->indexForString(prop->name().toString()); - store.parameters = output->indexForString(obj->metatype->signalParameterStringForJS(prop->index)); - store.signalIndex = prop->index; - store.value = output->indexForString(v->value.asScript()); - store.context = v->signalData.signalExpressionContextStack; - store.line = v->location.start.line; - store.column = v->location.start.column; - output->addInstruction(store); - - } - - } - - for (QQmlScript::Property *prop = obj->attachedProperties.first(); prop; prop = QQmlScript::Object::PropertyList::next(prop)) { - Instruction::FetchAttached fetch; - fetch.id = prop->index; - fetch.line = prop->location.start.line; - output->addInstruction(fetch); - - genObjectBody(prop->value); - - Instruction::PopFetchedObject pop; - output->addInstruction(pop); - } - - for (QQmlScript::Property *prop = obj->groupedProperties.first(); prop; prop = QQmlScript::Object::PropertyList::next(prop)) { - Instruction::FetchObject fetch; - fetch.property = prop->index; - fetch.line = prop->location.start.line; - fetch.column = prop->location.start.column; - output->addInstruction(fetch); - - if (!prop->value->synthdata.isEmpty()) { - Instruction::StoreMetaObject meta; - meta.aliasData = output->indexForByteArray(prop->value->synthdata); - meta.propertyCache = output->propertyCaches.count(); - QQmlPropertyCache *propertyCache = prop->value->synthCache; - Q_ASSERT(propertyCache); - propertyCache->addref(); - output->propertyCaches << propertyCache; - output->addInstruction(meta); - } - - genObjectBody(prop->value); - - Instruction::PopFetchedObject pop; - output->addInstruction(pop); - } - - for (QQmlScript::Property *prop = obj->valueTypeProperties.first(); prop; prop = QQmlScript::Object::PropertyList::next(prop)) { - if (!prop->isAlias) - genValueTypeProperty(obj, prop); - } - - for (QQmlScript::Property *prop = obj->valueProperties.first(); prop; prop = QQmlScript::Object::PropertyList::next(prop)) { - if (prop->isDeferred) - continue; - if (prop->isAlias) - genValueProperty(prop, obj); - } - - for (QQmlScript::Property *prop = obj->valueTypeProperties.first(); prop; prop = QQmlScript::Object::PropertyList::next(prop)) { - if (prop->isAlias) - genValueTypeProperty(obj, prop); - } -} - -void QQmlCompiler::genValueTypeProperty(QQmlScript::Object *obj, QQmlScript::Property *prop) -{ - Instruction::FetchValueType fetch; - fetch.property = prop->index; - fetch.type = prop->type; - fetch.bindingSkipList = 0; - - if (obj->type == -1 || output->types.at(obj->type).component) { - // We only have to do this if this is a composite type. If it is a builtin - // type it can't possibly already have bindings that need to be cleared. - for (QQmlScript::Property *vprop = prop->value->valueProperties.first(); vprop; vprop = QQmlScript::Object::PropertyList::next(vprop)) { - if (!vprop->values.isEmpty()) { - Q_ASSERT(vprop->index >= 0 && vprop->index < 32); - fetch.bindingSkipList |= (1 << vprop->index); - } - } - } - - output->addInstruction(fetch); - - for (QQmlScript::Property *vprop = prop->value->valueProperties.first(); vprop; vprop = QQmlScript::Object::PropertyList::next(vprop)) { - genPropertyAssignment(vprop, prop->value, prop); - } - - Instruction::PopValueType pop; - pop.property = prop->index; - pop.type = prop->type; - pop.bindingSkipList = 0; - output->addInstruction(pop); - - genPropertyAssignment(prop, obj); -} - -void QQmlCompiler::genComponent(QQmlScript::Object *obj) -{ - QQmlScript::Object *root = obj->defaultProperty->values.first()->object; - Q_ASSERT(root); - - Instruction::CreateComponent create; - create.line = root->location.start.line; - create.column = root->location.start.column; - create.endLine = root->location.end.line; - create.isRoot = (compileState->root == obj); - int createInstruction = output->addInstruction(create); - int nextInstructionIndex = output->nextInstructionIndex(); - - ComponentCompileState *oldCompileState = compileState; - compileState = componentState(root); - - Instruction::Init init; - init.bindingsSize = compileState->totalBindingsCount; - init.parserStatusSize = compileState->parserStatusCount; - init.contextCache = genContextCache(); - init.objectStackSize = compileState->objectDepth.maxDepth(); - init.listStackSize = compileState->listDepth.maxDepth(); - if (compileState->compiledBindingData.isEmpty()) - init.compiledBinding = -1; - else - init.compiledBinding = output->indexForByteArray(compileState->compiledBindingData); - output->addInstruction(init); - - genObject(root); - - Instruction::SetDefault def; - output->addInstruction(def); - - Instruction::Done done; - output->addInstruction(done); - - output->instruction(createInstruction)->createComponent.count = - output->nextInstructionIndex() - nextInstructionIndex; - - compileState = oldCompileState; - - if (!obj->id.isEmpty()) { - Instruction::SetId id; - id.value = output->indexForString(obj->id); - id.index = obj->idIndex; - output->addInstruction(id); - } - - if (obj == unitRoot) { - output->rootPropertyCache = output->types[obj->type].createPropertyCache(engine); - output->rootPropertyCache->addref(); - } -} - -bool QQmlCompiler::buildComponent(QQmlScript::Object *obj, - const BindingContext &ctxt) -{ - // The special "Component" element can only have the id property and a - // default property, that actually defines the component's tree - - compileState->objectDepth.push(); - - // Find, check and set the "id" property (if any) - QQmlScript::Property *idProp = 0; - if (obj->properties.isMany() || - (obj->properties.isOne() && obj->properties.first()->name() != id_string)) - COMPILE_EXCEPTION(obj->properties.first(), tr("Component elements may not contain properties other than id")); - - if (!obj->properties.isEmpty()) - idProp = obj->properties.first(); - - if (idProp) { - if (idProp->value || idProp->values.isMany() || idProp->values.first()->object) - COMPILE_EXCEPTION(idProp, tr("Invalid component id specification")); - COMPILE_CHECK(checkValidId(idProp->values.first(), idProp->values.first()->primitive())) - - QString idVal = idProp->values.first()->primitive(); - - if (compileState->ids.value(idVal)) - COMPILE_EXCEPTION(idProp, tr("id is not unique")); - - obj->id = idVal; - addId(idVal, obj); - } - - // Check the Component tree is well formed - if (obj->defaultProperty && - (obj->defaultProperty->value || obj->defaultProperty->values.isMany() || - (obj->defaultProperty->values.isOne() && !obj->defaultProperty->values.first()->object))) - COMPILE_EXCEPTION(obj, tr("Invalid component body specification")); - - if (!obj->dynamicProperties.isEmpty()) - COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties.")); - if (!obj->dynamicSignals.isEmpty()) - COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals.")); - if (!obj->dynamicSlots.isEmpty()) - COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions.")); - - QQmlScript::Object *root = 0; - if (obj->defaultProperty && !obj->defaultProperty->values.isEmpty()) - root = obj->defaultProperty->values.first()->object; - - if (!root) - COMPILE_EXCEPTION(obj, tr("Cannot create empty component specification")); - - // Build the component tree - COMPILE_CHECK(buildComponentFromRoot(root, ctxt)); - - compileState->objectDepth.pop(); - - return true; -} - -bool QQmlCompiler::buildComponentFromRoot(QQmlScript::Object *obj, - const BindingContext &ctxt) -{ - ComponentCompileState *oldComponentCompileState = compileState; - compileState = pool->New<ComponentCompileState>(); - compileState->root = obj; - compileState->nested = true; - - if (componentStats) { - ComponentStat oldComponentStat = componentStats->componentStat; - - componentStats->componentStat = ComponentStat(); - componentStats->componentStat.lineNumber = obj->location.start.line; - - if (obj) - COMPILE_CHECK(buildObject(obj, ctxt)); - - COMPILE_CHECK(completeComponentBuild()); - - componentStats->componentStat = oldComponentStat; - } else { - if (obj) - COMPILE_CHECK(buildObject(obj, ctxt)); - - COMPILE_CHECK(completeComponentBuild()); - } - - compileState = oldComponentCompileState; - - return true; -} - - -// Build a sub-object. A sub-object is one that was not created directly by -// QML - such as a grouped property object, or an attached object. Sub-object's -// can't have an id, involve a custom parser, have attached properties etc. -bool QQmlCompiler::buildSubObject(QQmlScript::Object *obj, const BindingContext &ctxt) -{ - Q_ASSERT(obj->metatype); - Q_ASSERT(!obj->defaultProperty); - Q_ASSERT(ctxt.isSubContext()); // sub-objects must always be in a binding - // sub-context - - for (QQmlScript::Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) { - if (isSignalPropertyName(prop->name())) { - COMPILE_CHECK(buildSignal(prop, obj, ctxt)); - } else { - COMPILE_CHECK(buildProperty(prop, obj, ctxt)); - } - } - - return true; -} - -int QQmlCompiler::componentTypeRef() -{ - if (cachedComponentTypeRef == -1) { - QQmlType *t = QQmlMetaType::qmlType(Component_string, Component_module_string, 1, 0); - for (int ii = output->types.count() - 1; ii >= 0; --ii) { - if (output->types.at(ii).type == t) { - cachedComponentTypeRef = ii; - return ii; - } - } - QQmlCompiledData::TypeReference ref; - ref.type = t; - output->types << ref; - cachedComponentTypeRef = output->types.count() - 1; - } - return cachedComponentTypeRef; -} - -int QQmlCompiler::translationContextIndex() -{ - if (cachedTranslationContextIndex == -1) { - // This code must match that in the qsTr() implementation - const QString &path = output->name; - int lastSlash = path.lastIndexOf(QLatin1Char('/')); - QString context = (lastSlash > -1) ? path.mid(lastSlash + 1, path.length()-lastSlash-5) : - QString(); - QByteArray contextUtf8 = context.toUtf8(); - cachedTranslationContextIndex = output->indexForByteArray(contextUtf8); - } - return cachedTranslationContextIndex; -} - -static AST::FunctionDeclaration *convertSignalHandlerExpressionToFunctionDeclaration(QQmlJS::Engine *jsEngine, - AST::Node *node, - const QString &signalName, - const QList<QByteArray> ¶meters) -{ - QQmlJS::MemoryPool *pool = jsEngine->pool(); - - AST::FormalParameterList *paramList = 0; - foreach (const QByteArray ¶m, parameters) { - QStringRef paramNameRef = jsEngine->newStringRef(QString::fromUtf8(param)); - - if (paramList) - paramList = new (pool) AST::FormalParameterList(paramList, paramNameRef); - else - paramList = new (pool) AST::FormalParameterList(paramNameRef); - } - - if (paramList) - paramList = paramList->finish(); - - AST::Statement *statement = node->statementCast(); - if (!statement) { - AST::ExpressionNode *expr = node->expressionCast(); - Q_ASSERT(expr); - statement = new (pool) AST::ExpressionStatement(expr); - } - AST::SourceElement *sourceElement = new (pool) AST::StatementSourceElement(statement); - AST::SourceElements *elements = new (pool) AST::SourceElements(sourceElement); - elements = elements->finish(); - - AST::FunctionBody *body = new (pool) AST::FunctionBody(elements); - - AST::FunctionDeclaration *functionDeclaration = new (pool) AST::FunctionDeclaration(jsEngine->newStringRef(signalName), paramList, body); - functionDeclaration->functionToken = statement->firstSourceLocation(); - return functionDeclaration; -} - -bool QQmlCompiler::buildSignal(QQmlScript::Property *prop, QQmlScript::Object *obj, - const BindingContext &ctxt) -{ - Q_ASSERT(obj->metatype); - - const QHashedStringRef &propName = prop->name(); - - Q_ASSERT(propName.startsWith(on_string)); - QString name = propName.mid(2, -1).toString(); - - // Note that the property name could start with any alpha or '_' or '$' character, - // so we need to do the lower-casing of the first alpha character. - for (int firstAlphaIndex = 0; firstAlphaIndex < name.size(); ++firstAlphaIndex) { - if (name.at(firstAlphaIndex).isUpper()) { - name[firstAlphaIndex] = name.at(firstAlphaIndex).toLower(); - break; - } - } - - bool notInRevision = false; - - QQmlPropertyData *sig = signal(obj, QStringRef(&name), ¬InRevision); - - if (sig == 0) { - - if (notInRevision && 0 == property(obj, propName, 0)) { - Q_ASSERT(obj->type != -1); - const QList<QQmlTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes(); - const QQmlTypeData::TypeReference &type = resolvedTypes.at(obj->type); - if (type.type) { - COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(elementName(obj)).arg(prop->name().toString()).arg(type.type->module()).arg(type.majorVersion).arg(type.minorVersion)); - } else { - COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available due to component versioning.").arg(elementName(obj)).arg(prop->name().toString())); - } - } - - // If the "on<Signal>" name doesn't resolve into a signal, try it as a - // property. - COMPILE_CHECK(buildProperty(prop, obj, ctxt)); - - } else { - - if (prop->value || !prop->values.isOne()) - COMPILE_EXCEPTION(prop, tr("Incorrectly specified signal assignment")); - - prop->index = propertyCacheForObject(obj)->methodIndexToSignalIndex(sig->coreIndex); - prop->core = *sig; - - obj->addSignalProperty(prop); - - if (prop->values.first()->object) { - COMPILE_CHECK(buildObject(prop->values.first()->object, ctxt)); - prop->values.first()->type = QQmlScript::Value::SignalObject; - } else { - prop->values.first()->type = QQmlScript::Value::SignalExpression; - - if (!prop->values.first()->value.isScript()) - COMPILE_EXCEPTION(prop, tr("Cannot assign a value to a signal (expecting a script to be run)")); - - QString script = prop->values.first()->value.asScript().trimmed(); - if (script.isEmpty()) - COMPILE_EXCEPTION(prop, tr("Empty signal assignment")); - - //all handlers should be on the original, rather than cloned signals in order - //to ensure all parameters are available (see qqmlboundsignal constructor for more details) - prop->index = obj->metatype->originalClone(prop->index); - prop->values.first()->signalData.signalExpressionContextStack = ctxt.stack; - prop->values.first()->signalData.signalScopeObject = ctxt.object; - - QList<QByteArray> parameters = obj->metatype->signalParameterNames(prop->index); - - AST::FunctionDeclaration *funcDecl = convertSignalHandlerExpressionToFunctionDeclaration(unit->parser().jsEngine(), prop->values.first()->value.asAST(), propName.toString(), parameters); - - ComponentCompileState::PerObjectCompileData *cd = &compileState->jsCompileData[ctxt.object]; - cd->functionsToCompile.append(funcDecl); - prop->values.first()->signalData.functionIndex = cd->functionsToCompile.count() - 1; - - QString errorString; - obj->metatype->signalParameterStringForJS(prop->index, &errorString); - if (!errorString.isEmpty()) - COMPILE_EXCEPTION(prop, errorString); - } - } - - return true; -} - - -/*! - Returns true if (value) property \a prop exists on obj, false otherwise. -*/ -bool QQmlCompiler::doesPropertyExist(QQmlScript::Property *prop, - QQmlScript::Object *obj) -{ - if (prop->name().isEmpty()) - return false; - if(isAttachedPropertyName(prop->name()) || prop->name() == id_string) - return true; - - return property(obj, prop->name()) != 0; -} - -bool QQmlCompiler::buildProperty(QQmlScript::Property *prop, - QQmlScript::Object *obj, - const BindingContext &ctxt) -{ - if (prop->isEmpty()) - COMPILE_EXCEPTION(prop, tr("Empty property assignment")); - - if (isAttachedPropertyName(prop->name())) { - // Setup attached property data - - if (ctxt.isSubContext()) { - // Attached properties cannot be used on sub-objects. Sub-objects - // always exist in a binding sub-context, which is what we test - // for here. - COMPILE_EXCEPTION(prop, tr("Attached properties cannot be used here")); - } - - QQmlType *type = 0; - QQmlImportNamespace *typeNamespace = 0; - unit->imports().resolveType(prop->name(), &type, 0, 0, &typeNamespace); - - if (typeNamespace) { - COMPILE_CHECK(buildPropertyInNamespace(typeNamespace, prop, obj, - ctxt)); - return true; - } else if (!type || !type->attachedPropertiesType()) { - COMPILE_EXCEPTION(prop, tr("Non-existent attached object")); - } - - if (!prop->value) - COMPILE_EXCEPTION(prop, tr("Invalid attached object assignment")); - - Q_ASSERT(type->attachedPropertiesFunction()); - prop->index = type->attachedPropertiesId(); - prop->value->metatype = enginePrivate->cache(type->attachedPropertiesType()); - } else { - // Setup regular property data - bool notInRevision = false; - QQmlPropertyData *d = - prop->name().isEmpty()?0:property(obj, prop->name(), ¬InRevision); - - if (d == 0 && notInRevision) { - const QList<QQmlTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes(); - QQmlTypeData::TypeReference type; - if (obj->type != -1) - type = resolvedTypes.at(obj->type); - if (type.type) { - COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(elementName(obj)).arg(prop->name().toString()).arg(type.type->module()).arg(type.majorVersion).arg(type.minorVersion)); - } else { - COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available due to component versioning.").arg(elementName(obj)).arg(prop->name().toString())); - } - } else if (d) { - prop->index = d->coreIndex; - prop->core = *d; - } else if (prop->isDefault) { - QString defaultPropertyName = obj->metatype->defaultPropertyName(); - - if (!defaultPropertyName.isEmpty()) { - prop->setName(defaultPropertyName); - prop->core = *obj->metatype->defaultProperty(); - prop->index = prop->core.coreIndex; - } - } - - // We can't error here as the "id" property does not require a - // successful index resolution - if (prop->index != -1) - prop->type = prop->core.propType; - - // Check if this is an alias - if (prop->index != -1 && - prop->parent && - prop->parent->type != -1 && - output->types.at(prop->parent->type).component) { - - QQmlPropertyCache *cache = output->types.at(prop->parent->type).component->rootPropertyCache; - if (cache && cache->property(prop->index) && cache->property(prop->index)->isAlias()) - prop->isAlias = true; - } - - if (prop->index != -1 && !prop->values.isEmpty()) - prop->parent->setBindingBit(prop->index); - } - - if (!prop->isDefault && prop->name() == id_string && !ctxt.isSubContext()) { - - // The magic "id" behavior doesn't apply when "id" is resolved as a - // default property or to sub-objects (which are always in binding - // sub-contexts) - COMPILE_CHECK(buildIdProperty(prop, obj)); - if (prop->type == QVariant::String && - prop->values.first()->value.isString()) - COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt)); - - } else if (isAttachedPropertyName(prop->name())) { - - COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt)); - - } else if (prop->index == -1) { - - if (prop->isDefault) { - COMPILE_EXCEPTION(prop->values.first(), tr("Cannot assign to non-existent default property")); - } else { - COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(prop->name().toString())); - } - - } else if (prop->value) { - - COMPILE_CHECK(buildGroupedProperty(prop, obj, ctxt)); - - } else if (prop->core.isQList()) { - - COMPILE_CHECK(buildListProperty(prop, obj, ctxt)); - - } else if (prop->type == qMetaTypeId<QQmlScriptString>()) { - - COMPILE_CHECK(buildScriptStringProperty(prop, obj, ctxt)); - - } else { - - COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt)); - - } - - return true; -} - -bool QQmlCompiler::buildPropertyInNamespace(QQmlImportNamespace *ns, - QQmlScript::Property *nsProp, - QQmlScript::Object *obj, - const BindingContext &ctxt) -{ - if (!nsProp->value) - COMPILE_EXCEPTION(nsProp, tr("Invalid use of namespace")); - - for (QQmlScript::Property *prop = nsProp->value->properties.first(); prop; prop = nsProp->value->properties.next(prop)) { - - if (!isAttachedPropertyName(prop->name())) - COMPILE_EXCEPTION(prop, tr("Expected type name")); - - // Setup attached property data - - QQmlType *type = 0; - unit->imports().resolveType(ns, prop->name(), &type, 0, 0); - - if (!type || !type->attachedPropertiesType()) - COMPILE_EXCEPTION(prop, tr("Non-existent attached object")); - - if (!prop->value) - COMPILE_EXCEPTION(prop, tr("Invalid attached object assignment")); - - Q_ASSERT(type->attachedPropertiesFunction()); - prop->index = type->index(); - prop->value->metatype = enginePrivate->cache(type->attachedPropertiesType()); - - COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt)); - } - - return true; -} - -void QQmlCompiler::genValueProperty(QQmlScript::Property *prop, - QQmlScript::Object *obj) -{ - if (prop->core.isQList()) { - genListProperty(prop, obj); - } else { - genPropertyAssignment(prop, obj); - } -} - -void QQmlCompiler::genListProperty(QQmlScript::Property *prop, - QQmlScript::Object *obj) -{ - int listType = enginePrivate->listType(prop->type); - - Instruction::FetchQList fetch; - fetch.property = prop->index; - bool listTypeIsInterface = QQmlMetaType::isInterface(listType); - fetch.type = listType; - output->addInstruction(fetch); - - for (QQmlScript::Value *v = prop->values.first(); v; v = QQmlScript::Property::ValueList::next(v)) { - - if (v->type == QQmlScript::Value::CreatedObject) { - - genObject(v->object); - if (listTypeIsInterface) { - Instruction::AssignObjectList assign; - assign.line = prop->location.start.line; - output->addInstruction(assign); - } else { - Instruction::StoreObjectQList store; - output->addInstruction(store); - } - - } else if (v->type == QQmlScript::Value::PropertyBinding) { - - genBindingAssignment(v, prop, obj); - - } - - } - - Instruction::PopQList pop; - output->addInstruction(pop); -} - -void QQmlCompiler::genPropertyAssignment(QQmlScript::Property *prop, - QQmlScript::Object *obj, - QQmlScript::Property *valueTypeProperty) -{ - for (QQmlScript::Value *v = prop->values.first(); v; v = QQmlScript::Property::ValueList::next(v)) { - - Q_ASSERT(v->type == QQmlScript::Value::CreatedObject || - v->type == QQmlScript::Value::PropertyBinding || - v->type == QQmlScript::Value::Literal); - - if (v->type == QQmlScript::Value::CreatedObject) { - - genObject(v->object); - - if (QQmlMetaType::isInterface(prop->type)) { - - Instruction::StoreInterface store; - store.line = v->object->location.start.line; - store.propertyIndex = prop->index; - output->addInstruction(store); - - } else if (prop->type == QMetaType::QVariant) { - - if (prop->core.isVarProperty()) { - Instruction::StoreVarObject store; - store.line = v->object->location.start.line; - store.propertyIndex = prop->index; - output->addInstruction(store); - } else { - Instruction::StoreVariantObject store; - store.line = v->object->location.start.line; - store.propertyIndex = prop->index; - output->addInstruction(store); - } - - - } else { - - Instruction::StoreObject store; - store.line = v->object->location.start.line; - store.propertyIndex = prop->index; - output->addInstruction(store); - - } - } else if (v->type == QQmlScript::Value::PropertyBinding) { - - genBindingAssignment(v, prop, obj, valueTypeProperty); - - } else if (v->type == QQmlScript::Value::Literal) { - - genLiteralAssignment(prop, v); - - } - - } - - for (QQmlScript::Value *v = prop->onValues.first(); v; v = QQmlScript::Property::ValueList::next(v)) { - - Q_ASSERT(v->type == QQmlScript::Value::ValueSource || - v->type == QQmlScript::Value::ValueInterceptor); - - if (v->type == QQmlScript::Value::ValueSource) { - genObject(v->object, valueTypeProperty?true:false); - - Instruction::StoreValueSource store; - if (valueTypeProperty) - store.property = genValueTypeData(prop, valueTypeProperty); - else - store.property = prop->core; - QQmlType *valueType = toQmlType(v->object); - store.castValue = valueType->propertyValueSourceCast(); - output->addInstruction(store); - - } else if (v->type == QQmlScript::Value::ValueInterceptor) { - genObject(v->object, valueTypeProperty?true:false); - - Instruction::StoreValueInterceptor store; - if (valueTypeProperty) - store.property = genValueTypeData(prop, valueTypeProperty); - else - store.property = prop->core; - QQmlType *valueType = toQmlType(v->object); - store.castValue = valueType->propertyValueInterceptorCast(); - output->addInstruction(store); - } - - } -} - -bool QQmlCompiler::buildIdProperty(QQmlScript::Property *prop, - QQmlScript::Object *obj) -{ - if (prop->value || - prop->values.isMany() || - prop->values.first()->object) - COMPILE_EXCEPTION(prop, tr("Invalid use of id property")); - - QQmlScript::Value *idValue = prop->values.first(); - QString val = idValue->primitive(); - - COMPILE_CHECK(checkValidId(idValue, val)); - - if (compileState->ids.value(val)) - COMPILE_EXCEPTION(prop, tr("id is not unique")); - - prop->values.first()->type = QQmlScript::Value::Id; - - obj->id = val; - addId(val, obj); - - return true; -} - -void QQmlCompiler::addId(const QString &id, QQmlScript::Object *obj) -{ - Q_UNUSED(id); - Q_ASSERT(!compileState->ids.value(id)); - Q_ASSERT(obj->id == id); - obj->idIndex = compileState->ids.count(); - compileState->ids.append(obj); -} - -void QQmlCompiler::addBindingReference(JSBindingReference *ref) -{ - Q_ASSERT(ref->value && !ref->value->bindingReference); - ref->value->bindingReference = ref; - compileState->totalBindingsCount++; - compileState->bindings.prepend(ref); -} - -void QQmlCompiler::saveComponentState() -{ - Q_ASSERT(compileState->root); - Q_ASSERT(compileState->root->componentCompileState == 0); - - compileState->root->componentCompileState = compileState; - - if (componentStats) - componentStats->savedComponentStats.append(componentStats->componentStat); -} - -QQmlCompilerTypes::ComponentCompileState * -QQmlCompiler::componentState(QQmlScript::Object *obj) -{ - Q_ASSERT(obj->componentCompileState); - return obj->componentCompileState; -} - -// Build attached property object. In this example, -// Text { -// GridView.row: 10 -// } -// GridView is an attached property object. -bool QQmlCompiler::buildAttachedProperty(QQmlScript::Property *prop, - QQmlScript::Object *obj, - const BindingContext &ctxt) -{ - Q_ASSERT(prop->value); - Q_ASSERT(prop->index != -1); // This is set in buildProperty() - - compileState->objectDepth.push(); - - obj->addAttachedProperty(prop); - - COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr())); - - compileState->objectDepth.pop(); - - return true; -} - - -// Build "grouped" properties. In this example: -// Text { -// font.pointSize: 12 -// font.family: "Helvetica" -// } -// font is a nested property. pointSize and family are not. -bool QQmlCompiler::buildGroupedProperty(QQmlScript::Property *prop, - QQmlScript::Object *obj, - const BindingContext &ctxt) -{ - Q_ASSERT(prop->type != 0); - Q_ASSERT(prop->index != -1); - - if (QQmlValueTypeFactory::isValueType(prop->type)) { - QQmlValueType *valueType = QQmlValueTypeFactory::valueType(prop->type); - if (prop->type >= 0 && valueType) { - if (!prop->values.isEmpty()) { - // Only error if we are assigning values, and not e.g. a property interceptor - for (QQmlScript::Property *dotProp = prop->value->properties.first(); dotProp; dotProp = prop->value->properties.next(dotProp)) { - if (!dotProp->values.isEmpty()) { - if (prop->values.first()->location < prop->value->location) { - COMPILE_EXCEPTION(prop->value, tr( "Property has already been assigned a value")); - } else { - COMPILE_EXCEPTION(prop->values.first(), tr( "Property has already been assigned a value")); - } - } - } - } - - if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration) { - COMPILE_EXCEPTION(prop, tr( "Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); - } - - if (prop->isAlias) { - for (QQmlScript::Property *vtProp = prop->value->properties.first(); vtProp; vtProp = prop->value->properties.next(vtProp)) { - vtProp->isAlias = true; - } - } - - COMPILE_CHECK(buildValueTypeProperty(valueType, prop->value, obj, ctxt.incr())); - - // When building a value type where sub components are declared, this - // code path is followed from buildProperty, even if there is a previous - // assignment to the value type as a whole. Therefore we need to look - // for (and build) assignments to the entire value type before looking - // for any onValue assignments. - for (QQmlScript::Value *v = prop->values.first(); v; v = QQmlScript::Property::ValueList::next(v)) { - if (v->object) { - COMPILE_EXCEPTION(v->object, tr("Objects cannot be assigned to value types")); - } - COMPILE_CHECK(buildPropertyLiteralAssignment(prop, obj, v, ctxt)); - } - - for (QQmlScript::Value *v = prop->onValues.first(); v; v = QQmlScript::Property::ValueList::next(v)) { - Q_ASSERT(v->object); - COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, obj, v, ctxt)); - } - - obj->addValueTypeProperty(prop); - } else { - COMPILE_EXCEPTION(prop, tr("Invalid grouped property access")); - } - } else { - // Load the nested property's meta type - prop->value->metatype = enginePrivate->propertyCacheForType(prop->type); - if (!prop->value->metatype) - COMPILE_EXCEPTION(prop, tr("Invalid grouped property access")); - - if (!prop->values.isEmpty()) - COMPILE_EXCEPTION(prop->values.first(), tr( "Cannot assign a value directly to a grouped property")); - - obj->addGroupedProperty(prop); - - compileState->objectDepth.push(); - - COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr())); - - compileState->objectDepth.pop(); - } - - return true; -} - -bool QQmlCompiler::buildValueTypeProperty(QObject *type, - QQmlScript::Object *obj, - QQmlScript::Object *baseObj, - const BindingContext &ctxt) -{ - compileState->objectDepth.push(); - - if (obj->defaultProperty) - COMPILE_EXCEPTION(obj, tr("Invalid property use")); - obj->metatype = enginePrivate->cache(type); - - for (QQmlScript::Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) { - - QQmlPropertyData *d = property(obj, prop->name()); - if (d == 0) - COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(prop->name().toString())); - - prop->index = d->coreIndex; - prop->type = d->propType; - prop->core = *d; - prop->isValueTypeSubProperty = true; - - if (prop->value) - COMPILE_EXCEPTION(prop, tr("Property assignment expected")); - - if (prop->values.isMany()) { - COMPILE_EXCEPTION(prop, tr("Single property assignment expected")); - } else if (!prop->values.isEmpty()) { - QQmlScript::Value *value = prop->values.first(); - - if (value->object) { - COMPILE_EXCEPTION(prop, tr("Unexpected object assignment")); - } else if (value->value.isScript()) { - // ### Check for writability - - //optimization for <Type>.<EnumValue> enum assignments - bool isEnumAssignment = false; - - if (prop->core.isEnum() || prop->core.propType == QMetaType::Int) - COMPILE_CHECK(testQualifiedEnumAssignment(prop, obj, value, &isEnumAssignment)); - - if (isEnumAssignment) { - value->type = QQmlScript::Value::Literal; - } else { - JSBindingReference *reference = pool->New<JSBindingReference>(); - reference->expression = value->value; - reference->property = prop; - reference->value = value; - reference->bindingContext = ctxt; - reference->bindingContext.owner++; - addBindingReference(reference); - value->type = QQmlScript::Value::PropertyBinding; - } - } else { - COMPILE_CHECK(testLiteralAssignment(prop, value)); - value->type = QQmlScript::Value::Literal; - } - } - - for (QQmlScript::Value *v = prop->onValues.first(); v; v = QQmlScript::Property::ValueList::next(v)) { - Q_ASSERT(v->object); - - COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, baseObj, v, ctxt)); - } - - obj->addValueProperty(prop); - } - - compileState->objectDepth.pop(); - - return true; -} - -// Build assignments to QML lists. QML lists are properties of type -// QQmlListProperty<T>. List properties can accept a list of -// objects, or a single binding. -bool QQmlCompiler::buildListProperty(QQmlScript::Property *prop, - QQmlScript::Object *obj, - const BindingContext &ctxt) -{ - Q_ASSERT(prop->core.isQList()); - - compileState->listDepth.push(); - - int t = prop->type; - - obj->addValueProperty(prop); - - int listType = enginePrivate->listType(t); - bool listTypeIsInterface = QQmlMetaType::isInterface(listType); - - bool assignedBinding = false; - for (QQmlScript::Value *v = prop->values.first(); v; v = QQmlScript::Property::ValueList::next(v)) { - if (v->object) { - v->type = QQmlScript::Value::CreatedObject; - COMPILE_CHECK(buildObject(v->object, ctxt)); - - // We check object coercian here. We check interface assignment - // at runtime. - if (!listTypeIsInterface) { - if (!canCoerce(listType, v->object)) { - COMPILE_EXCEPTION(v, tr("Cannot assign object to list")); - } - } - - } else if (v->value.isScript()) { - if (assignedBinding) - COMPILE_EXCEPTION(v, tr("Can only assign one binding to lists")); - - assignedBinding = true; - COMPILE_CHECK(buildBinding(v, prop, ctxt)); - } else { - COMPILE_EXCEPTION(v, tr("Cannot assign primitives to lists")); - } - } - - compileState->listDepth.pop(); - - return true; -} - -// Compiles an assignment to a QQmlScriptString property -bool QQmlCompiler::buildScriptStringProperty(QQmlScript::Property *prop, - QQmlScript::Object *obj, - const BindingContext &ctxt) -{ - if (prop->values.isMany()) - COMPILE_EXCEPTION(prop->values.first(), tr( "Cannot assign multiple values to a script property")); - - if (prop->values.first()->object) - COMPILE_EXCEPTION(prop->values.first(), tr( "Invalid property assignment: script expected")); - - prop->scriptStringScope = ctxt.stack; - obj->addScriptStringProperty(prop); - - return true; -} - -// Compile regular property assignments of the form "property: <value>" -bool QQmlCompiler::buildPropertyAssignment(QQmlScript::Property *prop, - QQmlScript::Object *obj, - const BindingContext &ctxt) -{ - obj->addValueProperty(prop); - - if (prop->values.isMany()) - COMPILE_EXCEPTION(prop->values.first(), tr( "Cannot assign multiple values to a singular property") ); - - for (QQmlScript::Value *v = prop->values.first(); v; v = QQmlScript::Property::ValueList::next(v)) { - if (v->object) { - - COMPILE_CHECK(buildPropertyObjectAssignment(prop, obj, v, ctxt)); - - } else { - - COMPILE_CHECK(buildPropertyLiteralAssignment(prop, obj, v, ctxt)); - - } - } - - for (QQmlScript::Value *v = prop->onValues.first(); v; v = QQmlScript::Property::ValueList::next(v)) { - Q_ASSERT(v->object); - COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, obj, v, ctxt)); - } - - return true; -} - -// Compile assigning a single object instance to a regular property -bool QQmlCompiler::buildPropertyObjectAssignment(QQmlScript::Property *prop, - QQmlScript::Object *obj, - QQmlScript::Value *v, - const BindingContext &ctxt) -{ - Q_ASSERT(prop->index != -1); - Q_ASSERT(v->object->type != -1); - - if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration) - COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); - - if (QQmlMetaType::isInterface(prop->type)) { - - // Assigning an object to an interface ptr property - COMPILE_CHECK(buildObject(v->object, ctxt)); - - v->type = QQmlScript::Value::CreatedObject; - - } else if (prop->type == QMetaType::QVariant) { - - // Assigning an object to a QVariant - COMPILE_CHECK(buildObject(v->object, ctxt)); - - v->type = QQmlScript::Value::CreatedObject; - } else { - // Normally buildObject() will set this up, but we need the static - // meta object earlier to test for assignability. It doesn't matter - // that there may still be outstanding synthesized meta object changes - // on this type, as they are not relevant for assignability testing - v->object->metatype = output->types[v->object->type].createPropertyCache(engine); - Q_ASSERT(v->object->metatype); - - // We want to raw metaObject here as the raw metaobject is the - // actual property type before we applied any extensions that might - // effect the properties on the type, but don't effect assignability - QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(prop->type); - - // Will be true if the assgned type inherits propertyMetaObject - bool isAssignable = false; - // Determine isAssignable value - if (propertyMetaObject) { - QQmlPropertyCache *c = v->object->metatype; - while (c && !isAssignable) { - isAssignable |= c == propertyMetaObject; - c = c->parent(); - } - } - - if (isAssignable) { - // Simple assignment - COMPILE_CHECK(buildObject(v->object, ctxt)); - - v->type = QQmlScript::Value::CreatedObject; - } else if (propertyMetaObject && propertyMetaObject->metaObject() == &QQmlComponent::staticMetaObject) { - // Automatic "Component" insertion - QQmlScript::Object *root = v->object; - QQmlScript::Object *component = pool->New<QQmlScript::Object>(); - component->type = componentTypeRef(); - component->metatype = enginePrivate->cache(&QQmlComponent::staticMetaObject); - component->location = root->location; - QQmlScript::Value *componentValue = pool->New<QQmlScript::Value>(); - componentValue->object = root; - component->getDefaultProperty()->addValue(componentValue); - v->object = component; - COMPILE_CHECK(buildPropertyObjectAssignment(prop, obj, v, ctxt)); - } else { - COMPILE_EXCEPTION(v->object, tr("Cannot assign object to property")); - } - } - - return true; -} - -// Compile assigning a single object instance to a regular property using the "on" syntax. -// -// For example: -// Item { -// NumberAnimation on x { } -// } -bool QQmlCompiler::buildPropertyOnAssignment(QQmlScript::Property *prop, - QQmlScript::Object *obj, - QQmlScript::Object *baseObj, - QQmlScript::Value *v, - const BindingContext &ctxt) -{ - Q_ASSERT(prop->index != -1); - Q_ASSERT(v->object->type != -1); - - Q_UNUSED(obj); - - if (!prop->core.isWritable()) - COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); - - - // Normally buildObject() will set this up, but we need the static - // meta object earlier to test for assignability. It doesn't matter - // that there may still be outstanding synthesized meta object changes - // on this type, as they are not relevant for assignability testing - v->object->metatype = output->types[v->object->type].createPropertyCache(engine); - Q_ASSERT(v->object->metatype); - - // Will be true if the assigned type inherits QQmlPropertyValueSource - bool isPropertyValue = false; - // Will be true if the assigned type inherits QQmlPropertyValueInterceptor - bool isPropertyInterceptor = false; - if (QQmlType *valueType = toQmlType(v->object)) { - isPropertyValue = valueType->propertyValueSourceCast() != -1; - isPropertyInterceptor = valueType->propertyValueInterceptorCast() != -1; - } - - if (isPropertyValue || isPropertyInterceptor) { - // Assign as a property value source - COMPILE_CHECK(buildObject(v->object, ctxt)); - - if (isPropertyInterceptor && baseObj->synthdata.isEmpty()) - buildDynamicMeta(baseObj, ForceCreation); - v->type = isPropertyValue ? QQmlScript::Value::ValueSource : QQmlScript::Value::ValueInterceptor; - } else { - COMPILE_EXCEPTION(v, tr("\"%1\" cannot operate on \"%2\"").arg(elementName(v->object)).arg(prop->name().toString())); - } - - return true; -} - -// Compile assigning a literal or binding to a regular property -bool QQmlCompiler::buildPropertyLiteralAssignment(QQmlScript::Property *prop, - QQmlScript::Object *obj, - QQmlScript::Value *v, - const BindingContext &ctxt) -{ - Q_ASSERT(prop->index != -1); - - if (v->value.isScript()) { - - //optimization for <Type>.<EnumValue> enum assignments - if (prop->core.isEnum() || prop->core.propType == QMetaType::Int) { - bool isEnumAssignment = false; - COMPILE_CHECK(testQualifiedEnumAssignment(prop, obj, v, &isEnumAssignment)); - if (isEnumAssignment) { - v->type = QQmlScript::Value::Literal; - return true; - } - } - - // Test for other binding optimizations - if (!buildLiteralBinding(v, prop, ctxt)) - COMPILE_CHECK(buildBinding(v, prop, ctxt)); - - } else { - - COMPILE_CHECK(testLiteralAssignment(prop, v)); - - v->type = QQmlScript::Value::Literal; - } - - return true; -} - -struct StaticQtMetaObject : public QObject -{ - static const QMetaObject *get() - { return &staticQtMetaObject; } -}; - -bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop, - QQmlScript::Object *obj, - QQmlScript::Value *v, - bool *isAssignment) -{ - bool isIntProp = (prop->core.propType == QMetaType::Int) && !prop->core.isEnum(); - *isAssignment = false; - if (!prop->core.isEnum() && !isIntProp) - return true; - - QMetaProperty mprop = obj->metatype->firstCppMetaObject()->property(prop->index); - - if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration) - COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); - - QString string = v->value.asString(); - if (!string.at(0).isUpper()) - return true; - - int dot = string.indexOf(QLatin1Char('.')); - if (dot == -1 || dot == string.length()-1) - return true; - - if (string.indexOf(QLatin1Char('.'), dot+1) != -1) - return true; - - QHashedStringRef typeName(string.constData(), dot); - QString enumValue = string.mid(dot+1); - - if (isIntProp) { - // Allow enum assignment to ints. - bool ok; - int enumval = evaluateEnum(typeName.toString(), enumValue.toUtf8(), &ok); - if (ok) { - v->type = QQmlScript::Value::Literal; - v->value = QQmlScript::Variant((double)enumval); - *isAssignment = true; - } - return true; - } - - QQmlType *type = 0; - unit->imports().resolveType(typeName, &type, 0, 0, 0); - - if (!type && typeName != QLatin1String("Qt")) - return true; - if (type && type->isComposite()) //No enums on composite (or composite singleton) types - return true; - - int value = 0; - bool ok = false; - - if (type && toQmlType(obj) == type) { - // When these two match, we can short cut the search - if (mprop.isFlagType()) { - value = mprop.enumerator().keysToValue(enumValue.toUtf8().constData(), &ok); - } else { - value = mprop.enumerator().keyToValue(enumValue.toUtf8().constData(), &ok); - } - } else { - // Otherwise we have to search the whole type - if (type) { - value = type->enumValue(QHashedStringRef(enumValue), &ok); - } else { - QByteArray enumName = enumValue.toUtf8(); - const QMetaObject *metaObject = StaticQtMetaObject::get(); - for (int ii = metaObject->enumeratorCount() - 1; !ok && ii >= 0; --ii) { - QMetaEnum e = metaObject->enumerator(ii); - value = e.keyToValue(enumName.constData(), &ok); - } - } - } - - if (!ok) - return true; - - v->type = QQmlScript::Value::Literal; - v->value = QQmlScript::Variant((double)value); - *isAssignment = true; - - return true; -} - -QQmlBinding::Identifier QQmlCompiler::bindingIdentifier(const Variant &value, const QString &name, QQmlCustomParser *customParser) -{ - JSBindingReference *reference = pool->New<JSBindingReference>(); - reference->expression = value; - reference->property = pool->New<QQmlScript::Property>(); - reference->property->setName(name); - reference->value = 0; - reference->bindingContext = QQmlCompilerTypes::BindingContext(customParser->object); - reference->bindingContext.owner++; - // Unfortunately this is required for example for PropertyChanges where the bindings - // will be executed in the dynamic scope of the target, so we can't resolve any lookups - // at run-time. - reference->disableLookupAcceleration = true; - - const int id = output->customParserBindings.count(); - output->customParserBindings.append(0); // Filled in later. - reference->customParserBindingsIndex = id; - - compileState->totalBindingsCount++; - compileState->bindings.prepend(reference); - - return id; -} - -// Ensures that the dynamic meta specification on obj is valid -bool QQmlCompiler::checkDynamicMeta(QQmlScript::Object *obj) -{ - if (output->types[obj->type].isFullyDynamicType) { - if (!obj->dynamicProperties.isEmpty()) - COMPILE_EXCEPTION(obj, tr("Fully dynamic types cannot declare new properties.")); - if (!obj->dynamicSignals.isEmpty()) - COMPILE_EXCEPTION(obj, tr("Fully dynamic types cannot declare new signals.")); - if (!obj->dynamicSlots.isEmpty()) - COMPILE_EXCEPTION(obj, tr("Fully Dynamic types cannot declare new functions.")); - } - - bool seenDefaultProperty = false; - - // We use a coarse grain, 31 bit hash to check if there are duplicates. - // Calculating the hash for the names is not a waste as we have to test - // them against the illegalNames set anyway. - QHashField propNames; - QHashField methodNames; - - // Check properties - for (QQmlScript::Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) { - const QQmlScript::Object::DynamicProperty &prop = *p; - - if (prop.isDefaultProperty) { - if (seenDefaultProperty) - COMPILE_EXCEPTION(&prop, tr("Duplicate default property")); - seenDefaultProperty = true; - } - - if (propNames.testAndSet(prop.name.hash())) { - for (QQmlScript::Object::DynamicProperty *p2 = obj->dynamicProperties.first(); p2 != p; - p2 = obj->dynamicProperties.next(p2)) { - if (p2->name == prop.name) { - COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line, - prop.nameLocation.column, - tr("Duplicate property name")); - } - } - } - - if (prop.name.at(0).isUpper()) { - COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line, - prop.nameLocation.column, - tr("Property names cannot begin with an upper case letter")); - } - - if (enginePrivate->v8engine()->illegalNames().contains(prop.name.toString())) { - COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line, - prop.nameLocation.column, - tr("Illegal property name")); - } - } - - for (QQmlScript::Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) { - const QQmlScript::Object::DynamicSignal &currSig = *s; - - if (methodNames.testAndSet(currSig.name.hash())) { - for (QQmlScript::Object::DynamicSignal *s2 = obj->dynamicSignals.first(); s2 != s; - s2 = obj->dynamicSignals.next(s2)) { - if (s2->name == currSig.name) - COMPILE_EXCEPTION(&currSig, tr("Duplicate signal name")); - } - } - - if (currSig.name.at(0).isUpper()) - COMPILE_EXCEPTION(&currSig, tr("Signal names cannot begin with an upper case letter")); - if (enginePrivate->v8engine()->illegalNames().contains(currSig.name.toString())) - COMPILE_EXCEPTION(&currSig, tr("Illegal signal name")); - } - - for (QQmlScript::Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) { - const QQmlScript::Object::DynamicSlot &currSlot = *s; - - if (methodNames.testAndSet(currSlot.name.hash())) { - for (QQmlScript::Object::DynamicSignal *s2 = obj->dynamicSignals.first(); s2; - s2 = obj->dynamicSignals.next(s2)) { - if (s2->name == currSlot.name) - COMPILE_EXCEPTION(&currSlot, tr("Duplicate method name")); - } - for (QQmlScript::Object::DynamicSlot *s2 = obj->dynamicSlots.first(); s2 != s; - s2 = obj->dynamicSlots.next(s2)) { - if (s2->name == currSlot.name) - COMPILE_EXCEPTION(&currSlot, tr("Duplicate method name")); - } - } - - if (currSlot.name.at(0).isUpper()) - COMPILE_EXCEPTION(&currSlot, tr("Method names cannot begin with an upper case letter")); - if (enginePrivate->v8engine()->illegalNames().contains(currSlot.name.toString())) - COMPILE_EXCEPTION(&currSlot, tr("Illegal method name")); - } - - return true; -} - -bool QQmlCompiler::mergeDynamicMetaProperties(QQmlScript::Object *obj) -{ - for (QQmlScript::Object::DynamicProperty *p = obj->dynamicProperties.first(); p; - p = obj->dynamicProperties.next(p)) { - - if (!p->defaultValue || p->type == QQmlScript::Object::DynamicProperty::Alias) - continue; - - QQmlScript::Property *property = 0; - if (p->isDefaultProperty) { - property = obj->getDefaultProperty(); - } else { - property = obj->getProperty(p->name); - if (!property->values.isEmpty()) - COMPILE_EXCEPTION(property, tr("Property value set multiple times")); - } - - if (p->isReadOnly) - property->isReadOnlyDeclaration = true; - - if (property->value) - COMPILE_EXCEPTION(property, tr("Invalid property nesting")); - - property->values.append(p->defaultValue->values); - } - return true; -} - -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(); -} - -static QAtomicInt classIndexCounter(0); - -bool QQmlCompiler::buildDynamicMeta(QQmlScript::Object *obj, DynamicMetaMode mode) -{ - Q_ASSERT(obj); - Q_ASSERT(obj->metatype); - - if (mode != ForceCreation && - obj->dynamicProperties.isEmpty() && - obj->dynamicSignals.isEmpty() && - obj->dynamicSlots.isEmpty()) - return true; - - Q_ASSERT(obj->synthCache == 0); - - struct TypeData { - QQmlScript::Object::DynamicProperty::Type dtype; - int metaType; - } builtinTypes[] = { - { QQmlScript::Object::DynamicProperty::Var, qMetaTypeId<QJSValue>() }, - { QQmlScript::Object::DynamicProperty::Variant, QMetaType::QVariant }, - { QQmlScript::Object::DynamicProperty::Int, QMetaType::Int }, - { QQmlScript::Object::DynamicProperty::Bool, QMetaType::Bool }, - { QQmlScript::Object::DynamicProperty::Real, QMetaType::Double }, - { QQmlScript::Object::DynamicProperty::String, QMetaType::QString }, - { QQmlScript::Object::DynamicProperty::Url, QMetaType::QUrl }, - { QQmlScript::Object::DynamicProperty::Color, QMetaType::QColor }, - { QQmlScript::Object::DynamicProperty::Font, QMetaType::QFont }, - { QQmlScript::Object::DynamicProperty::Time, QMetaType::QTime }, - { QQmlScript::Object::DynamicProperty::Date, QMetaType::QDate }, - { QQmlScript::Object::DynamicProperty::DateTime, QMetaType::QDateTime }, - { QQmlScript::Object::DynamicProperty::Rect, QMetaType::QRectF }, - { QQmlScript::Object::DynamicProperty::Point, QMetaType::QPointF }, - { QQmlScript::Object::DynamicProperty::Size, QMetaType::QSizeF }, - { QQmlScript::Object::DynamicProperty::Vector2D, QMetaType::QVector2D }, - { QQmlScript::Object::DynamicProperty::Vector3D, QMetaType::QVector3D }, - { QQmlScript::Object::DynamicProperty::Vector4D, QMetaType::QVector4D }, - { QQmlScript::Object::DynamicProperty::Matrix4x4, QMetaType::QMatrix4x4 }, - { QQmlScript::Object::DynamicProperty::Quaternion, QMetaType::QQuaternion } - }; - static const int builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData); - - QByteArray newClassName; - - if (compileState->root == obj && !compileState->nested) { - QString path = output->url.path(); - int lastSlash = path.lastIndexOf(QLatin1Char('/')); - if (lastSlash > -1) { - QString nameBase = path.mid(lastSlash + 1, path.length()-lastSlash-5); - if (!nameBase.isEmpty() && nameBase.at(0).isUpper()) - newClassName = nameBase.toUtf8() + "_QMLTYPE_" + - QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)); - } - } - if (newClassName.isEmpty()) { - newClassName = QQmlMetaObject(obj->metatype).className(); - newClassName.append("_QML_"); - newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1))); - } - QQmlPropertyCache *cache = obj->metatype->copyAndReserve(engine, obj->dynamicProperties.count(), - obj->dynamicProperties.count() + - obj->dynamicSignals.count() + - obj->dynamicSlots.count(), - obj->dynamicProperties.count() + - obj->dynamicSignals.count()); - - cache->_dynamicClassName = newClassName; - - int cStringNameCount = 0; - - int aliasCount = 0; - int varPropCount = 0; - - for (QQmlScript::Object::DynamicProperty *p = obj->dynamicProperties.first(); p; - p = obj->dynamicProperties.next(p)) { - - if (p->type == QQmlScript::Object::DynamicProperty::Alias) - aliasCount++; - else if (p->type == QQmlScript::Object::DynamicProperty::Var) - varPropCount++; - - if (p->name.isLatin1()) { - p->nameIndex = cStringNameCount; - cStringNameCount += p->name.length() + 7 /* strlen("Changed") */; - } - - // No point doing this for both the alias and non alias cases - QQmlPropertyData *d = property(obj, p->name); - if (d && d->isFinal()) - COMPILE_EXCEPTION(p, tr("Cannot override FINAL property")); - } - - for (QQmlScript::Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) { - if (s->name.isLatin1()) { - s->nameIndex = cStringNameCount; - cStringNameCount += s->name.length(); - } - } - - for (QQmlScript::Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) { - if (s->name.isLatin1()) { - s->nameIndex = cStringNameCount; - cStringNameCount += s->name.length(); - } - } - - char *cStringData = 0; - if (cStringNameCount) { - cache->_dynamicStringData.resize(cStringNameCount); - cStringData = cache->_dynamicStringData.data(); - - for (QQmlScript::Object::DynamicProperty *p = obj->dynamicProperties.first(); p; - p = obj->dynamicProperties.next(p)) { - - if (p->nameIndex == -1) continue; - - char *myData = cStringData + p->nameIndex; - for (int ii = 0; ii < p->name.length(); ++ii) - *myData++ = p->name.at(ii).unicode(); - *myData++ = 'C'; *myData++ = 'h'; *myData++ = 'a'; *myData++ = 'n'; - *myData++ = 'g'; *myData++ = 'e'; *myData++ = 'd'; - } - - for (QQmlScript::Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) { - - if (s->nameIndex == -1) continue; - - char *myData = cStringData + s->nameIndex; - for (int ii = 0; ii < s->name.length(); ++ii) - *myData++ = s->name.at(ii).unicode(); - } - - for (QQmlScript::Object::DynamicSignal *s = obj->dynamicSignals.first(); s; - s = obj->dynamicSignals.next(s)) { - - if (s->nameIndex == -1) continue; - - char *myData = cStringData + s->nameIndex; - for (int ii = 0; ii < s->name.length(); ++ii) - *myData++ = s->name.at(ii).unicode(); - } - } - - QByteArray dynamicData; - typedef QQmlVMEMetaData VMD; - - dynamicData = QByteArray(sizeof(QQmlVMEMetaData) + - obj->dynamicProperties.count() * sizeof(VMD::PropertyData) + - obj->dynamicSlots.count() * sizeof(VMD::MethodData) + - aliasCount * sizeof(VMD::AliasData), 0); - - int effectivePropertyIndex = cache->propertyIndexCacheStart; - int effectiveMethodIndex = cache->methodIndexCacheStart; - - // For property change signal override detection. - // We prepopulate a set of signal names which already exist in the object, - // and throw an error if there is a signal/method defined as an override. - QSet<QString> seenSignals; - seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged"); - QQmlPropertyCache *parentCache = cache; - while ((parentCache = parentCache->parent())) { - if (int pSigCount = parentCache->signalCount()) { - int pSigOffset = parentCache->signalOffset(); - for (int i = pSigOffset; i < pSigCount; ++i) { - QQmlPropertyData *currPSig = parentCache->signal(i); - // XXX TODO: find a better way to get signal name from the property data :-/ - for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin(); - iter != parentCache->stringCache.end(); ++iter) { - if (currPSig == (*iter).second) { - seenSignals.insert(iter.key()); - break; - } - } - } - } - } - - // First set up notify signals for properties - first normal, then var, then alias - enum { NSS_Normal = 0, NSS_Var = 1, NSS_Alias = 2 }; - for (int ii = NSS_Normal; ii <= NSS_Alias; ++ii) { // 0 == normal, 1 == var, 2 == alias - - if (ii == NSS_Var && varPropCount == 0) continue; - else if (ii == NSS_Alias && aliasCount == 0) continue; - - for (QQmlScript::Object::DynamicProperty *p = obj->dynamicProperties.first(); p; - p = obj->dynamicProperties.next(p)) { - - if ((ii == NSS_Normal && (p->type == QQmlScript::Object::DynamicProperty::Alias || - p->type == QQmlScript::Object::DynamicProperty::Var)) || - ((ii == NSS_Var) && (p->type != QQmlScript::Object::DynamicProperty::Var)) || - ((ii == NSS_Alias) && (p->type != QQmlScript::Object::DynamicProperty::Alias))) - continue; - - quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | - QQmlPropertyData::IsVMESignal; - - QString changedSigName = p->name.toString() + QLatin1String("Changed"); - seenSignals.insert(changedSigName); - - if (p->nameIndex != -1) { - QHashedCStringRef changedSignalName(cStringData + p->nameIndex, - p->name.length() + 7 /* strlen("Changed") */); - cache->appendSignal(changedSignalName, flags, effectiveMethodIndex++); - } else { - cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); - } - } - } - - // Dynamic signals - for (QQmlScript::Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) { - int paramCount = s->parameterNames.count(); - - QList<QByteArray> names; - QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0); - - if (paramCount) { - paramTypes[0] = paramCount; - - for (int i = 0; i < paramCount; ++i) { - if (s->parameterTypes.at(i) < builtinTypeCount) { - // built-in type - paramTypes[i + 1] = builtinTypes[s->parameterTypes.at(i)].metaType; - names.append(s->parameterNames.at(i).toString().toUtf8()); - } else { - // lazily resolved type - Q_ASSERT(s->parameterTypes.at(i) == QQmlScript::Object::DynamicProperty::Custom); - QQmlType *qmltype = 0; - if (!unit->imports().resolveType(s->parameterTypeNames.at(i).toString(), &qmltype, 0, 0, 0)) - COMPILE_EXCEPTION(s, tr("Invalid signal parameter type: %1").arg(s->parameterTypeNames.at(i).toString())); - - // We dont mind even if the composite type ends up being composite singleton, here - // we just acquire the metaTypeId. - if (qmltype->isComposite()) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - QQmlCompiledData *data = tdata->compiledData(); - - paramTypes[i + 1] = data->metaTypeId; - - tdata->release(); - } else { - paramTypes[i + 1] = qmltype->typeId(); - } - names.append(s->parameterNames.at(i).toString().toUtf8()); - } - } - } - - ((QQmlVMEMetaData *)dynamicData.data())->signalCount++; - - quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | - QQmlPropertyData::IsVMESignal; - if (paramCount) - flags |= QQmlPropertyData::HasArguments; - - QString signalName = s->name.toString(); - if (seenSignals.contains(signalName)) { - const QQmlScript::Object::DynamicSignal &currSig = *s; - COMPILE_EXCEPTION(&currSig, tr("Duplicate signal name: invalid override of property change signal or superclass signal")); - } - seenSignals.insert(signalName); - - if (s->nameIndex != -1) { - QHashedCStringRef name(cStringData + s->nameIndex, s->name.length(), s->name.hash()); - cache->appendSignal(name, flags, effectiveMethodIndex++, - paramCount?paramTypes.constData():0, names); - } else { - cache->appendSignal(signalName, flags, effectiveMethodIndex++, - paramCount?paramTypes.constData():0, names); - } - } - - - // Dynamic slots - for (QQmlScript::Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) { - int paramCount = s->parameterNames.count(); - - quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction; - - if (paramCount) - flags |= QQmlPropertyData::HasArguments; - - QString slotName = s->name.toString(); - if (seenSignals.contains(slotName)) { - const QQmlScript::Object::DynamicSlot &currSlot = *s; - COMPILE_EXCEPTION(&currSlot, tr("Duplicate method name: invalid override of property change signal or superclass signal")); - } - // Note: we don't append slotName to the seenSignals list, since we don't - // protect against overriding change signals or methods with properties. - - if (s->nameIndex != -1) { - QHashedCStringRef name(cStringData + s->nameIndex, s->name.length(), s->name.hash()); - cache->appendMethod(name, flags, effectiveMethodIndex++, s->parameterNames); - } else { - cache->appendMethod(slotName, flags, effectiveMethodIndex++, s->parameterNames); - } - } - - - // Dynamic properties (except var and aliases) - int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; - for (QQmlScript::Object::DynamicProperty *p = obj->dynamicProperties.first(); p; - p = obj->dynamicProperties.next(p)) { - - if (p->type == QQmlScript::Object::DynamicProperty::Alias || - p->type == QQmlScript::Object::DynamicProperty::Var) - continue; - - int propertyType = 0; - int vmePropertyType = 0; - quint32 propertyFlags = 0; - - if (p->type < builtinTypeCount) { - propertyType = builtinTypes[p->type].metaType; - vmePropertyType = propertyType; - - if (p->type == QQmlScript::Object::DynamicProperty::Variant) - propertyFlags |= QQmlPropertyData::IsQVariant; - } else { - Q_ASSERT(p->type == QQmlScript::Object::DynamicProperty::CustomList || - p->type == QQmlScript::Object::DynamicProperty::Custom); - - QQmlType *qmltype = 0; - if (!unit->imports().resolveType(p->customType.toString(), &qmltype, 0, 0, 0)) - COMPILE_EXCEPTION(p, tr("Invalid property type")); - - Q_ASSERT(qmltype); - if (qmltype->isComposite()) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - QQmlCompiledData *data = tdata->compiledData(); - - if (p->type == QQmlScript::Object::DynamicProperty::Custom) { - propertyType = data->metaTypeId; - vmePropertyType = QMetaType::QObjectStar; - } else { - propertyType = data->listMetaTypeId; - vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >(); - } - - tdata->release(); - } else { - if (p->type == QQmlScript::Object::DynamicProperty::Custom) { - propertyType = qmltype->typeId(); - vmePropertyType = QMetaType::QObjectStar; - } else { - propertyType = qmltype->qListTypeId(); - vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >(); - } - } - - if (p->type == QQmlScript::Object::DynamicProperty::Custom) - propertyFlags |= QQmlPropertyData::IsQObjectDerived; - else - propertyFlags |= QQmlPropertyData::IsQList; - } - - if (!p->isReadOnly && p->type != QQmlScript::Object::DynamicProperty::CustomList) - propertyFlags |= QQmlPropertyData::IsWritable; - - if (p->nameIndex != -1) { - QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(), - p->name.hash()); - if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16(); - cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - propertyType, effectiveSignalIndex); - } else { - QString propertyName = p->name.toString(); - if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName; - cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - propertyType, effectiveSignalIndex); - } - - effectiveSignalIndex++; - - VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); - (vmd->propertyData() + vmd->propertyCount)->propertyType = vmePropertyType; - vmd->propertyCount++; - } - - // Now do var properties - for (QQmlScript::Object::DynamicProperty *p = obj->dynamicProperties.first(); p && varPropCount; - p = obj->dynamicProperties.next(p)) { - - if (p->type != QQmlScript::Object::DynamicProperty::Var) - continue; - - quint32 propertyFlags = QQmlPropertyData::IsVarProperty; - if (!p->isReadOnly) - propertyFlags |= QQmlPropertyData::IsWritable; - - VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); - (vmd->propertyData() + vmd->propertyCount)->propertyType = QMetaType::QVariant; - vmd->propertyCount++; - ((QQmlVMEMetaData *)dynamicData.data())->varPropertyCount++; - - if (p->nameIndex != -1) { - QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(), - p->name.hash()); - if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16(); - cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - QMetaType::QVariant, effectiveSignalIndex); - } else { - QString propertyName = p->name.toString(); - if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName; - cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - QMetaType::QVariant, effectiveSignalIndex); - } - - effectiveSignalIndex++; - } - - // Alias property count. Actual data is setup in buildDynamicMetaAliases - ((QQmlVMEMetaData *)dynamicData.data())->aliasCount = aliasCount; - - // Dynamic slot data - comes after the property data - for (QQmlScript::Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) { - VMD::MethodData methodData = { /*runtimeFunctionIndex*/ 0, // To be filled in later - s->parameterNames.count(), - s->location.start.line }; - - VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); - VMD::MethodData &md = *(vmd->methodData() + vmd->methodCount); - vmd->methodCount++; - md = methodData; - - ComponentCompileState::PerObjectCompileData *cd = &compileState->jsCompileData[obj]; - - ComponentCompileState::CompiledMetaMethod cmm; - cmm.methodIndex = vmd->methodCount - 1; - cd->functionsToCompile.append(s->funcDecl); - cmm.compiledFunctionIndex = cd->functionsToCompile.count() - 1; - cd->compiledMetaMethods.append(cmm); - } - - if (aliasCount) - compileState->aliasingObjects.append(obj); - - obj->synthdata = dynamicData; - obj->synthCache = cache; - obj->metatype = cache; - - return true; -} - -bool QQmlCompiler::buildDynamicMetaAliases(QQmlScript::Object *obj) -{ - Q_ASSERT(obj->synthCache); - - QByteArray &dynamicData = obj->synthdata; - - QQmlPropertyCache *cache = obj->synthCache; - char *cStringData = cache->_dynamicStringData.data(); - - int effectiveSignalIndex = cache->signalHandlerIndexCacheStart + cache->propertyIndexCache.count(); - int effectivePropertyIndex = cache->propertyIndexCacheStart + cache->propertyIndexCache.count(); - int effectiveAliasIndex = 0; - - for (QQmlScript::Object::DynamicProperty *p = obj->dynamicProperties.first(); p; - p = obj->dynamicProperties.next(p)) { - - if (p->type != QQmlScript::Object::DynamicProperty::Alias) - continue; - - if (!p->defaultValue) - COMPILE_EXCEPTION(p, tr("No property alias location")); - - if (!p->defaultValue->values.isOne() || - p->defaultValue->values.first()->object || - !p->defaultValue->values.first()->value.isScript()) - COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location")); - - QQmlJS::AST::Node *node = p->defaultValue->values.first()->value.asAST(); - Q_ASSERT(node); - - QStringList alias = astNodeToStringList(node); - if (alias.count() < 1 || alias.count() > 3) - COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>")); - - QQmlScript::Object *idObject = compileState->ids.value(alias.at(0)); - if (!idObject) - COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias reference. Unable to find id \"%1\"").arg(alias.at(0))); - - int propIdx = -1; - int propType = 0; - int notifySignal = -1; - int flags = 0; - int type = 0; - bool writable = false; - bool resettable = false; - - quint32 propertyFlags = QQmlPropertyData::IsAlias; - - if (alias.count() == 2 || alias.count() == 3) { - QQmlPropertyData *property = this->property(idObject, alias.at(1)); - - if (!property || property->coreIndex > 0x0000FFFF) - COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location")); - - propIdx = property->coreIndex; - type = property->propType; - - writable = property->isWritable(); - resettable = property->isResettable(); - notifySignal = property->notifyIndex; - - if (alias.count() == 3) { - QQmlValueType *valueType = QQmlValueTypeFactory::valueType(type); - if (!valueType) - COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location")); - - propType = type; - - int valueTypeIndex = - valueType->metaObject()->indexOfProperty(alias.at(2).toUtf8().constData()); - if (valueTypeIndex == -1) - COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location")); - Q_ASSERT(valueTypeIndex <= 0x0000FFFF); - - propIdx |= (valueTypeIndex << 16); - if (valueType->metaObject()->property(valueTypeIndex).isEnumType()) - type = QVariant::Int; - else - type = valueType->metaObject()->property(valueTypeIndex).userType(); - - } else { - if (property->isEnum()) { - type = QVariant::Int; - } else { - // Copy type flags - propertyFlags |= property->getFlags() & QQmlPropertyData::PropTypeFlagMask; - - if (property->isVarProperty()) - propertyFlags |= QQmlPropertyData::IsQVariant; - - if (property->isQObject()) - flags |= QML_ALIAS_FLAG_PTR; - } - } - } else { - Q_ASSERT(idObject->type != -1); // How else did it get an id? - - const QQmlCompiledData::TypeReference &ref = output->types.at(idObject->type); - if (ref.type) - type = ref.type->typeId(); - else - type = ref.component->metaTypeId; - - flags |= QML_ALIAS_FLAG_PTR; - propertyFlags |= QQmlPropertyData::IsQObjectDerived; - } - - QQmlVMEMetaData::AliasData aliasData = { idObject->idIndex, propIdx, propType, flags, notifySignal }; - - typedef QQmlVMEMetaData VMD; - VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); - *(vmd->aliasData() + effectiveAliasIndex++) = aliasData; - - if (!p->isReadOnly && writable) - propertyFlags |= QQmlPropertyData::IsWritable; - else - propertyFlags &= ~QQmlPropertyData::IsWritable; - - if (resettable) - propertyFlags |= QQmlPropertyData::IsResettable; - else - propertyFlags &= ~QQmlPropertyData::IsResettable; - - if (p->nameIndex != -1) { - QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(), - p->name.hash()); - if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16(); - cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - type, effectiveSignalIndex++); - } else { - QString propertyName = p->name.toString(); - if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName; - cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - type, effectiveSignalIndex++); - } - } - - return true; -} - -bool QQmlCompiler::checkValidId(QQmlScript::Value *v, const QString &val) -{ - if (val.isEmpty()) - COMPILE_EXCEPTION(v, tr( "Invalid empty ID")); - - QChar ch = val.at(0); - if (ch.isLetter() && !ch.isLower()) - COMPILE_EXCEPTION(v, tr( "IDs cannot start with an uppercase letter")); - - QChar u(QLatin1Char('_')); - if (!ch.isLetter() && ch != u) - COMPILE_EXCEPTION(v, tr( "IDs must start with a letter or underscore")); - - for (int ii = 1; ii < val.count(); ++ii) { - ch = val.at(ii); - if (!ch.isLetterOrNumber() && ch != u) - COMPILE_EXCEPTION(v, tr( "IDs must contain only letters, numbers, and underscores")); - } - - if (enginePrivate->v8engine()->illegalNames().contains(val)) - COMPILE_EXCEPTION(v, tr( "ID illegally masks global JavaScript property")); - - return true; -} - -bool QQmlCompiler::buildBinding(QQmlScript::Value *value, - QQmlScript::Property *prop, - const BindingContext &ctxt) -{ - Q_ASSERT(prop->index != -1); - Q_ASSERT(prop->parent); - Q_ASSERT(prop->parent->metatype); - - if (!prop->core.isWritable() && !prop->core.isQList() && !prop->isReadOnlyDeclaration) - COMPILE_EXCEPTION(value, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); - - JSBindingReference *reference = pool->New<JSBindingReference>(); - reference->expression = value->value; - reference->property = prop; - reference->value = value; - reference->bindingContext = ctxt; - addBindingReference(reference); - value->type = QQmlScript::Value::PropertyBinding; - - return true; -} - -bool QQmlCompiler::buildLiteralBinding(QQmlScript::Value *v, - QQmlScript::Property *prop, - const QQmlCompilerTypes::BindingContext &) -{ - Q_ASSERT(v->value.isScript()); - - if (!prop->core.isWritable()) - return false; - - AST::Node *binding = v->value.asAST(); - - if (prop->type == QVariant::String) { - if (AST::CallExpression *e = AST::cast<AST::CallExpression *>(binding)) { - if (AST::IdentifierExpression *i = AST::cast<AST::IdentifierExpression *>(e->base)) { - if (i->name == qsTrId_string) { - AST::ArgumentList *arg1 = e->arguments?e->arguments:0; - AST::ArgumentList *arg2 = arg1?arg1->next:0; - - if (arg1 && arg1->expression->kind == AST::Node::Kind_StringLiteral && - (!arg2 || arg2->expression->kind == AST::Node::Kind_NumericLiteral) && - (!arg2 || !arg2->next)) { - - QStringRef text; - int n = -1; - - text = AST::cast<AST::StringLiteral *>(arg1->expression)->value; - if (arg2) n = (int)AST::cast<AST::NumericLiteral *>(arg2->expression)->value; - - TrBindingReference *reference = pool->New<TrBindingReference>(); - reference->dataType = BindingReference::TrId; - reference->text = text; - reference->n = n; - v->bindingReference = reference; - v->type = QQmlScript::Value::PropertyBinding; - return true; - } - - } else if (i->name == qsTr_string) { - - AST::ArgumentList *arg1 = e->arguments?e->arguments:0; - AST::ArgumentList *arg2 = arg1?arg1->next:0; - AST::ArgumentList *arg3 = arg2?arg2->next:0; - - if (arg1 && arg1->expression->kind == AST::Node::Kind_StringLiteral && - (!arg2 || arg2->expression->kind == AST::Node::Kind_StringLiteral) && - (!arg3 || arg3->expression->kind == AST::Node::Kind_NumericLiteral) && - (!arg3 || !arg3->next)) { - - QStringRef text; - QStringRef comment; - int n = -1; - - text = AST::cast<AST::StringLiteral *>(arg1->expression)->value; - if (arg2) comment = AST::cast<AST::StringLiteral *>(arg2->expression)->value; - if (arg3) n = (int)AST::cast<AST::NumericLiteral *>(arg3->expression)->value; - - TrBindingReference *reference = pool->New<TrBindingReference>(); - reference->dataType = BindingReference::Tr; - reference->text = text; - reference->comment = comment; - reference->n = n; - v->bindingReference = reference; - v->type = QQmlScript::Value::PropertyBinding; - return true; - } - - } - } - } - - } - - return false; -} - -void QQmlCompiler::genBindingAssignment(QQmlScript::Value *binding, - QQmlScript::Property *prop, - QQmlScript::Object *obj, - QQmlScript::Property *valueTypeProperty) -{ - Q_UNUSED(obj); - Q_ASSERT(binding->bindingReference); - - const BindingReference &ref = *binding->bindingReference; -#ifndef QT_NO_TRANSLATION - if (ref.dataType == BindingReference::TrId) { - const TrBindingReference &tr = static_cast<const TrBindingReference &>(ref); - - Instruction::StoreTrIdString store; - store.propertyIndex = prop->core.coreIndex; - store.text = output->indexForByteArray(tr.text.toUtf8()); - store.n = tr.n; - output->addInstruction(store); - } else if (ref.dataType == BindingReference::Tr) { - const TrBindingReference &tr = static_cast<const TrBindingReference &>(ref); - - Instruction::StoreTrString store; - store.propertyIndex = prop->core.coreIndex; - store.context = translationContextIndex(); - store.text = output->indexForByteArray(tr.text.toUtf8()); - store.comment = output->indexForByteArray(tr.comment.toUtf8()); - store.n = tr.n; - output->addInstruction(store); - } else -#endif - if (ref.dataType == BindingReference::QtScript) { - const JSBindingReference &js = static_cast<const JSBindingReference &>(ref); - - Instruction::StoreBinding store; - store.functionIndex = js.compiledIndex; - store.context = js.bindingContext.stack; - store.owner = js.bindingContext.owner; - store.line = binding->location.start.line; - store.column = binding->location.start.column; - store.isAlias = prop->isAlias; - - if (valueTypeProperty) { - store.isRoot = (compileState->root == valueTypeProperty->parent); - } else { - store.isRoot = (compileState->root == obj); - } - store.isFallback = false; - - Q_ASSERT(js.bindingContext.owner == 0 || - (js.bindingContext.owner != 0 && valueTypeProperty)); - if (js.bindingContext.owner) { - store.property = genValueTypeData(prop, valueTypeProperty); - } else { - store.property = prop->core; - } - - output->addInstruction(store); - } else { - Q_ASSERT(!"Unhandled BindingReference::DataType type"); - } -} - -int QQmlCompiler::genContextCache() -{ - if (compileState->ids.count() == 0) - return -1; - - QVector<QQmlContextData::ObjectIdMapping> cache(compileState->ids.count()); - int i = 0; - for (QQmlScript::Object *o = compileState->ids.first(); o; o = compileState->ids.next(o), ++i) - cache[i] = QQmlContextData::ObjectIdMapping(o->id, o->idIndex); - - output->contextCaches.append(cache); - return output->contextCaches.count() - 1; -} - -QQmlPropertyData -QQmlCompiler::genValueTypeData(QQmlScript::Property *valueTypeProp, - QQmlScript::Property *prop) -{ - QQmlValueType *vt = QQmlValueTypeFactory::valueType(prop->type); - Q_ASSERT(vt); - return QQmlPropertyPrivate::saveValueType(prop->core, vt->metaObject(), valueTypeProp->index, engine); -} - -bool QQmlCompiler::completeComponentBuild() -{ - if (componentStats) - componentStats->componentStat.ids = compileState->ids.count(); - - for (QQmlScript::Object *aliasObject = compileState->aliasingObjects.first(); aliasObject; - aliasObject = compileState->aliasingObjects.next(aliasObject)) - COMPILE_CHECK(buildDynamicMetaAliases(aliasObject)); - - const QQmlScript::Parser &parser = unit->parser(); - QQmlJS::Engine *jsEngine = parser.jsEngine(); - QQmlJS::MemoryPool *pool = jsEngine->pool(); - QStringList stringPool; - stringPool.append(QString()); - - for (JSBindingReference *b = compileState->bindings.first(); b; b = b->nextReference) { - - JSBindingReference &binding = *b; - binding.dataType = BindingReference::QtScript; - - QQmlJS::AST::Node *node = binding.expression.asAST(); - // Always wrap this in an ExpressionStatement, to make sure that - // property var foo: function() { ... } results in a closure initialization. - if (!node->statementCast()) { - AST::ExpressionNode *expr = node->expressionCast(); - node = new (pool) AST::ExpressionStatement(expr); - } - - ComponentCompileState::PerObjectCompileData *cd = &compileState->jsCompileData[b->bindingContext.object]; - QtQml::CompiledFunctionOrExpression f; - f.node = node; - QString name = binding.property->name().toString().prepend(QStringLiteral("expression for ")); - stringPool.append(name); - f.nameIndex = stringPool.count() - 1; - f.disableAcceleratedLookups = binding.disableLookupAcceleration; - cd->functionsToCompile.append(f); - binding.compiledIndex = cd->functionsToCompile.count() - 1; - - if (componentStats && b->value) - componentStats->componentStat.scriptBindings.append(b->value->location); - } - - if (!compileState->jsCompileData.isEmpty()) { - const QString &sourceCode = jsEngine->code(); - AST::UiProgram *qmlRoot = parser.qmlRoot(); - - JSCodeGen jsCodeGen(unit->finalUrlString(), sourceCode, jsModule.data(), jsEngine, qmlRoot, output->importCache, stringPool); - - JSCodeGen::ObjectIdMapping idMapping; - if (compileState->ids.count() > 0) { - idMapping.reserve(compileState->ids.count()); - for (QQmlScript::Object *o = compileState->ids.first(); o; o = compileState->ids.next(o)) { - JSCodeGen::IdMapping m; - m.name = o->id; - m.idIndex = o->idIndex; - if (output->types[o->type].isFullyDynamicType) - m.type = 0; - else - m.type = o->metatype; - idMapping << m; - } - } - - jsCodeGen.beginContextScope(idMapping, compileState->root->metatype); - - for (QHash<QQmlScript::Object *, ComponentCompileState::PerObjectCompileData>::Iterator it = compileState->jsCompileData.begin(); - it != compileState->jsCompileData.end(); ++it) { - QQmlScript::Object *scopeObject = it.key(); - ComponentCompileState::PerObjectCompileData *cd = &it.value(); - - jsCodeGen.beginObjectScope(scopeObject->metatype); - - cd->runtimeFunctionIndices = jsCodeGen.generateJSCodeForFunctionsAndBindings(cd->functionsToCompile); - QList<QQmlError> errors = jsCodeGen.errors(); - if (!errors.isEmpty()) { - exceptions << errors; - return false; - } - - foreach (const QQmlCompilerTypes::ComponentCompileState::CompiledMetaMethod &cmm, cd->compiledMetaMethods) { - typedef QQmlVMEMetaData VMD; - VMD *vmd = (QQmlVMEMetaData *)scopeObject->synthdata.data(); - VMD::MethodData &md = *(vmd->methodData() + cmm.methodIndex); - md.runtimeFunctionIndex = cd->runtimeFunctionIndices.at(cmm.compiledFunctionIndex); - } - } - - for (JSBindingReference *b = compileState->bindings.first(); b; b = b->nextReference) { - JSBindingReference &binding = *b; - binding.compiledIndex = compileState->jsCompileData[binding.bindingContext.object].runtimeFunctionIndices[binding.compiledIndex]; - if (!binding.value) { // Must be a binding requested from custom parser - Q_ASSERT(binding.customParserBindingsIndex >= 0 && binding.customParserBindingsIndex < output->customParserBindings.count()); - output->customParserBindings[binding.customParserBindingsIndex] = binding.compiledIndex; - } - } - } - - // Check pop()'s matched push()'s - Q_ASSERT(compileState->objectDepth.depth() == 0); - Q_ASSERT(compileState->listDepth.depth() == 0); - - saveComponentState(); - - return true; -} - -void QQmlCompiler::dumpStats() -{ - Q_ASSERT(componentStats); - qWarning().nospace() << "QML Document: " << output->url.toString(); - for (int ii = 0; ii < componentStats->savedComponentStats.count(); ++ii) { - const ComponentStat &stat = componentStats->savedComponentStats.at(ii); - qWarning().nospace() << " Component Line " << stat.lineNumber; - qWarning().nospace() << " Total Objects: " << stat.objects; - qWarning().nospace() << " IDs Used: " << stat.ids; - - qWarning().nospace() << " QScript Bindings: " << stat.scriptBindings.count(); - { - QByteArray output; - for (int ii = 0; ii < stat.scriptBindings.count(); ++ii) { - if (0 == (ii % 10)) { - if (ii) output.append('\n'); - output.append(" "); - } - - output.append('('); - output.append(QByteArray::number(stat.scriptBindings.at(ii).start.line)); - output.append(':'); - output.append(QByteArray::number(stat.scriptBindings.at(ii).start.column)); - output.append(") "); - } - if (!output.isEmpty()) - qWarning().nospace() << output.constData(); - } - } -} - -/*! - Returns true if from can be assigned to a (QObject) property of type - to. -*/ -bool QQmlCompiler::canCoerce(int to, QQmlScript::Object *from) -{ - QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to); - QQmlPropertyCache *fromMo = from->metatype; - - while (fromMo) { - if (fromMo == toMo) - return true; - fromMo = fromMo->parent(); - } - return false; -} - -/*! - Returns the element name, as written in the QML file, for o. -*/ -QString QQmlCompiler::elementName(QQmlScript::Object *o) -{ - Q_ASSERT(o); - if (o->type != -1) { - return unit->parser().referencedTypes().at(o->type)->name; - } else { - return QString(); - } -} - -QQmlType *QQmlCompiler::toQmlType(QQmlScript::Object *from) -{ - if (from->type != -1 && output->types.at(from->type).type) - return output->types.at(from->type).type; - - const QMetaObject *mo = from->metatype->firstCppMetaObject(); - QQmlType *type = 0; - while (!type && mo) { - type = QQmlMetaType::qmlType(mo); - mo = mo->superClass(); - } - return type; -} - -QStringList QQmlCompiler::deferredProperties(QQmlScript::Object *obj) -{ - const QMetaObject *mo = obj->metatype->firstCppMetaObject(); - - int idx = mo->indexOfClassInfo("DeferredPropertyNames"); - if (idx == -1) - return QStringList(); - - QMetaClassInfo classInfo = mo->classInfo(idx); - QStringList rv = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); - return rv; -} - -QQmlPropertyCache * -QQmlCompiler::propertyCacheForObject(QQmlScript::Object *object) - { - if (object->synthCache) - return object->synthCache; - else if (object->type != -1) - return output->types[object->type].createPropertyCache(engine); - else - return object->metatype; -} - -QQmlPropertyData * -QQmlCompiler::property(QQmlScript::Object *object, int index) -{ - QQmlPropertyCache *cache = propertyCacheForObject(object); - - return cache->property(index); -} - -QQmlPropertyData * -QQmlCompiler::property(QQmlScript::Object *object, const QHashedStringRef &name, bool *notInRevision) -{ - if (notInRevision) *notInRevision = false; - - QQmlPropertyCache *cache = propertyCacheForObject(object); - - QQmlPropertyData *d = cache->property(name, 0, 0); - - // Find the first property - while (d && d->isFunction()) - d = cache->overrideData(d); - - if (d && !cache->isAllowedInRevision(d)) { - if (notInRevision) *notInRevision = true; - return 0; - } else { - return d; - } -} - -// This code must match the semantics of QQmlPropertyPrivate::findSignalByName -QQmlPropertyData * -QQmlCompiler::signal(QQmlScript::Object *object, const QHashedStringRef &name, bool *notInRevision) -{ - if (notInRevision) *notInRevision = false; - - QQmlPropertyCache *cache = propertyCacheForObject(object); - - - QQmlPropertyData *d = cache->property(name, 0, 0); - if (notInRevision) *notInRevision = false; - - while (d && !(d->isFunction())) - d = cache->overrideData(d); - - if (d && !cache->isAllowedInRevision(d)) { - if (notInRevision) *notInRevision = true; - return 0; - } else if (d && d->isSignal()) { - return d; - } - - if (name.endsWith(Changed_string)) { - QHashedStringRef propName = name.mid(0, name.length() - Changed_string.length()); - - d = property(object, propName, notInRevision); - if (d) - return cache->signal(d->notifyIndex); - } - - return 0; -} - -// This code must match the semantics of QQmlPropertyPrivate::findSignalByName -int QQmlCompiler::indexOfSignal(QQmlScript::Object *object, const QString &name, - bool *notInRevision) -{ - QQmlPropertyData *d = signal(object, QStringRef(&name), notInRevision); - return d?d->coreIndex:-1; -} - -int QQmlCompiler::indexOfProperty(QQmlScript::Object *object, const QString &name, - bool *notInRevision) -{ - return indexOfProperty(object, QStringRef(&name), notInRevision); -} - -int QQmlCompiler::indexOfProperty(QQmlScript::Object *object, const QHashedStringRef &name, - bool *notInRevision) -{ - QQmlPropertyData *d = property(object, name, notInRevision); - return d?d->coreIndex:-1; -} - -QT_END_NAMESPACE |