/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the QtDeclarative module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** 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, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia 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. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "private/qdeclarativecompiler_p.h" | |
#include "private/qdeclarativeparser_p.h" | |
#include "private/qdeclarativescriptparser_p.h" | |
#include "qdeclarativepropertyvaluesource.h" | |
#include "qdeclarativecomponent.h" | |
#include "private/qmetaobjectbuilder_p.h" | |
#include "private/qdeclarativestringconverters_p.h" | |
#include "private/qdeclarativeengine_p.h" | |
#include "qdeclarativeengine.h" | |
#include "qdeclarativecontext.h" | |
#include "private/qdeclarativemetatype_p.h" | |
#include "private/qdeclarativecustomparser_p_p.h" | |
#include "private/qdeclarativecontext_p.h" | |
#include "private/qdeclarativecomponent_p.h" | |
#include "parser/qdeclarativejsast_p.h" | |
#include "private/qdeclarativevmemetaobject_p.h" | |
#include "private/qdeclarativeexpression_p.h" | |
#include "private/qdeclarativeproperty_p.h" | |
#include "private/qdeclarativerewrite_p.h" | |
#include "qdeclarativescriptstring.h" | |
#include "private/qdeclarativeglobal_p.h" | |
#include "private/qdeclarativescriptparser_p.h" | |
#include "private/qdeclarativebinding_p.h" | |
#include "private/qdeclarativecompiledbindings_p.h" | |
#include "private/qdeclarativeglobalscriptclass_p.h" | |
#include <QColor> | |
#include <QDebug> | |
#include <QPointF> | |
#include <QSizeF> | |
#include <QRectF> | |
#include <QAtomicInt> | |
#include <QtCore/qdebug.h> | |
#include <QtCore/qdatetime.h> | |
QT_BEGIN_NAMESPACE | |
DEFINE_BOOL_CONFIG_OPTION(compilerDump, QML_COMPILER_DUMP); | |
DEFINE_BOOL_CONFIG_OPTION(compilerStatDump, QML_COMPILER_STATS); | |
DEFINE_BOOL_CONFIG_OPTION(bindingsDump, QML_BINDINGS_DUMP); | |
using namespace QDeclarativeParser; | |
/*! | |
Instantiate a new QDeclarativeCompiler. | |
*/ | |
QDeclarativeCompiler::QDeclarativeCompiler() | |
: output(0), engine(0), unitRoot(0), unit(0) | |
{ | |
} | |
/*! | |
Returns true if the last call to compile() caused errors. | |
\sa errors() | |
*/ | |
bool QDeclarativeCompiler::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<QDeclarativeError> QDeclarativeCompiler::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 QDeclarativeCompiler::isAttachedPropertyName(const QByteArray &name) | |
{ | |
return !name.isEmpty() && name.at(0) >= 'A' && name.at(0) <= 'Z'; | |
} | |
/*! | |
Returns true if \a name refers to a signal property, false otherwise. | |
Signal property names are those that start with "on", followed by a capital | |
letter. | |
*/ | |
bool QDeclarativeCompiler::isSignalPropertyName(const QByteArray &name) | |
{ | |
return name.length() >= 3 && name.startsWith("on") && | |
'A' <= name.at(2) && 'Z' >= name.at(2); | |
} | |
/*! | |
\macro COMPILE_EXCEPTION | |
\internal | |
Inserts an error into the QDeclarativeCompiler 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(QString::fromUtf8(property->name))); | |
\endcode | |
*/ | |
#define COMPILE_EXCEPTION(token, desc) \ | |
{ \ | |
QString exceptionDescription; \ | |
QDeclarativeError error; \ | |
error.setUrl(output->url); \ | |
error.setLine((token)->location.start.line); \ | |
error.setColumn((token)->location.start.column); \ | |
error.setDescription(desc.trimmed()); \ | |
exceptions << error; \ | |
return false; \ | |
} | |
/*! | |
\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 QDeclarativeCompiler::testLiteralAssignment(const QMetaProperty &prop, | |
QDeclarativeParser::Value *v) | |
{ | |
QString string = v->value.asString(); | |
if (!prop.isWritable()) | |
COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop.name()))); | |
if (prop.isEnumType()) { | |
int value; | |
if (prop.isFlagType()) { | |
value = prop.enumerator().keysToValue(string.toUtf8().constData()); | |
} else | |
value = prop.enumerator().keyToValue(string.toUtf8().constData()); | |
if (value == -1) | |
COMPILE_EXCEPTION(v, tr("Invalid property assignment: unknown enumeration")); | |
return true; | |
} | |
int type = prop.userType(); | |
switch(type) { | |
case -1: | |
break; | |
case QVariant::String: | |
if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: string expected")); | |
break; | |
case QVariant::Url: | |
if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: url expected")); | |
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; | |
QDeclarativeStringConverters::colorFromString(string, &ok); | |
if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: color expected")); | |
} | |
break; | |
#ifndef QT_NO_DATESTRING | |
case QVariant::Date: | |
{ | |
bool ok; | |
QDeclarativeStringConverters::dateFromString(string, &ok); | |
if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: date expected")); | |
} | |
break; | |
case QVariant::Time: | |
{ | |
bool ok; | |
QDeclarativeStringConverters::timeFromString(string, &ok); | |
if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: time expected")); | |
} | |
break; | |
case QVariant::DateTime: | |
{ | |
bool ok; | |
QDeclarativeStringConverters::dateTimeFromString(string, &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; | |
QPointF point = QDeclarativeStringConverters::pointFFromString(string, &ok); | |
if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: point expected")); | |
} | |
break; | |
case QVariant::Size: | |
case QVariant::SizeF: | |
{ | |
bool ok; | |
QSizeF size = QDeclarativeStringConverters::sizeFFromString(string, &ok); | |
if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: size expected")); | |
} | |
break; | |
case QVariant::Rect: | |
case QVariant::RectF: | |
{ | |
bool ok; | |
QRectF rect = QDeclarativeStringConverters::rectFFromString(string, &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: | |
{ | |
bool ok; | |
QDeclarativeStringConverters::vector3DFromString(string, &ok); | |
if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: 3D vector expected")); | |
} | |
break; | |
default: | |
{ | |
int t = prop.userType(); | |
QDeclarativeMetaType::StringConverter converter = | |
QDeclarativeMetaType::customStringConverter(t); | |
if (!converter) | |
COMPILE_EXCEPTION(v, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QVariant::typeToName(prop.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 QDeclarativeCompiler::genLiteralAssignment(const QMetaProperty &prop, | |
QDeclarativeParser::Value *v) | |
{ | |
QString string = v->value.asString(); | |
QDeclarativeInstruction instr; | |
instr.line = v->location.start.line; | |
if (prop.isEnumType()) { | |
int value; | |
if (prop.isFlagType()) { | |
value = prop.enumerator().keysToValue(string.toUtf8().constData()); | |
} else | |
value = prop.enumerator().keyToValue(string.toUtf8().constData()); | |
instr.type = QDeclarativeInstruction::StoreInteger; | |
instr.storeInteger.propertyIndex = prop.propertyIndex(); | |
instr.storeInteger.value = value; | |
output->bytecode << instr; | |
return; | |
} | |
int type = prop.userType(); | |
switch(type) { | |
case -1: | |
{ | |
if (v->value.isNumber()) { | |
double n = v->value.asNumber(); | |
if (double(int(n)) == n) { | |
instr.type = QDeclarativeInstruction::StoreVariantInteger; | |
instr.storeInteger.propertyIndex = prop.propertyIndex(); | |
instr.storeInteger.value = int(n); | |
} else { | |
instr.type = QDeclarativeInstruction::StoreVariantDouble; | |
instr.storeDouble.propertyIndex = prop.propertyIndex(); | |
instr.storeDouble.value = n; | |
} | |
} else if(v->value.isBoolean()) { | |
instr.type = QDeclarativeInstruction::StoreVariantBool; | |
instr.storeBool.propertyIndex = prop.propertyIndex(); | |
instr.storeBool.value = v->value.asBoolean(); | |
} else { | |
instr.type = QDeclarativeInstruction::StoreVariant; | |
instr.storeString.propertyIndex = prop.propertyIndex(); | |
instr.storeString.value = output->indexForString(string); | |
} | |
} | |
break; | |
case QVariant::String: | |
{ | |
instr.type = QDeclarativeInstruction::StoreString; | |
instr.storeString.propertyIndex = prop.propertyIndex(); | |
instr.storeString.value = output->indexForString(string); | |
} | |
break; | |
case QVariant::Url: | |
{ | |
instr.type = QDeclarativeInstruction::StoreUrl; | |
QUrl u = string.isEmpty() ? QUrl() : output->url.resolved(QUrl(string)); | |
instr.storeUrl.propertyIndex = prop.propertyIndex(); | |
instr.storeUrl.value = output->indexForUrl(u); | |
} | |
break; | |
case QVariant::UInt: | |
{ | |
instr.type = QDeclarativeInstruction::StoreInteger; | |
instr.storeInteger.propertyIndex = prop.propertyIndex(); | |
instr.storeInteger.value = uint(v->value.asNumber()); | |
} | |
break; | |
case QVariant::Int: | |
{ | |
instr.type = QDeclarativeInstruction::StoreInteger; | |
instr.storeInteger.propertyIndex = prop.propertyIndex(); | |
instr.storeInteger.value = int(v->value.asNumber()); | |
} | |
break; | |
case QMetaType::Float: | |
{ | |
instr.type = QDeclarativeInstruction::StoreFloat; | |
instr.storeFloat.propertyIndex = prop.propertyIndex(); | |
instr.storeFloat.value = float(v->value.asNumber()); | |
} | |
break; | |
case QVariant::Double: | |
{ | |
instr.type = QDeclarativeInstruction::StoreDouble; | |
instr.storeDouble.propertyIndex = prop.propertyIndex(); | |
instr.storeDouble.value = v->value.asNumber(); | |
} | |
break; | |
case QVariant::Color: | |
{ | |
QColor c = QDeclarativeStringConverters::colorFromString(string); | |
instr.type = QDeclarativeInstruction::StoreColor; | |
instr.storeColor.propertyIndex = prop.propertyIndex(); | |
instr.storeColor.value = c.rgba(); | |
} | |
break; | |
#ifndef QT_NO_DATESTRING | |
case QVariant::Date: | |
{ | |
QDate d = QDeclarativeStringConverters::dateFromString(string); | |
instr.type = QDeclarativeInstruction::StoreDate; | |
instr.storeDate.propertyIndex = prop.propertyIndex(); | |
instr.storeDate.value = d.toJulianDay(); | |
} | |
break; | |
case QVariant::Time: | |
{ | |
QTime time = QDeclarativeStringConverters::timeFromString(string); | |
int data[] = { time.hour(), time.minute(), | |
time.second(), time.msec() }; | |
int index = output->indexForInt(data, 4); | |
instr.type = QDeclarativeInstruction::StoreTime; | |
instr.storeTime.propertyIndex = prop.propertyIndex(); | |
instr.storeTime.valueIndex = index; | |
} | |
break; | |
case QVariant::DateTime: | |
{ | |
QDateTime dateTime = QDeclarativeStringConverters::dateTimeFromString(string); | |
int data[] = { dateTime.date().toJulianDay(), | |
dateTime.time().hour(), | |
dateTime.time().minute(), | |
dateTime.time().second(), | |
dateTime.time().msec() }; | |
int index = output->indexForInt(data, 5); | |
instr.type = QDeclarativeInstruction::StoreDateTime; | |
instr.storeDateTime.propertyIndex = prop.propertyIndex(); | |
instr.storeDateTime.valueIndex = index; | |
} | |
break; | |
#endif // QT_NO_DATESTRING | |
case QVariant::Point: | |
case QVariant::PointF: | |
{ | |
bool ok; | |
QPointF point = | |
QDeclarativeStringConverters::pointFFromString(string, &ok); | |
float data[] = { float(point.x()), float(point.y()) }; | |
int index = output->indexForFloat(data, 2); | |
if (type == QVariant::PointF) | |
instr.type = QDeclarativeInstruction::StorePointF; | |
else | |
instr.type = QDeclarativeInstruction::StorePoint; | |
instr.storeRealPair.propertyIndex = prop.propertyIndex(); | |
instr.storeRealPair.valueIndex = index; | |
} | |
break; | |
case QVariant::Size: | |
case QVariant::SizeF: | |
{ | |
bool ok; | |
QSizeF size = QDeclarativeStringConverters::sizeFFromString(string, &ok); | |
float data[] = { float(size.width()), float(size.height()) }; | |
int index = output->indexForFloat(data, 2); | |
if (type == QVariant::SizeF) | |
instr.type = QDeclarativeInstruction::StoreSizeF; | |
else | |
instr.type = QDeclarativeInstruction::StoreSize; | |
instr.storeRealPair.propertyIndex = prop.propertyIndex(); | |
instr.storeRealPair.valueIndex = index; | |
} | |
break; | |
case QVariant::Rect: | |
case QVariant::RectF: | |
{ | |
bool ok; | |
QRectF rect = QDeclarativeStringConverters::rectFFromString(string, &ok); | |
float data[] = { float(rect.x()), float(rect.y()), | |
float(rect.width()), float(rect.height()) }; | |
int index = output->indexForFloat(data, 4); | |
if (type == QVariant::RectF) | |
instr.type = QDeclarativeInstruction::StoreRectF; | |
else | |
instr.type = QDeclarativeInstruction::StoreRect; | |
instr.storeRect.propertyIndex = prop.propertyIndex(); | |
instr.storeRect.valueIndex = index; | |
} | |
break; | |
case QVariant::Bool: | |
{ | |
bool b = v->value.asBoolean(); | |
instr.type = QDeclarativeInstruction::StoreBool; | |
instr.storeBool.propertyIndex = prop.propertyIndex(); | |
instr.storeBool.value = b; | |
} | |
break; | |
case QVariant::Vector3D: | |
{ | |
bool ok; | |
QVector3D vector = | |
QDeclarativeStringConverters::vector3DFromString(string, &ok); | |
float data[] = { float(vector.x()), float(vector.y()), float(vector.z()) }; | |
int index = output->indexForFloat(data, 3); | |
instr.type = QDeclarativeInstruction::StoreVector3D; | |
instr.storeRealPair.propertyIndex = prop.propertyIndex(); | |
instr.storeRealPair.valueIndex = index; | |
} | |
break; | |
default: | |
{ | |
int t = prop.userType(); | |
int index = output->customTypeData.count(); | |
instr.type = QDeclarativeInstruction::AssignCustomType; | |
instr.assignCustomType.propertyIndex = prop.propertyIndex(); | |
instr.assignCustomType.valueIndex = index; | |
QDeclarativeCompiledData::CustomTypeData data; | |
data.index = output->indexForString(string); | |
data.type = t; | |
output->customTypeData << data; | |
} | |
break; | |
} | |
output->bytecode << instr; | |
} | |
/*! | |
Resets data by clearing the lists that the QDeclarativeCompiler modifies. | |
*/ | |
void QDeclarativeCompiler::reset(QDeclarativeCompiledData *data) | |
{ | |
data->types.clear(); | |
data->primitives.clear(); | |
data->floatData.clear(); | |
data->intData.clear(); | |
data->customTypeData.clear(); | |
data->datas.clear(); | |
data->bytecode.clear(); | |
} | |
/*! | |
Compile \a unit, and store the output in \a out. \a engine is the QDeclarativeEngine | |
with which the QDeclarativeCompiledData 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 QDeclarativeCompiler::compile(QDeclarativeEngine *engine, | |
QDeclarativeTypeData *unit, | |
QDeclarativeCompiledData *out) | |
{ | |
exceptions.clear(); | |
Q_ASSERT(out); | |
reset(out); | |
output = out; | |
// Compile types | |
const QList<QDeclarativeTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes(); | |
QList<QDeclarativeScriptParser::TypeReference *> referencedTypes = unit->parser().referencedTypes(); | |
for (int ii = 0; ii < resolvedTypes.count(); ++ii) { | |
QDeclarativeCompiledData::TypeReference ref; | |
const QDeclarativeTypeData::TypeReference &tref = resolvedTypes.at(ii); | |
QDeclarativeScriptParser::TypeReference *parserRef = referencedTypes.at(ii); | |
if (tref.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->refObjects.first(), err); | |
} | |
if (ref.type->containsRevisionedAttributes()) { | |
QDeclarativeError cacheError; | |
ref.typePropertyCache = | |
QDeclarativeEnginePrivate::get(engine)->cache(ref.type, resolvedTypes.at(ii).minorVersion, cacheError); | |
if (!ref.typePropertyCache) { | |
COMPILE_EXCEPTION(parserRef->refObjects.first(), cacheError.description()); | |
} | |
ref.typePropertyCache->addref(); | |
} | |
} else if (tref.typeData) { | |
ref.component = tref.typeData->compiledData(); | |
} | |
ref.className = parserRef->name.toUtf8(); | |
out->types << ref; | |
} | |
Object *root = unit->parser().tree(); | |
Q_ASSERT(root); | |
this->engine = engine; | |
this->enginePrivate = QDeclarativeEnginePrivate::get(engine); | |
this->unit = unit; | |
this->unitRoot = root; | |
compileTree(root); | |
if (!isError()) { | |
if (compilerDump()) | |
out->dumpInstructions(); | |
if (compilerStatDump()) | |
dumpStats(); | |
} else { | |
reset(out); | |
} | |
compileState = ComponentCompileState(); | |
savedCompileStates.clear(); | |
output = 0; | |
this->engine = 0; | |
this->enginePrivate = 0; | |
this->unit = 0; | |
this->unitRoot = 0; | |
return !isError(); | |
} | |
void QDeclarativeCompiler::compileTree(Object *tree) | |
{ | |
compileState.root = tree; | |
componentStat.lineNumber = tree->location.start.line; | |
if (!buildObject(tree, BindingContext()) || !completeComponentBuild()) | |
return; | |
QDeclarativeInstruction init; | |
init.type = QDeclarativeInstruction::Init; | |
init.line = 0; | |
init.init.bindingsSize = compileState.bindings.count(); | |
init.init.parserStatusSize = compileState.parserStatusCount; | |
init.init.contextCache = genContextCache(); | |
if (compileState.compiledBindingData.isEmpty()) | |
init.init.compiledBinding = -1; | |
else | |
init.init.compiledBinding = output->indexForByteArray(compileState.compiledBindingData); | |
output->bytecode << init; | |
// Build global import scripts | |
QHash<QString, Object::ScriptBlock> importedScripts; | |
QStringList importedScriptIndexes; | |
foreach (const QDeclarativeTypeData::ScriptReference &script, unit->resolvedScripts()) { | |
QString scriptCode = script.script->scriptSource(); | |
Object::ScriptBlock::Pragmas pragmas = script.script->pragmas(); | |
Q_ASSERT(!importedScripts.contains(script.qualifier)); | |
if (!scriptCode.isEmpty()) { | |
Object::ScriptBlock &scriptBlock = importedScripts[script.qualifier]; | |
scriptBlock.code = scriptCode; | |
scriptBlock.file = script.script->finalUrl().toString(); | |
scriptBlock.pragmas = pragmas; | |
} | |
} | |
for (QHash<QString, Object::ScriptBlock>::Iterator iter = importedScripts.begin(); | |
iter != importedScripts.end(); ++iter) { | |
importedScriptIndexes.append(iter.key()); | |
QDeclarativeInstruction import; | |
import.type = QDeclarativeInstruction::StoreImportedScript; | |
import.line = 0; | |
import.storeScript.value = output->scripts.count(); | |
output->scripts << *iter; | |
output->bytecode << import; | |
} | |
genObject(tree); | |
QDeclarativeInstruction def; | |
init.line = 0; | |
def.type = QDeclarativeInstruction::SetDefault; | |
output->bytecode << def; | |
output->importCache = new QDeclarativeTypeNameCache(engine); | |
for (int ii = 0; ii < importedScriptIndexes.count(); ++ii) | |
output->importCache->add(importedScriptIndexes.at(ii), ii); | |
unit->imports().populateCache(output->importCache, engine); | |
Q_ASSERT(tree->metatype); | |
if (tree->metadata.isEmpty()) { | |
output->root = tree->metatype; | |
} else { | |
static_cast<QMetaObject &>(output->rootData) = *tree->metaObject(); | |
output->root = &output->rootData; | |
} | |
if (!tree->metadata.isEmpty()) | |
enginePrivate->registerCompositeType(output); | |
} | |
static bool ValuePtrLessThan(const Value *t1, const Value *t2) | |
{ | |
return t1->location.start.line < t2->location.start.line || | |
(t1->location.start.line == t2->location.start.line && | |
t1->location.start.column < t2->location.start.column); | |
} | |
bool QDeclarativeCompiler::buildObject(Object *obj, const BindingContext &ctxt) | |
{ | |
componentStat.objects++; | |
Q_ASSERT (obj->type != -1); | |
const QDeclarativeCompiledData::TypeReference &tr = | |
output->types.at(obj->type); | |
obj->metatype = tr.metaObject(); | |
if (tr.component) | |
obj->url = tr.component->url; | |
if (tr.type) | |
obj->typeName = tr.type->qmlTypeName(); | |
obj->className = tr.className; | |
// This object is a "Component" element | |
if (tr.type && obj->metatype == &QDeclarativeComponent::staticMetaObject) { | |
COMPILE_CHECK(buildComponent(obj, ctxt)); | |
return true; | |
} | |
// 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, IgnoreAliases)); | |
// Find the native type and check for the QDeclarativeParserStatus interface | |
QDeclarativeType *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<QDeclarativeCustomParserProperty> 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 | |
foreach(Property *prop, obj->properties) { | |
if (prop->name == "id") { | |
COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); | |
break; | |
} | |
} | |
// Merge | |
Property *defaultProperty = 0; | |
Property *skipProperty = 0; | |
if (obj->defaultProperty) { | |
const QMetaObject *metaObject = obj->metaObject(); | |
Q_ASSERT(metaObject); | |
QMetaProperty p = QDeclarativeMetaType::defaultProperty(metaObject); | |
if (p.name()) { | |
Property *explicitProperty = obj->getProperty(p.name(), false); | |
if (explicitProperty && !explicitProperty->value) { | |
skipProperty = explicitProperty; | |
defaultProperty = new Property; | |
defaultProperty->parent = obj; | |
defaultProperty->isDefault = true; | |
defaultProperty->location = obj->defaultProperty->location; | |
defaultProperty->listValueRange = obj->defaultProperty->listValueRange; | |
defaultProperty->listCommaPositions = obj->defaultProperty->listCommaPositions; | |
defaultProperty->values = obj->defaultProperty->values; | |
defaultProperty->values += explicitProperty->values; | |
foreach(Value *value, defaultProperty->values) | |
value->addref(); | |
qSort(defaultProperty->values.begin(), defaultProperty->values.end(), ValuePtrLessThan); | |
} else { | |
defaultProperty = obj->defaultProperty; | |
defaultProperty->addref(); | |
} | |
} else { | |
defaultProperty = obj->defaultProperty; | |
defaultProperty->addref(); | |
} | |
} | |
QDeclarativeCustomParser *cp = 0; | |
if (isCustomParser) | |
cp = output->types.at(obj->type).type->customParser(); | |
// Build all explicit properties specified | |
foreach(Property *prop, obj->properties) { | |
if (prop == skipProperty) | |
continue; | |
if (prop->name == "id") | |
continue; | |
bool canDefer = false; | |
if (isCustomParser) { | |
if (doesPropertyExist(prop, obj) && | |
(!(cp->flags() & QDeclarativeCustomParser::AcceptsAttachedProperties) || | |
!isAttachedPropertyName(prop->name))) { | |
int ids = compileState.ids.count(); | |
COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); | |
canDefer = ids == compileState.ids.count(); | |
} else { | |
customProps << QDeclarativeCustomParserNodePrivate::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() && | |
deferredList.contains(QString::fromUtf8(prop->name))) | |
prop->isDeferred = true; | |
} | |
// Build the default property | |
if (defaultProperty) { | |
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 << QDeclarativeCustomParserNodePrivate::fromProperty(prop); | |
} | |
} else { | |
int ids = compileState.ids.count(); | |
COMPILE_CHECK(buildProperty(prop, obj, objCtxt)); | |
canDefer = ids == compileState.ids.count(); | |
} | |
if (canDefer && !deferredList.isEmpty() && | |
deferredList.contains(QString::fromUtf8(prop->name))) | |
prop->isDeferred = true; | |
} | |
if (defaultProperty) | |
defaultProperty->release(); | |
// 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 (QDeclarativeError err, cp->errors()) { | |
err.setUrl(output->url); | |
exceptions << err; | |
} | |
} | |
return true; | |
} | |
void QDeclarativeCompiler::genObject(QDeclarativeParser::Object *obj) | |
{ | |
QDeclarativeCompiledData::TypeReference &tr = output->types[obj->type]; | |
if (tr.type && obj->metatype == &QDeclarativeComponent::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) { | |
QDeclarativeInstruction create; | |
create.type = QDeclarativeInstruction::CreateSimpleObject; | |
create.line = obj->location.start.line; | |
create.createSimple.create = output->types.at(obj->type).type->createFunction(); | |
create.createSimple.typeSize = output->types.at(obj->type).type->createSize(); | |
create.createSimple.type = obj->type; | |
create.createSimple.column = obj->location.start.column; | |
output->bytecode << create; | |
} else { | |
QDeclarativeInstruction create; | |
create.type = QDeclarativeInstruction::CreateObject; | |
create.line = obj->location.start.line; | |
create.create.column = obj->location.start.column; | |
create.create.data = -1; | |
if (!obj->custom.isEmpty()) | |
create.create.data = output->indexForByteArray(obj->custom); | |
create.create.type = obj->type; | |
if (!output->types.at(create.create.type).type && | |
!obj->bindingBitmask.isEmpty()) { | |
Q_ASSERT(obj->bindingBitmask.size() % 4 == 0); | |
create.create.bindingBits = | |
output->indexForByteArray(obj->bindingBitmask); | |
} else { | |
create.create.bindingBits = -1; | |
} | |
output->bytecode << create; | |
} | |
// Setup the synthesized meta object if necessary | |
if (!obj->metadata.isEmpty()) { | |
QDeclarativeInstruction meta; | |
meta.type = QDeclarativeInstruction::StoreMetaObject; | |
meta.line = 0; | |
meta.storeMeta.data = output->indexForByteArray(obj->metadata); | |
meta.storeMeta.aliasData = output->indexForByteArray(obj->synthdata); | |
meta.storeMeta.propertyCache = output->propertyCaches.count(); | |
QDeclarativePropertyCache *propertyCache = obj->synthCache; | |
Q_ASSERT(propertyCache); | |
propertyCache->addref(); | |
// Add flag for alias properties | |
if (!obj->synthdata.isEmpty()) { | |
const QDeclarativeVMEMetaData *vmeMetaData = | |
reinterpret_cast<const QDeclarativeVMEMetaData *>(obj->synthdata.constData()); | |
for (int ii = 0; ii < vmeMetaData->aliasCount; ++ii) { | |
int index = obj->metaObject()->propertyOffset() + vmeMetaData->propertyCount + ii; | |
propertyCache->property(index)->flags |= QDeclarativePropertyCache::Data::IsAlias; | |
} | |
} | |
if (obj == unitRoot) { | |
propertyCache->addref(); | |
output->rootPropertyCache = propertyCache; | |
} | |
output->propertyCaches << propertyCache; | |
output->bytecode << meta; | |
} else if (obj == unitRoot) { | |
output->rootPropertyCache = tr.createPropertyCache(engine); | |
output->rootPropertyCache->addref(); | |
} | |
// Set the object id | |
if (!obj->id.isEmpty()) { | |
QDeclarativeInstruction id; | |
id.type = QDeclarativeInstruction::SetId; | |
id.line = 0; | |
id.setId.value = output->indexForString(obj->id); | |
id.setId.index = obj->idIndex; | |
output->bytecode << id; | |
} | |
// Begin the class | |
if (tr.type && obj->parserStatusCast != -1) { | |
QDeclarativeInstruction begin; | |
begin.type = QDeclarativeInstruction::BeginObject; | |
begin.begin.castValue = obj->parserStatusCast; | |
begin.line = obj->location.start.line; | |
output->bytecode << begin; | |
} | |
genObjectBody(obj); | |
} | |
void QDeclarativeCompiler::genObjectBody(QDeclarativeParser::Object *obj) | |
{ | |
typedef QPair<Property *, int> PropPair; | |
foreach(const PropPair &prop, obj->scriptStringProperties) { | |
QDeclarativeInstruction ss; | |
ss.type = QDeclarativeInstruction::StoreScriptString; | |
ss.storeScriptString.propertyIndex = prop.first->index; | |
ss.storeScriptString.value = | |
output->indexForString(prop.first->values.at(0)->value.asScript()); | |
ss.storeScriptString.scope = prop.second; | |
output->bytecode << ss; | |
} | |
bool seenDefer = false; | |
foreach(Property *prop, obj->valueProperties) { | |
if (prop->isDeferred) { | |
seenDefer = true; | |
continue; | |
} | |
if (!prop->isAlias) | |
genValueProperty(prop, obj); | |
} | |
if (seenDefer) { | |
QDeclarativeInstruction defer; | |
defer.type = QDeclarativeInstruction::Defer; | |
defer.line = 0; | |
defer.defer.deferCount = 0; | |
int deferIdx = output->bytecode.count(); | |
output->bytecode << defer; | |
QDeclarativeInstruction init; | |
init.type = QDeclarativeInstruction::Init; | |
init.init.bindingsSize = compileState.bindings.count(); // XXX - bigger than necessary | |
init.init.parserStatusSize = compileState.parserStatusCount; // XXX - bigger than necessary | |
init.init.contextCache = -1; | |
init.init.compiledBinding = -1; | |
output->bytecode << init; | |
foreach(Property *prop, obj->valueProperties) { | |
if (!prop->isDeferred) | |
continue; | |
genValueProperty(prop, obj); | |
} | |
output->bytecode[deferIdx].defer.deferCount = | |
output->bytecode.count() - deferIdx - 1; | |
} | |
foreach(Property *prop, obj->signalProperties) { | |
QDeclarativeParser::Value *v = prop->values.at(0); | |
if (v->type == Value::SignalObject) { | |
genObject(v->object); | |
QDeclarativeInstruction assign; | |
assign.type = QDeclarativeInstruction::AssignSignalObject; | |
assign.line = v->location.start.line; | |
assign.assignSignalObject.signal = | |
output->indexForByteArray(prop->name); | |
output->bytecode << assign; | |
} else if (v->type == Value::SignalExpression) { | |
BindingContext ctxt = compileState.signalExpressions.value(v); | |
QDeclarativeInstruction store; | |
store.type = QDeclarativeInstruction::StoreSignal; | |
store.line = v->location.start.line; | |
store.storeSignal.signalIndex = prop->index; | |
store.storeSignal.value = | |
output->indexForString(v->value.asScript().trimmed()); | |
store.storeSignal.context = ctxt.stack; | |
store.storeSignal.name = output->indexForByteArray(prop->name); | |
output->bytecode << store; | |
} | |
} | |
foreach(Property *prop, obj->attachedProperties) { | |
QDeclarativeInstruction fetch; | |
fetch.type = QDeclarativeInstruction::FetchAttached; | |
fetch.line = prop->location.start.line; | |
fetch.fetchAttached.id = prop->index; | |
output->bytecode << fetch; | |
genObjectBody(prop->value); | |
QDeclarativeInstruction pop; | |
pop.type = QDeclarativeInstruction::PopFetchedObject; | |
pop.line = prop->location.start.line; | |
output->bytecode << pop; | |
} | |
foreach(Property *prop, obj->groupedProperties) { | |
QDeclarativeInstruction fetch; | |
fetch.type = QDeclarativeInstruction::FetchObject; | |
fetch.fetch.property = prop->index; | |
fetch.line = prop->location.start.line; | |
output->bytecode << fetch; | |
if (!prop->value->metadata.isEmpty()) { | |
QDeclarativeInstruction meta; | |
meta.type = QDeclarativeInstruction::StoreMetaObject; | |
meta.line = 0; | |
meta.storeMeta.data = output->indexForByteArray(prop->value->metadata); | |
meta.storeMeta.aliasData = output->indexForByteArray(prop->value->synthdata); | |
meta.storeMeta.propertyCache = -1; | |
output->bytecode << meta; | |
} | |
genObjectBody(prop->value); | |
QDeclarativeInstruction pop; | |
pop.type = QDeclarativeInstruction::PopFetchedObject; | |
pop.line = prop->location.start.line; | |
output->bytecode << pop; | |
} | |
foreach(Property *prop, obj->valueTypeProperties) { | |
if (!prop->isAlias) | |
genValueTypeProperty(obj, prop); | |
} | |
foreach(Property *prop, obj->valueProperties) { | |
if (prop->isDeferred) | |
continue; | |
if (prop->isAlias) | |
genValueProperty(prop, obj); | |
} | |
foreach(Property *prop, obj->valueTypeProperties) { | |
if (prop->isAlias) | |
genValueTypeProperty(obj, prop); | |
} | |
} | |
void QDeclarativeCompiler::genValueTypeProperty(QDeclarativeParser::Object *obj,QDeclarativeParser::Property *prop) | |
{ | |
QDeclarativeInstruction fetch; | |
fetch.type = QDeclarativeInstruction::FetchValueType; | |
fetch.fetchValue.property = prop->index; | |
fetch.fetchValue.type = prop->type; | |
fetch.fetchValue.bindingSkipList = 0; | |
fetch.line = prop->location.start.line; | |
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. | |
foreach(Property *vprop, prop->value->valueProperties) { | |
if (!vprop->values.isEmpty()) { | |
Q_ASSERT(vprop->index >= 0 && vprop->index < 32); | |
fetch.fetchValue.bindingSkipList |= (1 << vprop->index); | |
} | |
} | |
} | |
output->bytecode << fetch; | |
foreach(Property *vprop, prop->value->valueProperties) { | |
genPropertyAssignment(vprop, prop->value, prop); | |
} | |
QDeclarativeInstruction pop; | |
pop.type = QDeclarativeInstruction::PopValueType; | |
pop.fetchValue.property = prop->index; | |
pop.fetchValue.type = prop->type; | |
pop.fetchValue.bindingSkipList = 0; | |
pop.line = prop->location.start.line; | |
output->bytecode << pop; | |
} | |
void QDeclarativeCompiler::genComponent(QDeclarativeParser::Object *obj) | |
{ | |
QDeclarativeParser::Object *root = obj->defaultProperty->values.at(0)->object; | |
Q_ASSERT(root); | |
QDeclarativeInstruction create; | |
create.type = QDeclarativeInstruction::CreateComponent; | |
create.line = root->location.start.line; | |
create.createComponent.column = root->location.start.column; | |
create.createComponent.endLine = root->location.end.line; | |
output->bytecode << create; | |
int count = output->bytecode.count(); | |
ComponentCompileState oldCompileState = compileState; | |
compileState = componentState(root); | |
QDeclarativeInstruction init; | |
init.type = QDeclarativeInstruction::Init; | |
init.init.bindingsSize = compileState.bindings.count(); | |
init.init.parserStatusSize = compileState.parserStatusCount; | |
init.init.contextCache = genContextCache(); | |
if (compileState.compiledBindingData.isEmpty()) | |
init.init.compiledBinding = -1; | |
else | |
init.init.compiledBinding = output->indexForByteArray(compileState.compiledBindingData); | |
init.line = obj->location.start.line; | |
output->bytecode << init; | |
genObject(root); | |
QDeclarativeInstruction def; | |
init.line = 0; | |
def.type = QDeclarativeInstruction::SetDefault; | |
output->bytecode << def; | |
output->bytecode[count - 1].createComponent.count = | |
output->bytecode.count() - count; | |
compileState = oldCompileState; | |
if (!obj->id.isEmpty()) { | |
QDeclarativeInstruction id; | |
id.type = QDeclarativeInstruction::SetId; | |
id.line = 0; | |
id.setId.value = output->indexForString(obj->id); | |
id.setId.index = obj->idIndex; | |
output->bytecode << id; | |
} | |
} | |
bool QDeclarativeCompiler::buildComponent(QDeclarativeParser::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 | |
// Find, check and set the "id" property (if any) | |
Property *idProp = 0; | |
if (obj->properties.count() > 1 || | |
(obj->properties.count() == 1 && obj->properties.begin().key() != "id")) | |
COMPILE_EXCEPTION(*obj->properties.begin(), tr("Component elements may not contain properties other than id")); | |
if (obj->properties.count()) | |
idProp = *obj->properties.begin(); | |
if (idProp) { | |
if (idProp->value || idProp->values.count() > 1 || idProp->values.at(0)->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.contains(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.count() > 1 || | |
(obj->defaultProperty->values.count() == 1 && !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.")); | |
Object *root = 0; | |
if (obj->defaultProperty && obj->defaultProperty->values.count()) | |
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)); | |
return true; | |
} | |
bool QDeclarativeCompiler::buildComponentFromRoot(QDeclarativeParser::Object *obj, | |
const BindingContext &ctxt) | |
{ | |
ComponentCompileState oldComponentCompileState = compileState; | |
ComponentStat oldComponentStat = componentStat; | |
compileState = ComponentCompileState(); | |
compileState.root = obj; | |
componentStat = ComponentStat(); | |
componentStat.lineNumber = obj->location.start.line; | |
if (obj) | |
COMPILE_CHECK(buildObject(obj, ctxt)); | |
COMPILE_CHECK(completeComponentBuild()); | |
compileState = oldComponentCompileState; | |
componentStat = oldComponentStat; | |
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 QDeclarativeCompiler::buildSubObject(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 | |
foreach(Property *prop, obj->properties) { | |
if (isSignalPropertyName(prop->name)) { | |
COMPILE_CHECK(buildSignal(prop, obj, ctxt)); | |
} else { | |
COMPILE_CHECK(buildProperty(prop, obj, ctxt)); | |
} | |
} | |
return true; | |
} | |
int QDeclarativeCompiler::componentTypeRef() | |
{ | |
QDeclarativeType *t = QDeclarativeMetaType::qmlType("QtQuick/Component",1,0); | |
for (int ii = output->types.count() - 1; ii >= 0; --ii) { | |
if (output->types.at(ii).type == t) | |
return ii; | |
} | |
QDeclarativeCompiledData::TypeReference ref; | |
ref.className = "Component"; | |
ref.type = t; | |
output->types << ref; | |
return output->types.count() - 1; | |
} | |
bool QDeclarativeCompiler::buildSignal(QDeclarativeParser::Property *prop, QDeclarativeParser::Object *obj, | |
const BindingContext &ctxt) | |
{ | |
Q_ASSERT(obj->metaObject()); | |
QByteArray name = prop->name; | |
Q_ASSERT(name.startsWith("on")); | |
name = name.mid(2); | |
if(name[0] >= 'A' && name[0] <= 'Z') | |
name[0] = name[0] - 'A' + 'a'; | |
bool notInRevision = false; | |
int sigIdx = indexOfSignal(obj, name, ¬InRevision); | |
if (sigIdx == -1) { | |
if (notInRevision && -1 == indexOfProperty(obj, prop->name, 0)) { | |
Q_ASSERT(obj->type != -1); | |
const QList<QDeclarativeTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes(); | |
const QDeclarativeTypeData::TypeReference &type = resolvedTypes.at(obj->type); | |
if (type.type) { | |
COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(QString::fromUtf8(obj->className)).arg(QString::fromUtf8(prop->name)).arg(QString::fromUtf8(type.type->module())).arg(type.majorVersion).arg(type.minorVersion)); | |
} else { | |
COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available due to component versioning.").arg(QString::fromUtf8(obj->className)).arg(QString::fromUtf8(prop->name))); | |
} | |
} | |
// 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.count() != 1) | |
COMPILE_EXCEPTION(prop, tr("Incorrectly specified signal assignment")); | |
prop->index = sigIdx; | |
obj->addSignalProperty(prop); | |
if (prop->values.at(0)->object) { | |
COMPILE_CHECK(buildObject(prop->values.at(0)->object, ctxt)); | |
prop->values.at(0)->type = Value::SignalObject; | |
} else { | |
prop->values.at(0)->type = Value::SignalExpression; | |
if (!prop->values.at(0)->value.isScript()) | |
COMPILE_EXCEPTION(prop, tr("Cannot assign a value to a signal (expecting a script to be run)")); | |
QString script = prop->values.at(0)->value.asScript().trimmed(); | |
if (script.isEmpty()) | |
COMPILE_EXCEPTION(prop, tr("Empty signal assignment")); | |
compileState.signalExpressions.insert(prop->values.at(0), ctxt); | |
} | |
} | |
return true; | |
} | |
/*! | |
Returns true if (value) property \a prop exists on obj, false otherwise. | |
*/ | |
bool QDeclarativeCompiler::doesPropertyExist(QDeclarativeParser::Property *prop, | |
QDeclarativeParser::Object *obj) | |
{ | |
if(isAttachedPropertyName(prop->name) || prop->name == "id") | |
return true; | |
const QMetaObject *mo = obj->metaObject(); | |
if (mo) { | |
if (prop->isDefault) { | |
QMetaProperty p = QDeclarativeMetaType::defaultProperty(mo); | |
return p.name() != 0; | |
} else { | |
int idx = indexOfProperty(obj, prop->name); | |
return idx != -1 && mo->property(idx).isScriptable(); | |
} | |
} | |
return false; | |
} | |
bool QDeclarativeCompiler::buildProperty(QDeclarativeParser::Property *prop, | |
QDeclarativeParser::Object *obj, | |
const BindingContext &ctxt) | |
{ | |
if (prop->isEmpty()) | |
COMPILE_EXCEPTION(prop, tr("Empty property assignment")); | |
const QMetaObject *metaObject = obj->metaObject(); | |
Q_ASSERT(metaObject); | |
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")); | |
} | |
QDeclarativeType *type = 0; | |
QDeclarativeImportedNamespace *typeNamespace = 0; | |
unit->imports().resolveType(prop->name, &type, 0, 0, 0, &typeNamespace); | |
if (typeNamespace) { | |
// ### We might need to indicate that this property is a namespace | |
// for the DOM API | |
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 = type->attachedPropertiesType(); | |
} else { | |
// Setup regular property data | |
QMetaProperty p; | |
if (prop->isDefault) { | |
p = QDeclarativeMetaType::defaultProperty(metaObject); | |
if (p.name()) { | |
prop->index = p.propertyIndex(); | |
prop->name = p.name(); | |
} | |
} else { | |
bool notInRevision = false; | |
prop->index = indexOfProperty(obj, prop->name, ¬InRevision); | |
if (prop->index == -1 && notInRevision) { | |
const QList<QDeclarativeTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes(); | |
const QDeclarativeTypeData::TypeReference &type = resolvedTypes.at(obj->type); | |
if (type.type) { | |
COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(QString::fromUtf8(obj->className)).arg(QString::fromUtf8(prop->name)).arg(QString::fromUtf8(type.type->module())).arg(type.majorVersion).arg(type.minorVersion)); | |
} else { | |
COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available due to component versioning.").arg(QString::fromUtf8(obj->className)).arg(QString::fromUtf8(prop->name))); | |
} | |
} | |
if (prop->index != -1) { | |
p = metaObject->property(prop->index); | |
Q_ASSERT(p.name()); | |
if (!p.isScriptable()) { | |
prop->index = -1; | |
p = QMetaProperty(); | |
} | |
} | |
} | |
// We can't error here as the "id" property does not require a | |
// successful index resolution | |
if (p.name()) | |
prop->type = p.userType(); | |
// Check if this is an alias | |
if (prop->index != -1 && | |
prop->parent && | |
prop->parent->type != -1 && | |
output->types.at(prop->parent->type).component) { | |
QDeclarativePropertyCache *cache = output->types.at(prop->parent->type).component->rootPropertyCache; | |
if (cache && cache->property(prop->index) && | |
cache->property(prop->index)->flags & QDeclarativePropertyCache::Data::IsAlias) | |
prop->isAlias = true; | |
} | |
if (prop->index != -1 && !prop->values.isEmpty()) | |
prop->parent->setBindingBit(prop->index); | |
} | |
if (!prop->isDefault && prop->name == "id" && !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.at(0)->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(QString::fromUtf8(prop->name))); | |
} | |
} else if (prop->value) { | |
COMPILE_CHECK(buildGroupedProperty(prop, obj, ctxt)); | |
} else if (enginePrivate->isList(prop->type)) { | |
COMPILE_CHECK(buildListProperty(prop, obj, ctxt)); | |
} else if (prop->type == qMetaTypeId<QDeclarativeScriptString>()) { | |
COMPILE_CHECK(buildScriptStringProperty(prop, obj, ctxt)); | |
} else { | |
COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt)); | |
} | |
return true; | |
} | |
bool QDeclarativeCompiler::buildPropertyInNamespace(QDeclarativeImportedNamespace *ns, | |
QDeclarativeParser::Property *nsProp, | |
QDeclarativeParser::Object *obj, | |
const BindingContext &ctxt) | |
{ | |
if (!nsProp->value) | |
COMPILE_EXCEPTION(nsProp, tr("Invalid use of namespace")); | |
foreach (Property *prop, nsProp->value->properties) { | |
if (!isAttachedPropertyName(prop->name)) | |
COMPILE_EXCEPTION(prop, tr("Not an attached property name")); | |
// Setup attached property data | |
QDeclarativeType *type = 0; | |
unit->imports().resolveType(ns, prop->name, &type, 0, 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 = type->attachedPropertiesType(); | |
COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt)); | |
} | |
return true; | |
} | |
void QDeclarativeCompiler::genValueProperty(QDeclarativeParser::Property *prop, | |
QDeclarativeParser::Object *obj) | |
{ | |
if (enginePrivate->isList(prop->type)) { | |
genListProperty(prop, obj); | |
} else { | |
genPropertyAssignment(prop, obj); | |
} | |
} | |
void QDeclarativeCompiler::genListProperty(QDeclarativeParser::Property *prop, | |
QDeclarativeParser::Object *obj) | |
{ | |
int listType = enginePrivate->listType(prop->type); | |
QDeclarativeInstruction fetch; | |
fetch.type = QDeclarativeInstruction::FetchQList; | |
fetch.line = prop->location.start.line; | |
fetch.fetchQmlList.property = prop->index; | |
bool listTypeIsInterface = QDeclarativeMetaType::isInterface(listType); | |
fetch.fetchQmlList.type = listType; | |
output->bytecode << fetch; | |
for (int ii = 0; ii < prop->values.count(); ++ii) { | |
Value *v = prop->values.at(ii); | |
if (v->type == Value::CreatedObject) { | |
genObject(v->object); | |
if (listTypeIsInterface) { | |
QDeclarativeInstruction assign; | |
assign.type = QDeclarativeInstruction::AssignObjectList; | |
assign.line = prop->location.start.line; | |
output->bytecode << assign; | |
} else { | |
QDeclarativeInstruction store; | |
store.type = QDeclarativeInstruction::StoreObjectQList; | |
store.line = prop->location.start.line; | |
output->bytecode << store; | |
} | |
} else if (v->type == Value::PropertyBinding) { | |
genBindingAssignment(v, prop, obj); | |
} | |
} | |
QDeclarativeInstruction pop; | |
pop.type = QDeclarativeInstruction::PopQList; | |
pop.line = prop->location.start.line; | |
output->bytecode << pop; | |
} | |
void QDeclarativeCompiler::genPropertyAssignment(QDeclarativeParser::Property *prop, | |
QDeclarativeParser::Object *obj, | |
QDeclarativeParser::Property *valueTypeProperty) | |
{ | |
for (int ii = 0; ii < prop->values.count(); ++ii) { | |
QDeclarativeParser::Value *v = prop->values.at(ii); | |
Q_ASSERT(v->type == Value::CreatedObject || | |
v->type == Value::PropertyBinding || | |
v->type == Value::Literal); | |
if (v->type == Value::CreatedObject) { | |
genObject(v->object); | |
if (QDeclarativeMetaType::isInterface(prop->type)) { | |
QDeclarativeInstruction store; | |
store.type = QDeclarativeInstruction::StoreInterface; | |
store.line = v->object->location.start.line; | |
store.storeObject.propertyIndex = prop->index; | |
output->bytecode << store; | |
} else if (prop->type == -1) { | |
QDeclarativeInstruction store; | |
store.type = QDeclarativeInstruction::StoreVariantObject; | |
store.line = v->object->location.start.line; | |
store.storeObject.propertyIndex = prop->index; | |
output->bytecode << store; | |
} else { | |
QDeclarativeInstruction store; | |
store.type = QDeclarativeInstruction::StoreObject; | |
store.line = v->object->location.start.line; | |
store.storeObject.propertyIndex = prop->index; | |
output->bytecode << store; | |
} | |
} else if (v->type == Value::PropertyBinding) { | |
genBindingAssignment(v, prop, obj, valueTypeProperty); | |
} else if (v->type == Value::Literal) { | |
QMetaProperty mp = obj->metaObject()->property(prop->index); | |
genLiteralAssignment(mp, v); | |
} | |
} | |
for (int ii = 0; ii < prop->onValues.count(); ++ii) { | |
QDeclarativeParser::Value *v = prop->onValues.at(ii); | |
Q_ASSERT(v->type == Value::ValueSource || | |
v->type == Value::ValueInterceptor); | |
if (v->type == Value::ValueSource) { | |
genObject(v->object); | |
QDeclarativeInstruction store; | |
store.type = QDeclarativeInstruction::StoreValueSource; | |
store.line = v->object->location.start.line; | |
if (valueTypeProperty) { | |
store.assignValueSource.property = genValueTypeData(prop, valueTypeProperty); | |
store.assignValueSource.owner = 1; | |
} else { | |
store.assignValueSource.property = genPropertyData(prop); | |
store.assignValueSource.owner = 0; | |
} | |
QDeclarativeType *valueType = toQmlType(v->object); | |
store.assignValueSource.castValue = valueType->propertyValueSourceCast(); | |
output->bytecode << store; | |
} else if (v->type == Value::ValueInterceptor) { | |
genObject(v->object); | |
QDeclarativeInstruction store; | |
store.type = QDeclarativeInstruction::StoreValueInterceptor; | |
store.line = v->object->location.start.line; | |
if (valueTypeProperty) { | |
store.assignValueInterceptor.property = genValueTypeData(prop, valueTypeProperty); | |
store.assignValueInterceptor.owner = 1; | |
} else { | |
store.assignValueInterceptor.property = genPropertyData(prop); | |
store.assignValueInterceptor.owner = 0; | |
} | |
QDeclarativeType *valueType = toQmlType(v->object); | |
store.assignValueInterceptor.castValue = valueType->propertyValueInterceptorCast(); | |
output->bytecode << store; | |
} | |
} | |
} | |
bool QDeclarativeCompiler::buildIdProperty(QDeclarativeParser::Property *prop, | |
QDeclarativeParser::Object *obj) | |
{ | |
if (prop->value || | |
prop->values.count() > 1 || | |
prop->values.at(0)->object) | |
COMPILE_EXCEPTION(prop, tr("Invalid use of id property")); | |
QDeclarativeParser::Value *idValue = prop->values.at(0); | |
QString val = idValue->primitive(); | |
COMPILE_CHECK(checkValidId(idValue, val)); | |
if (compileState.ids.contains(val)) | |
COMPILE_EXCEPTION(prop, tr("id is not unique")); | |
prop->values.at(0)->type = Value::Id; | |
obj->id = val; | |
addId(val, obj); | |
return true; | |
} | |
void QDeclarativeCompiler::addId(const QString &id, QDeclarativeParser::Object *obj) | |
{ | |
Q_ASSERT(!compileState.ids.contains(id)); | |
Q_ASSERT(obj->id == id); | |
obj->idIndex = compileState.ids.count(); | |
compileState.ids.insert(id, obj); | |
compileState.idIndexes.insert(obj->idIndex, obj); | |
} | |
void QDeclarativeCompiler::addBindingReference(const BindingReference &ref) | |
{ | |
Q_ASSERT(ref.value && !compileState.bindings.contains(ref.value)); | |
compileState.bindings.insert(ref.value, ref); | |
} | |
void QDeclarativeCompiler::saveComponentState() | |
{ | |
Q_ASSERT(compileState.root); | |
Q_ASSERT(!savedCompileStates.contains(compileState.root)); | |
savedCompileStates.insert(compileState.root, compileState); | |
savedComponentStats.append(componentStat); | |
} | |
QDeclarativeCompiler::ComponentCompileState | |
QDeclarativeCompiler::componentState(QDeclarativeParser::Object *obj) | |
{ | |
Q_ASSERT(savedCompileStates.contains(obj)); | |
return savedCompileStates.value(obj); | |
} | |
// Build attached property object. In this example, | |
// Text { | |
// GridView.row: 10 | |
// } | |
// GridView is an attached property object. | |
bool QDeclarativeCompiler::buildAttachedProperty(QDeclarativeParser::Property *prop, | |
QDeclarativeParser::Object *obj, | |
const BindingContext &ctxt) | |
{ | |
Q_ASSERT(prop->value); | |
Q_ASSERT(prop->index != -1); // This is set in buildProperty() | |
obj->addAttachedProperty(prop); | |
COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr())); | |
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 QDeclarativeCompiler::buildGroupedProperty(QDeclarativeParser::Property *prop, | |
QDeclarativeParser::Object *obj, | |
const BindingContext &ctxt) | |
{ | |
Q_ASSERT(prop->type != 0); | |
Q_ASSERT(prop->index != -1); | |
if (QDeclarativeValueTypeFactory::isValueType(prop->type)) { | |
if (prop->type >= 0 /* QVariant == -1 */ && enginePrivate->valueTypes[prop->type]) { | |
if (prop->values.count()) { | |
if (prop->values.at(0)->location < prop->value->location) { | |
COMPILE_EXCEPTION(prop->value, tr( "Property has already been assigned a value")); | |
} else { | |
COMPILE_EXCEPTION(prop->values.at(0), tr( "Property has already been assigned a value")); | |
} | |
} | |
if (!obj->metaObject()->property(prop->index).isWritable()) { | |
COMPILE_EXCEPTION(prop, tr( "Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop->name))); | |
} | |
if (prop->isAlias) { | |
foreach (Property *vtProp, prop->value->properties) | |
vtProp->isAlias = true; | |
} | |
COMPILE_CHECK(buildValueTypeProperty(enginePrivate->valueTypes[prop->type], | |
prop->value, obj, ctxt.incr())); | |
obj->addValueTypeProperty(prop); | |
} else { | |
COMPILE_EXCEPTION(prop, tr("Invalid grouped property access")); | |
} | |
} else { | |
// Load the nested property's meta type | |
prop->value->metatype = enginePrivate->metaObjectForType(prop->type); | |
if (!prop->value->metatype) | |
COMPILE_EXCEPTION(prop, tr("Invalid grouped property access")); | |
if (prop->values.count()) | |
COMPILE_EXCEPTION(prop->values.at(0), tr( "Cannot assign a value directly to a grouped property")); | |
obj->addGroupedProperty(prop); | |
COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr())); | |
} | |
return true; | |
} | |
bool QDeclarativeCompiler::buildValueTypeProperty(QObject *type, | |
QDeclarativeParser::Object *obj, | |
QDeclarativeParser::Object *baseObj, | |
const BindingContext &ctxt) | |
{ | |
if (obj->defaultProperty) | |
COMPILE_EXCEPTION(obj, tr("Invalid property use")); | |
obj->metatype = type->metaObject(); | |
foreach (Property *prop, obj->properties) { | |
int idx = type->metaObject()->indexOfProperty(prop->name.constData()); | |
if (idx == -1) | |
COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(QString::fromUtf8(prop->name))); | |
QMetaProperty p = type->metaObject()->property(idx); | |
if (!p.isScriptable()) | |
COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(QString::fromUtf8(prop->name))); | |
prop->index = idx; | |
prop->type = p.userType(); | |
prop->isValueTypeSubProperty = true; | |
if (prop->value) | |
COMPILE_EXCEPTION(prop, tr("Property assignment expected")); | |
if (prop->values.count() > 1) { | |
COMPILE_EXCEPTION(prop, tr("Single property assignment expected")); | |
} else if (prop->values.count()) { | |
Value *value = prop->values.at(0); | |
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; | |
COMPILE_CHECK(testQualifiedEnumAssignment(p, obj, value, &isEnumAssignment)); | |
if (isEnumAssignment) { | |
value->type = Value::Literal; | |
} else { | |
BindingReference reference; | |
reference.expression = value->value; | |
reference.property = prop; | |
reference.value = value; | |
reference.bindingContext = ctxt; | |
reference.bindingContext.owner++; | |
addBindingReference(reference); | |
value->type = Value::PropertyBinding; | |
} | |
} else { | |
COMPILE_CHECK(testLiteralAssignment(p, value)); | |
value->type = Value::Literal; | |
} | |
} | |
for (int ii = 0; ii < prop->onValues.count(); ++ii) { | |
Value *v = prop->onValues.at(ii); | |
Q_ASSERT(v->object); | |
COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, baseObj, v, ctxt)); | |
} | |
obj->addValueProperty(prop); | |
} | |
return true; | |
} | |
// Build assignments to QML lists. QML lists are properties of type | |
// QDeclarativeListProperty<T>. List properties can accept a list of | |
// objects, or a single binding. | |
bool QDeclarativeCompiler::buildListProperty(QDeclarativeParser::Property *prop, | |
QDeclarativeParser::Object *obj, | |
const BindingContext &ctxt) | |
{ | |
Q_ASSERT(enginePrivate->isList(prop->type)); | |
int t = prop->type; | |
obj->addValueProperty(prop); | |
int listType = enginePrivate->listType(t); | |
bool listTypeIsInterface = QDeclarativeMetaType::isInterface(listType); | |
bool assignedBinding = false; | |
for (int ii = 0; ii < prop->values.count(); ++ii) { | |
Value *v = prop->values.at(ii); | |
if (v->object) { | |
v->type = 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)); | |
v->type = Value::PropertyBinding; | |
} else { | |
COMPILE_EXCEPTION(v, tr("Cannot assign primitives to lists")); | |
} | |
} | |
return true; | |
} | |
// Compiles an assignment to a QDeclarativeScriptString property | |
bool QDeclarativeCompiler::buildScriptStringProperty(QDeclarativeParser::Property *prop, | |
QDeclarativeParser::Object *obj, | |
const BindingContext &ctxt) | |
{ | |
if (prop->values.count() > 1) | |
COMPILE_EXCEPTION(prop->values.at(1), tr( "Cannot assign multiple values to a script property")); | |
if (prop->values.at(0)->object) | |
COMPILE_EXCEPTION(prop->values.at(0), tr( "Invalid property assignment: script expected")); | |
obj->addScriptStringProperty(prop, ctxt.stack); | |
return true; | |
} | |
// Compile regular property assignments of the form "property: <value>" | |
bool QDeclarativeCompiler::buildPropertyAssignment(QDeclarativeParser::Property *prop, | |
QDeclarativeParser::Object *obj, | |
const BindingContext &ctxt) | |
{ | |
obj->addValueProperty(prop); | |
if (prop->values.count() > 1) | |
COMPILE_EXCEPTION(prop->values.at(0), tr( "Cannot assign multiple values to a singular property") ); | |
for (int ii = 0; ii < prop->values.count(); ++ii) { | |
Value *v = prop->values.at(ii); | |
if (v->object) { | |
COMPILE_CHECK(buildPropertyObjectAssignment(prop, obj, v, ctxt)); | |
} else { | |
COMPILE_CHECK(buildPropertyLiteralAssignment(prop, obj, v, ctxt)); | |
} | |
} | |
for (int ii = 0; ii < prop->onValues.count(); ++ii) { | |
Value *v = prop->onValues.at(ii); | |
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 QDeclarativeCompiler::buildPropertyObjectAssignment(QDeclarativeParser::Property *prop, | |
QDeclarativeParser::Object *obj, | |
QDeclarativeParser::Value *v, | |
const BindingContext &ctxt) | |
{ | |
Q_ASSERT(prop->index != -1); | |
Q_ASSERT(v->object->type != -1); | |
if (!obj->metaObject()->property(prop->index).isWritable()) | |
COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop->name))); | |
if (QDeclarativeMetaType::isInterface(prop->type)) { | |
// Assigning an object to an interface ptr property | |
COMPILE_CHECK(buildObject(v->object, ctxt)); | |
v->type = Value::CreatedObject; | |
} else if (prop->type == -1) { | |
// Assigning an object to a QVariant | |
COMPILE_CHECK(buildObject(v->object, ctxt)); | |
v->type = 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.at(v->object->type).metaObject(); | |
Q_ASSERT(v->object->metaObject()); | |
// 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 | |
const QMetaObject *propertyMetaObject = enginePrivate->rawMetaObjectForType(prop->type); | |
// Will be true if the assgned type inherits propertyMetaObject | |
bool isAssignable = false; | |
// Determine isAssignable value | |
if (propertyMetaObject) { | |
const QMetaObject *c = v->object->metatype; | |
while(c) { | |
isAssignable |= (QDeclarativePropertyPrivate::equal(c, propertyMetaObject)); | |
c = c->superClass(); | |
} | |
} | |
if (isAssignable) { | |
// Simple assignment | |
COMPILE_CHECK(buildObject(v->object, ctxt)); | |
v->type = Value::CreatedObject; | |
} else if (propertyMetaObject == &QDeclarativeComponent::staticMetaObject) { | |
// Automatic "Component" insertion | |
QDeclarativeParser::Object *root = v->object; | |
QDeclarativeParser::Object *component = new QDeclarativeParser::Object; | |
component->type = componentTypeRef(); | |
component->typeName = "Qt/Component"; | |
component->metatype = &QDeclarativeComponent::staticMetaObject; | |
component->location = root->location; | |
QDeclarativeParser::Value *componentValue = new QDeclarativeParser::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 QDeclarativeCompiler::buildPropertyOnAssignment(QDeclarativeParser::Property *prop, | |
QDeclarativeParser::Object *obj, | |
QDeclarativeParser::Object *baseObj, | |
QDeclarativeParser::Value *v, | |
const BindingContext &ctxt) | |
{ | |
Q_ASSERT(prop->index != -1); | |
Q_ASSERT(v->object->type != -1); | |
if (!obj->metaObject()->property(prop->index).isWritable()) | |
COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop->name))); | |
// 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.at(v->object->type).metaObject(); | |
Q_ASSERT(v->object->metaObject()); | |
// Will be true if the assigned type inherits QDeclarativePropertyValueSource | |
bool isPropertyValue = false; | |
// Will be true if the assigned type inherits QDeclarativePropertyValueInterceptor | |
bool isPropertyInterceptor = false; | |
if (QDeclarativeType *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 && prop->parent->synthdata.isEmpty()) | |
buildDynamicMeta(baseObj, ForceCreation); | |
v->type = isPropertyValue ? Value::ValueSource : Value::ValueInterceptor; | |
} else { | |
COMPILE_EXCEPTION(v, tr("\"%1\" cannot operate on \"%2\"").arg(QString::fromUtf8(v->object->typeName)).arg(QString::fromUtf8(prop->name.constData()))); | |
} | |
return true; | |
} | |
// Compile assigning a literal or binding to a regular property | |
bool QDeclarativeCompiler::buildPropertyLiteralAssignment(QDeclarativeParser::Property *prop, | |
QDeclarativeParser::Object *obj, | |
QDeclarativeParser::Value *v, | |
const BindingContext &ctxt) | |
{ | |
Q_ASSERT(prop->index != -1); | |
if (v->value.isScript()) { | |
//optimization for <Type>.<EnumValue> enum assignments | |
bool isEnumAssignment = false; | |
COMPILE_CHECK(testQualifiedEnumAssignment(obj->metaObject()->property(prop->index), obj, v, &isEnumAssignment)); | |
if (isEnumAssignment) { | |
v->type = Value::Literal; | |
return true; | |
} | |
COMPILE_CHECK(buildBinding(v, prop, ctxt)); | |
v->type = Value::PropertyBinding; | |
} else { | |
COMPILE_CHECK(testLiteralAssignment(obj->metaObject()->property(prop->index), v)); | |
v->type = Value::Literal; | |
} | |
return true; | |
} | |
bool QDeclarativeCompiler::testQualifiedEnumAssignment(const QMetaProperty &prop, | |
QDeclarativeParser::Object *obj, | |
QDeclarativeParser::Value *v, | |
bool *isAssignment) | |
{ | |
*isAssignment = false; | |
if (!prop.isEnumType()) | |
return true; | |
if (!prop.isWritable()) | |
COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop.name()))); | |
QString string = v->value.asString(); | |
if (!string.at(0).isUpper()) | |
return true; | |
QStringList parts = string.split(QLatin1Char('.')); | |
if (parts.count() != 2) | |
return true; | |
QString typeName = parts.at(0); | |
QDeclarativeType *type = 0; | |
unit->imports().resolveType(typeName.toUtf8(), &type, 0, 0, 0, 0); | |
//handle enums on value types (where obj->typeName is empty) | |
QByteArray objTypeName = obj->typeName; | |
if (objTypeName.isEmpty()) { | |
QDeclarativeType *objType = toQmlType(obj); | |
if (objType) | |
objTypeName = objType->qmlTypeName(); | |
} | |
if (!type || objTypeName != type->qmlTypeName()) | |
return true; | |
QString enumValue = parts.at(1); | |
int value; | |
if (prop.isFlagType()) { | |
value = prop.enumerator().keysToValue(enumValue.toUtf8().constData()); | |
} else | |
value = prop.enumerator().keyToValue(enumValue.toUtf8().constData()); | |
if (value == -1) | |
return true; | |
v->type = Value::Literal; | |
v->value = QDeclarativeParser::Variant(enumValue); | |
*isAssignment = true; | |
return true; | |
} | |
// Similar logic to above, but not knowing target property. | |
int QDeclarativeCompiler::evaluateEnum(const QByteArray& script) const | |
{ | |
int dot = script.indexOf('.'); | |
if (dot > 0) { | |
QDeclarativeType *type = 0; | |
unit->imports().resolveType(script.left(dot), &type, 0, 0, 0, 0); | |
if (!type) | |
return -1; | |
const QMetaObject *mo = type->metaObject(); | |
const char *key = script.constData() + dot+1; | |
int i = mo->enumeratorCount(); | |
while (i--) { | |
int v = mo->enumerator(i).keyToValue(key); | |
if (v >= 0) | |
return v; | |
} | |
} | |
return -1; | |
} | |
const QMetaObject *QDeclarativeCompiler::resolveType(const QByteArray& name) const | |
{ | |
QDeclarativeType *qmltype = 0; | |
if (!unit->imports().resolveType(name, &qmltype, 0, 0, 0, 0)) | |
return 0; | |
if (!qmltype) | |
return 0; | |
return qmltype->metaObject(); | |
} | |
// similar to logic of completeComponentBuild, but also sticks data | |
// into datas at the end | |
int QDeclarativeCompiler::rewriteBinding(const QString& expression, const QByteArray& name) | |
{ | |
QDeclarativeRewrite::RewriteBinding rewriteBinding; | |
rewriteBinding.setName('$' + name.mid(name.lastIndexOf('.') + 1)); | |
bool isSharable = false; | |
QString rewrite = rewriteBinding(expression, 0, &isSharable); | |
quint32 length = rewrite.length(); | |
quint32 pc; | |
if (isSharable) { | |
pc = output->cachedClosures.count(); | |
pc |= 0x80000000; | |
output->cachedClosures.append(0); | |
} else { | |
pc = output->cachedPrograms.length(); | |
output->cachedPrograms.append(0); | |
} | |
QByteArray compiledData = | |
QByteArray((const char *)&pc, sizeof(quint32)) + | |
QByteArray((const char *)&length, sizeof(quint32)) + | |
QByteArray((const char *)rewrite.constData(), | |
rewrite.length() * sizeof(QChar)); | |
return output->indexForByteArray(compiledData); | |
} | |
// Ensures that the dynamic meta specification on obj is valid | |
bool QDeclarativeCompiler::checkDynamicMeta(QDeclarativeParser::Object *obj) | |
{ | |
QSet<QByteArray> propNames; | |
QSet<QByteArray> methodNames; | |
bool seenDefaultProperty = false; | |
// Check properties | |
for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { | |
const QDeclarativeParser::Object::DynamicProperty &prop = | |
obj->dynamicProperties.at(ii); | |
if (prop.isDefaultProperty) { | |
if (seenDefaultProperty) | |
COMPILE_EXCEPTION(&prop, tr("Duplicate default property")); | |
seenDefaultProperty = true; | |
} | |
if (propNames.contains(prop.name)) | |
COMPILE_EXCEPTION(&prop, tr("Duplicate property name")); | |
QString propName = QString::fromUtf8(prop.name); | |
if (propName.at(0).isUpper()) | |
COMPILE_EXCEPTION(&prop, tr("Property names cannot begin with an upper case letter")); | |
if (enginePrivate->globalClass->illegalNames().contains(propName)) | |
COMPILE_EXCEPTION(&prop, tr("Illegal property name")); | |
propNames.insert(prop.name); | |
} | |
for (int ii = 0; ii < obj->dynamicSignals.count(); ++ii) { | |
QByteArray name = obj->dynamicSignals.at(ii).name; | |
if (methodNames.contains(name)) | |
COMPILE_EXCEPTION(obj, tr("Duplicate signal name")); | |
QString nameStr = QString::fromUtf8(name); | |
if (nameStr.at(0).isUpper()) | |
COMPILE_EXCEPTION(obj, tr("Signal names cannot begin with an upper case letter")); | |
if (enginePrivate->globalClass->illegalNames().contains(nameStr)) | |
COMPILE_EXCEPTION(obj, tr("Illegal signal name")); | |
methodNames.insert(name); | |
} | |
for (int ii = 0; ii < obj->dynamicSlots.count(); ++ii) { | |
QByteArray name = obj->dynamicSlots.at(ii).name; | |
if (methodNames.contains(name)) | |
COMPILE_EXCEPTION(obj, tr("Duplicate method name")); | |
QString nameStr = QString::fromUtf8(name); | |
if (nameStr.at(0).isUpper()) | |
COMPILE_EXCEPTION(obj, tr("Method names cannot begin with an upper case letter")); | |
if (enginePrivate->globalClass->illegalNames().contains(nameStr)) | |
COMPILE_EXCEPTION(obj, tr("Illegal method name")); | |
methodNames.insert(name); | |
} | |
return true; | |
} | |
bool QDeclarativeCompiler::mergeDynamicMetaProperties(QDeclarativeParser::Object *obj) | |
{ | |
for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { | |
const Object::DynamicProperty &p = obj->dynamicProperties.at(ii); | |
if (!p.defaultValue || p.type == Object::DynamicProperty::Alias) | |
continue; | |
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 (property->value) | |
COMPILE_EXCEPTION(property, tr("Invalid property nesting")); | |
for (int ii = 0; ii < p.defaultValue->values.count(); ++ii) { | |
Value *v = p.defaultValue->values.at(ii); | |
v->addref(); | |
property->values.append(v); | |
} | |
} | |
return true; | |
} | |
Q_GLOBAL_STATIC(QAtomicInt, classIndexCounter) | |
bool QDeclarativeCompiler::buildDynamicMeta(QDeclarativeParser::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; | |
QByteArray dynamicData(sizeof(QDeclarativeVMEMetaData), (char)0); | |
QByteArray newClassName = obj->metatype->className(); | |
newClassName.append("_QML_"); | |
int idx = classIndexCounter()->fetchAndAddRelaxed(1); | |
newClassName.append(QByteArray::number(idx)); | |
if (compileState.root == obj) { | |
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(idx); | |
} | |
} | |
QMetaObjectBuilder builder; | |
builder.setClassName(newClassName); | |
builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); | |
bool hasAlias = false; | |
for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { | |
const Object::DynamicProperty &p = obj->dynamicProperties.at(ii); | |
int propIdx = obj->metaObject()->indexOfProperty(p.name.constData()); | |
if (-1 != propIdx) { | |
QMetaProperty prop = obj->metaObject()->property(propIdx); | |
if (prop.isFinal()) | |
COMPILE_EXCEPTION(&p, tr("Cannot override FINAL property")); | |
} | |
if (p.isDefaultProperty && | |
(p.type != Object::DynamicProperty::Alias || | |
mode == ResolveAliases)) | |
builder.addClassInfo("DefaultProperty", p.name); | |
QByteArray type; | |
int propertyType = 0; | |
bool readonly = false; | |
switch(p.type) { | |
case Object::DynamicProperty::Alias: | |
hasAlias = true; | |
continue; | |
break; | |
case Object::DynamicProperty::CustomList: | |
case Object::DynamicProperty::Custom: | |
{ | |
QByteArray customTypeName; | |
QDeclarativeType *qmltype = 0; | |
QUrl url; | |
if (!unit->imports().resolveType(p.customType, &qmltype, &url, 0, 0, 0)) | |
COMPILE_EXCEPTION(&p, tr("Invalid property type")); | |
if (!qmltype) { | |
QDeclarativeTypeData *tdata = enginePrivate->typeLoader.get(url); | |
Q_ASSERT(tdata); | |
Q_ASSERT(tdata->isComplete()); | |
QDeclarativeCompiledData *data = tdata->compiledData(); | |
customTypeName = data->root->className(); | |
data->release(); | |
tdata->release(); | |
} else { | |
customTypeName = qmltype->typeName(); | |
} | |
if (p.type == Object::DynamicProperty::Custom) { | |
type = customTypeName + '*'; | |
propertyType = QMetaType::QObjectStar; | |
} else { | |
readonly = true; | |
type = "QDeclarativeListProperty<"; | |
type.append(customTypeName); | |
type.append(">"); | |
propertyType = qMetaTypeId<QDeclarativeListProperty<QObject> >(); | |
} | |
} | |
break; | |
case Object::DynamicProperty::Variant: | |
propertyType = -1; | |
type = "QVariant"; | |
break; | |
case Object::DynamicProperty::Int: | |
propertyType = QVariant::Int; | |
type = "int"; | |
break; | |
case Object::DynamicProperty::Bool: | |
propertyType = QVariant::Bool; | |
type = "bool"; | |
break; | |
case Object::DynamicProperty::Real: | |
propertyType = QVariant::Double; | |
type = "double"; | |
break; | |
case Object::DynamicProperty::String: | |
propertyType = QVariant::String; | |
type = "QString"; | |
break; | |
case Object::DynamicProperty::Url: | |
propertyType = QVariant::Url; | |
type = "QUrl"; | |
break; | |
case Object::DynamicProperty::Color: | |
propertyType = QVariant::Color; | |
type = "QColor"; | |
break; | |
case Object::DynamicProperty::Time: | |
propertyType = QVariant::Time; | |
type = "QTime"; | |
break; | |
case Object::DynamicProperty::Date: | |
propertyType = QVariant::Date; | |
type = "QDate"; | |
break; | |
case Object::DynamicProperty::DateTime: | |
propertyType = QVariant::DateTime; | |
type = "QDateTime"; | |
break; | |
} | |
((QDeclarativeVMEMetaData *)dynamicData.data())->propertyCount++; | |
QDeclarativeVMEMetaData::PropertyData propertyData = { propertyType }; | |
dynamicData.append((char *)&propertyData, sizeof(propertyData)); | |
builder.addSignal(p.name + "Changed()"); | |
QMetaPropertyBuilder propBuilder = | |
builder.addProperty(p.name, type, builder.methodCount() - 1); | |
propBuilder.setWritable(!readonly); | |
} | |
for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { | |
const Object::DynamicProperty &p = obj->dynamicProperties.at(ii); | |
if (p.type == Object::DynamicProperty::Alias) { | |
if (mode == ResolveAliases) { | |
((QDeclarativeVMEMetaData *)dynamicData.data())->aliasCount++; | |
COMPILE_CHECK(compileAlias(builder, dynamicData, obj, p)); | |
} else { | |
// Need a fake signal so that the metaobject remains consistent across | |
// the resolve and non-resolve alias runs | |
builder.addSignal(p.name + "Changed()"); | |
} | |
} | |
} | |
for (int ii = 0; ii < obj->dynamicSignals.count(); ++ii) { | |
const Object::DynamicSignal &s = obj->dynamicSignals.at(ii); | |
QByteArray sig(s.name + '('); | |
for (int jj = 0; jj < s.parameterTypes.count(); ++jj) { | |
if (jj) sig.append(','); | |
sig.append(s.parameterTypes.at(jj)); | |
} | |
sig.append(')'); | |
QMetaMethodBuilder b = builder.addSignal(sig); | |
b.setParameterNames(s.parameterNames); | |
((QDeclarativeVMEMetaData *)dynamicData.data())->signalCount++; | |
} | |
QStringList funcScripts; | |
for (int ii = 0; ii < obj->dynamicSlots.count(); ++ii) { | |
Object::DynamicSlot &s = obj->dynamicSlots[ii]; | |
QByteArray sig(s.name + '('); | |
QString funcScript(QLatin1String("(function ") + s.name + QLatin1Char('(')); | |
for (int jj = 0; jj < s.parameterNames.count(); ++jj) { | |
if (jj) { | |
sig.append(','); | |
funcScript.append(QLatin1Char(',')); | |
} | |
funcScript.append(QLatin1String(s.parameterNames.at(jj))); | |
sig.append("QVariant"); | |
} | |
sig.append(')'); | |
funcScript.append(QLatin1Char(')')); | |
funcScript.append(s.body); | |
funcScript.append(QLatin1Char(')')); | |
funcScripts << funcScript; | |
QMetaMethodBuilder b = builder.addSlot(sig); | |
b.setReturnType("QVariant"); | |
b.setParameterNames(s.parameterNames); | |
((QDeclarativeVMEMetaData *)dynamicData.data())->methodCount++; | |
QDeclarativeVMEMetaData::MethodData methodData = | |
{ s.parameterNames.count(), 0, funcScript.length(), s.location.start.line }; | |
dynamicData.append((char *)&methodData, sizeof(methodData)); | |
} | |
for (int ii = 0; ii < obj->dynamicSlots.count(); ++ii) { | |
const QString &funcScript = funcScripts.at(ii); | |
QDeclarativeVMEMetaData::MethodData *data = | |
((QDeclarativeVMEMetaData *)dynamicData.data())->methodData() + ii; | |
data->bodyOffset = dynamicData.size(); | |
dynamicData.append((const char *)funcScript.constData(), | |
(funcScript.length() * sizeof(QChar))); | |
} | |
obj->metadata = builder.toRelocatableData(); | |
builder.fromRelocatableData(&obj->extObject, obj->metatype, obj->metadata); | |
if (mode == IgnoreAliases && hasAlias) | |
compileState.aliasingObjects << obj; | |
obj->synthdata = dynamicData; | |
if (obj->synthCache) { | |
obj->synthCache->release(); | |
obj->synthCache = 0; | |
} | |
if (obj->type != -1) { | |
QDeclarativePropertyCache *cache = output->types[obj->type].createPropertyCache(engine)->copy(); | |
cache->append(engine, &obj->extObject, QDeclarativePropertyCache::Data::NoFlags, | |
QDeclarativePropertyCache::Data::IsVMEFunction, | |
QDeclarativePropertyCache::Data::IsVMESignal); | |
obj->synthCache = cache; | |
} | |
return true; | |
} | |
bool QDeclarativeCompiler::checkValidId(QDeclarativeParser::Value *v, const QString &val) | |
{ | |
if (val.isEmpty()) | |
COMPILE_EXCEPTION(v, tr( "Invalid empty ID")); | |
if (val.at(0).isLetter() && !val.at(0).isLower()) | |
COMPILE_EXCEPTION(v, tr( "IDs cannot start with an uppercase letter")); | |
QChar u(QLatin1Char('_')); | |
for (int ii = 0; ii < val.count(); ++ii) { | |
if (ii == 0 && !val.at(ii).isLetter() && val.at(ii) != u) { | |
COMPILE_EXCEPTION(v, tr( "IDs must start with a letter or underscore")); | |
} else if (ii != 0 && !val.at(ii).isLetterOrNumber() && val.at(ii) != u) { | |
COMPILE_EXCEPTION(v, tr( "IDs must contain only letters, numbers, and underscores")); | |
} | |
} | |
if (enginePrivate->globalClass->illegalNames().contains(val)) | |
COMPILE_EXCEPTION(v, tr( "ID illegally masks global JavaScript property")); | |
return true; | |
} | |
#include <qdeclarativejsparser_p.h> | |
static QStringList astNodeToStringList(QDeclarativeJS::AST::Node *node) | |
{ | |
if (node->kind == QDeclarativeJS::AST::Node::Kind_IdentifierExpression) { | |
QString name = | |
static_cast<QDeclarativeJS::AST::IdentifierExpression *>(node)->name->asString(); | |
return QStringList() << name; | |
} else if (node->kind == QDeclarativeJS::AST::Node::Kind_FieldMemberExpression) { | |
QDeclarativeJS::AST::FieldMemberExpression *expr = static_cast<QDeclarativeJS::AST::FieldMemberExpression *>(node); | |
QStringList rv = astNodeToStringList(expr->base); | |
if (rv.isEmpty()) | |
return rv; | |
rv.append(expr->name->asString()); | |
return rv; | |
} | |
return QStringList(); | |
} | |
bool QDeclarativeCompiler::compileAlias(QMetaObjectBuilder &builder, | |
QByteArray &data, | |
Object *obj, | |
const Object::DynamicProperty &prop) | |
{ | |
if (!prop.defaultValue) | |
COMPILE_EXCEPTION(obj, tr("No property alias location")); | |
if (prop.defaultValue->values.count() != 1 || | |
prop.defaultValue->values.at(0)->object || | |
!prop.defaultValue->values.at(0)->value.isScript()) | |
COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); | |
QDeclarativeJS::AST::Node *node = prop.defaultValue->values.at(0)->value.asAST(); | |
if (!node) | |
COMPILE_EXCEPTION(obj, tr("No property alias location")); // ### Can this happen? | |
QStringList alias = astNodeToStringList(node); | |
if (alias.count() < 1 || alias.count() > 3) | |
COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>")); | |
if (!compileState.ids.contains(alias.at(0))) | |
COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias reference. Unable to find id \"%1\"").arg(alias.at(0))); | |
Object *idObject = compileState.ids[alias.at(0)]; | |
QByteArray typeName; | |
int propIdx = -1; | |
int flags = 0; | |
bool writable = false; | |
if (alias.count() == 2 || alias.count() == 3) { | |
propIdx = indexOfProperty(idObject, alias.at(1).toUtf8()); | |
if (-1 == propIdx) { | |
COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); | |
} else if (propIdx > 0xFFFF) { | |
COMPILE_EXCEPTION(prop.defaultValue, tr("Alias property exceeds alias bounds")); | |
} | |
QMetaProperty aliasProperty = idObject->metaObject()->property(propIdx); | |
if (!aliasProperty.isScriptable()) | |
COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); | |
writable = aliasProperty.isWritable(); | |
if (alias.count() == 3) { | |
QDeclarativeValueType *valueType = enginePrivate->valueTypes[aliasProperty.type()]; | |
if (!valueType) | |
COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); | |
propIdx |= ((unsigned int)aliasProperty.type()) << 24; | |
int valueTypeIndex = valueType->metaObject()->indexOfProperty(alias.at(2).toUtf8().constData()); | |
if (valueTypeIndex == -1) | |
COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); | |
Q_ASSERT(valueTypeIndex <= 0xFF); | |
aliasProperty = valueType->metaObject()->property(valueTypeIndex); | |
propIdx |= (valueTypeIndex << 16); | |
} | |
if (aliasProperty.isEnumType()) | |
typeName = "int"; // Avoid introducing a dependency on the aliased metaobject | |
else | |
typeName = aliasProperty.typeName(); | |
} else { | |
typeName = idObject->metaObject()->className(); | |
//use the base type since it has been registered with metatype system | |
int index = typeName.indexOf("_QML_"); | |
if (index != -1) { | |
typeName = typeName.left(index); | |
} else { | |
index = typeName.indexOf("_QMLTYPE_"); | |
const QMetaObject *mo = idObject->metaObject(); | |
while (index != -1 && mo) { | |
typeName = mo->superClass()->className(); | |
index = typeName.indexOf("_QMLTYPE_"); | |
mo = mo->superClass(); | |
} | |
} | |
typeName += '*'; | |
} | |
if (typeName.endsWith('*')) | |
flags |= QML_ALIAS_FLAG_PTR; | |
data.append((const char *)&idObject->idIndex, sizeof(idObject->idIndex)); | |
data.append((const char *)&propIdx, sizeof(propIdx)); | |
data.append((const char *)&flags, sizeof(flags)); | |
builder.addSignal(prop.name + "Changed()"); | |
QMetaPropertyBuilder propBuilder = | |
builder.addProperty(prop.name, typeName.constData(), builder.methodCount() - 1); | |
propBuilder.setWritable(writable); | |
return true; | |
} | |
bool QDeclarativeCompiler::buildBinding(QDeclarativeParser::Value *value, | |
QDeclarativeParser::Property *prop, | |
const BindingContext &ctxt) | |
{ | |
Q_ASSERT(prop->index != -1); | |
Q_ASSERT(prop->parent); | |
Q_ASSERT(prop->parent->metaObject()); | |
QMetaProperty mp = prop->parent->metaObject()->property(prop->index); | |
if (!mp.isWritable() && !QDeclarativeMetaType::isList(prop->type)) | |
COMPILE_EXCEPTION(prop, tr("Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop->name))); | |
BindingReference reference; | |
reference.expression = value->value; | |
reference.property = prop; | |
reference.value = value; | |
reference.bindingContext = ctxt; | |
addBindingReference(reference); | |
return true; | |
} | |
void QDeclarativeCompiler::genBindingAssignment(QDeclarativeParser::Value *binding, | |
QDeclarativeParser::Property *prop, | |
QDeclarativeParser::Object *obj, | |
QDeclarativeParser::Property *valueTypeProperty) | |
{ | |
Q_UNUSED(obj); | |
Q_ASSERT(compileState.bindings.contains(binding)); | |
const BindingReference &ref = compileState.bindings.value(binding); | |
if (ref.dataType == BindingReference::Experimental) { | |
QDeclarativeInstruction store; | |
store.type = QDeclarativeInstruction::StoreCompiledBinding; | |
store.assignBinding.value = ref.compiledIndex; | |
store.assignBinding.context = ref.bindingContext.stack; | |
store.assignBinding.owner = ref.bindingContext.owner; | |
if (valueTypeProperty) | |
store.assignBinding.property = (valueTypeProperty->index & 0xFFFF) | | |
((valueTypeProperty->type & 0xFF)) << 16 | | |
((prop->index & 0xFF) << 24); | |
else | |
store.assignBinding.property = prop->index; | |
store.line = binding->location.start.line; | |
output->bytecode << store; | |
return; | |
} | |
QDeclarativeInstruction store; | |
if (!prop->isAlias) | |
store.type = QDeclarativeInstruction::StoreBinding; | |
else | |
store.type = QDeclarativeInstruction::StoreBindingOnAlias; | |
store.assignBinding.value = output->indexForByteArray(ref.compiledData); | |
store.assignBinding.context = ref.bindingContext.stack; | |
store.assignBinding.owner = ref.bindingContext.owner; | |
store.line = binding->location.start.line; | |
Q_ASSERT(ref.bindingContext.owner == 0 || | |
(ref.bindingContext.owner != 0 && valueTypeProperty)); | |
if (ref.bindingContext.owner) { | |
store.assignBinding.property = genValueTypeData(prop, valueTypeProperty); | |
} else { | |
store.assignBinding.property = genPropertyData(prop); | |
} | |
output->bytecode << store; | |
} | |
int QDeclarativeCompiler::genContextCache() | |
{ | |
if (compileState.ids.count() == 0) | |
return -1; | |
QDeclarativeIntegerCache *cache = new QDeclarativeIntegerCache(engine); | |
for (QHash<QString, QDeclarativeParser::Object *>::ConstIterator iter = compileState.ids.begin(); | |
iter != compileState.ids.end(); | |
++iter) | |
cache->add(iter.key(), (*iter)->idIndex); | |
output->contextCaches.append(cache); | |
return output->contextCaches.count() - 1; | |
} | |
int QDeclarativeCompiler::genValueTypeData(QDeclarativeParser::Property *valueTypeProp, | |
QDeclarativeParser::Property *prop) | |
{ | |
QByteArray data = | |
QDeclarativePropertyPrivate::saveValueType(prop->parent->metaObject(), prop->index, | |
enginePrivate->valueTypes[prop->type]->metaObject(), | |
valueTypeProp->index); | |
// valueTypeProp->index, valueTypeProp->type); | |
return output->indexForByteArray(data); | |
} | |
int QDeclarativeCompiler::genPropertyData(QDeclarativeParser::Property *prop) | |
{ | |
return output->indexForByteArray(QDeclarativePropertyPrivate::saveProperty(prop->parent->metaObject(), prop->index)); | |
} | |
bool QDeclarativeCompiler::completeComponentBuild() | |
{ | |
componentStat.ids = compileState.ids.count(); | |
for (int ii = 0; ii < compileState.aliasingObjects.count(); ++ii) { | |
Object *aliasObject = compileState.aliasingObjects.at(ii); | |
COMPILE_CHECK(buildDynamicMeta(aliasObject, ResolveAliases)); | |
} | |
QDeclarativeBindingCompiler::Expression expr; | |
expr.component = compileState.root; | |
expr.ids = compileState.ids; | |
QDeclarativeBindingCompiler bindingCompiler; | |
for (QHash<QDeclarativeParser::Value*,BindingReference>::Iterator iter = compileState.bindings.begin(); | |
iter != compileState.bindings.end(); ++iter) { | |
BindingReference &binding = *iter; | |
expr.context = binding.bindingContext.object; | |
expr.property = binding.property; | |
expr.expression = binding.expression; | |
expr.imports = unit->imports(); | |
// ### We don't currently optimize for bindings on alias's - because | |
// of the solution to QTBUG-13719 | |
if (!binding.property->isAlias) { | |
int index = bindingCompiler.compile(expr, enginePrivate); | |
if (index != -1) { | |
binding.dataType = BindingReference::Experimental; | |
binding.compiledIndex = index; | |
componentStat.optimizedBindings.append(iter.key()->location); | |
continue; | |
} | |
} | |
binding.dataType = BindingReference::QtScript; | |
// Pre-rewrite the expression | |
QString expression = binding.expression.asScript(); | |
QDeclarativeRewrite::RewriteBinding rewriteBinding; | |
rewriteBinding.setName('$'+binding.property->name); | |
bool isSharable = false; | |
expression = rewriteBinding(binding.expression.asAST(), expression, &isSharable); | |
quint32 length = expression.length(); | |
quint32 pc; | |
if (isSharable) { | |
pc = output->cachedClosures.count(); | |
pc |= 0x80000000; | |
output->cachedClosures.append(0); | |
} else { | |
pc = output->cachedPrograms.length(); | |
output->cachedPrograms.append(0); | |
} | |
binding.compiledData = | |
QByteArray((const char *)&pc, sizeof(quint32)) + | |
QByteArray((const char *)&length, sizeof(quint32)) + | |
QByteArray((const char *)expression.constData(), | |
expression.length() * sizeof(QChar)); | |
componentStat.scriptBindings.append(iter.key()->location); | |
} | |
if (bindingCompiler.isValid()) { | |
compileState.compiledBindingData = bindingCompiler.program(); | |
if (bindingsDump()) | |
QDeclarativeBindingCompiler::dump(compileState.compiledBindingData); | |
} | |
saveComponentState(); | |
return true; | |
} | |
void QDeclarativeCompiler::dumpStats() | |
{ | |
qWarning().nospace() << "QML Document: " << output->url.toString(); | |
for (int ii = 0; ii < savedComponentStats.count(); ++ii) { | |
const ComponentStat &stat = savedComponentStats.at(ii); | |
qWarning().nospace() << " Component Line " << stat.lineNumber; | |
qWarning().nospace() << " Total Objects: " << stat.objects; | |
qWarning().nospace() << " IDs Used: " << stat.ids; | |
qWarning().nospace() << " Optimized Bindings: " << stat.optimizedBindings.count(); | |
{ | |
QByteArray output; | |
for (int ii = 0; ii < stat.optimizedBindings.count(); ++ii) { | |
if (0 == (ii % 10)) { | |
if (ii) output.append("\n"); | |
output.append(" "); | |
} | |
output.append("("); | |
output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.line)); | |
output.append(":"); | |
output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.column)); | |
output.append(") "); | |
} | |
if (!output.isEmpty()) | |
qWarning().nospace() << output.constData(); | |
} | |
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 QDeclarativeCompiler::canCoerce(int to, QDeclarativeParser::Object *from) | |
{ | |
const QMetaObject *toMo = | |
enginePrivate->rawMetaObjectForType(to); | |
const QMetaObject *fromMo = from->metaObject(); | |
while (fromMo) { | |
if (QDeclarativePropertyPrivate::equal(fromMo, toMo)) | |
return true; | |
fromMo = fromMo->superClass(); | |
} | |
return false; | |
} | |
QDeclarativeType *QDeclarativeCompiler::toQmlType(QDeclarativeParser::Object *from) | |
{ | |
// ### Optimize | |
const QMetaObject *mo = from->metatype; | |
QDeclarativeType *type = 0; | |
while (!type && mo) { | |
type = QDeclarativeMetaType::qmlType(mo); | |
mo = mo->superClass(); | |
} | |
return type; | |
} | |
QStringList QDeclarativeCompiler::deferredProperties(QDeclarativeParser::Object *obj) | |
{ | |
const QMetaObject *mo = obj->metatype; | |
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; | |
} | |
// This code must match the semantics of QDeclarativePropertyPrivate::findSignalByName | |
int QDeclarativeCompiler::indexOfSignal(QDeclarativeParser::Object *object, const QByteArray &name, | |
bool *notInRevision) | |
{ | |
if (notInRevision) *notInRevision = false; | |
if (object->synthCache || (object->type != -1 && output->types.at(object->type).propertyCache())) { | |
// XXX fromUtf8 | |
QString strName(QString::fromUtf8(name)); | |
QDeclarativePropertyCache *cache = | |
object->synthCache?object->synthCache:output->types.at(object->type).propertyCache(); | |
QDeclarativePropertyCache::Data *d = cache->property(strName); | |
if (notInRevision) *notInRevision = false; | |
while (d && !(d->flags & QDeclarativePropertyCache::Data::IsFunction)) | |
d = cache->overrideData(d); | |
if (d && !cache->isAllowedInRevision(d)) { | |
if (notInRevision) *notInRevision = true; | |
return -1; | |
} else if (d) { | |
return d->coreIndex; | |
} | |
if (name.endsWith("Changed")) { | |
QByteArray propName = name.mid(0, name.length() - 7); | |
int propIndex = indexOfProperty(object, propName, notInRevision); | |
if (propIndex != -1) { | |
d = cache->property(propIndex); | |
return d->notifyIndex; | |
} | |
} | |
return -1; | |
} else { | |
return QDeclarativePropertyPrivate::findSignalByName(object->metaObject(), name).methodIndex(); | |
} | |
} | |
int QDeclarativeCompiler::indexOfProperty(QDeclarativeParser::Object *object, const QByteArray &name, | |
bool *notInRevision) | |
{ | |
if (notInRevision) *notInRevision = false; | |
if (object->synthCache || (object->type != -1 && output->types.at(object->type).propertyCache())) { | |
// XXX fromUtf8 | |
QString strName(QString::fromUtf8(name)); | |
QDeclarativePropertyCache *cache = | |
object->synthCache?object->synthCache:output->types.at(object->type).propertyCache(); | |
QDeclarativePropertyCache::Data *d = cache->property(strName); | |
// Find the first property | |
while (d && d->flags & QDeclarativePropertyCache::Data::IsFunction) | |
d = cache->overrideData(d); | |
if (d && !cache->isAllowedInRevision(d)) { | |
if (notInRevision) *notInRevision = true; | |
return -1; | |
} else { | |
return d?d->coreIndex:-1; | |
} | |
} else { | |
const QMetaObject *mo = object->metaObject(); | |
return mo->indexOfProperty(name.constData()); | |
} | |
} | |
QT_END_NAMESPACE |