1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include "qqmlcompiler_p.h"
44 #include "qqmlpropertyvaluesource.h"
45 #include "qqmlcomponent.h"
46 #include <private/qmetaobjectbuilder_p.h>
47 #include "qqmlstringconverters_p.h"
48 #include "qqmlengine_p.h"
49 #include "qqmlengine.h"
50 #include "qqmlcontext.h"
51 #include "qqmlmetatype_p.h"
52 #include "qqmlcustomparser_p_p.h"
53 #include "qqmlcontext_p.h"
54 #include "qqmlcomponent_p.h"
55 #include <private/qqmljsast_p.h>
56 #include "qqmlvmemetaobject_p.h"
57 #include "qqmlexpression_p.h"
58 #include "qqmlproperty_p.h"
59 #include "qqmlrewrite_p.h"
60 #include "qqmlscriptstring.h"
61 #include "qqmlglobal_p.h"
62 #include "qqmlbinding_p.h"
63 #include <private/qv4compiler_p.h>
70 #include <QtCore/qdebug.h>
71 #include <QtCore/qdatetime.h>
72 #include <QtCore/qvarlengtharray.h>
74 Q_DECLARE_METATYPE(QList<int>)
75 Q_DECLARE_METATYPE(QList<qreal>)
76 Q_DECLARE_METATYPE(QList<bool>)
77 Q_DECLARE_METATYPE(QList<QString>)
78 Q_DECLARE_METATYPE(QList<QUrl>)
82 DEFINE_BOOL_CONFIG_OPTION(compilerDump, QML_COMPILER_DUMP);
83 DEFINE_BOOL_CONFIG_OPTION(compilerStatDump, QML_COMPILER_STATS);
85 using namespace QQmlJS;
86 using namespace QQmlScript;
87 using namespace QQmlCompilerTypes;
89 static QString id_string(QLatin1String("id"));
90 static QString on_string(QLatin1String("on"));
91 static QString Changed_string(QLatin1String("Changed"));
92 static QString Component_string(QLatin1String("Component"));
93 static QString Component_module_string(QLatin1String("QML"));
94 static QString qsTr_string(QLatin1String("qsTr"));
95 static QString qsTrId_string(QLatin1String("qsTrId"));
98 Instantiate a new QQmlCompiler.
100 QQmlCompiler::QQmlCompiler(QQmlPool *pool)
101 : compileState(0), pool(pool), output(0), engine(0), enginePrivate(0), unitRoot(0), unit(0), cachedComponentTypeRef(-1),
102 cachedTranslationContextIndex(-1), componentStats(0)
104 if (compilerStatDump())
105 componentStats = pool->New<ComponentStats>();
109 Returns true if the last call to compile() caused errors.
113 bool QQmlCompiler::isError() const
115 return !exceptions.isEmpty();
119 Return the list of errors from the last call to compile(), or an empty list
120 if there were no errors.
122 QList<QQmlError> QQmlCompiler::errors() const
128 Returns true if \a name refers to an attached property, false otherwise.
130 Attached property names are those that start with a capital letter.
132 bool QQmlCompiler::isAttachedPropertyName(const QString &name)
134 return isAttachedPropertyName(QHashedStringRef(&name));
137 bool QQmlCompiler::isAttachedPropertyName(const QHashedStringRef &name)
139 return !name.isEmpty() && name.at(0).isUpper();
143 Returns true if \a name refers to a signal property, false otherwise.
145 Signal property names are those that start with "on", followed by a first
146 character which is either a capital letter or one or more underscores followed
147 by a capital letter, which is then followed by other allowed characters.
149 Note that although ECMA-262r3 supports dollarsigns and escaped unicode
150 character codes in property names, for simplicity and performance reasons
151 QML only supports letters, numbers and underscores.
153 bool QQmlCompiler::isSignalPropertyName(const QString &name)
155 return isSignalPropertyName(QStringRef(&name));
158 bool QQmlCompiler::isSignalPropertyName(const QHashedStringRef &name)
160 if (name.length() < 3) return false;
161 if (!name.startsWith(on_string)) return false;
162 int ns = name.length();
163 for (int i = 2; i < ns; ++i) {
164 const QChar curr = name.at(i);
165 if (curr.unicode() == '_') continue;
166 if (curr.isUpper()) return true;
169 return false; // consists solely of underscores - invalid.
173 \macro COMPILE_EXCEPTION
175 Inserts an error into the QQmlCompiler error list, and returns false
178 \a token is used to source the error line and column, and \a desc is the
179 error itself. \a desc can be an expression that can be piped into QDebug.
184 COMPILE_EXCEPTION(property, tr("Error for property \"%1\"").arg(property->name));
187 #define COMPILE_EXCEPTION_LOCATION(line, column, desc) \
190 error.setUrl(output->url); \
191 error.setLine(line); \
192 error.setColumn(column); \
193 error.setDescription(desc.trimmed()); \
194 exceptions << error; \
198 #define COMPILE_EXCEPTION(token, desc) \
199 COMPILE_EXCEPTION_LOCATION((token)->location.start.line, (token)->location.start.column, desc)
204 Returns false if \a is false, otherwise does nothing.
206 #define COMPILE_CHECK(a) \
208 if (!a) return false; \
212 Returns true if literal \a v can be assigned to property \a prop, otherwise
215 This test corresponds to action taken by genLiteralAssignment(). Any change
216 made here, must have a corresponding action in genLiteralAssigment().
218 bool QQmlCompiler::testLiteralAssignment(QQmlScript::Property *prop, QQmlScript::Value *v)
220 const QQmlScript::Variant &value = v->value;
222 if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration)
223 COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
225 if (prop->core.isEnum()) {
226 QMetaProperty p = prop->parent->metatype->firstCppMetaObject()->property(prop->index);
229 if (p.isFlagType()) {
230 enumValue = p.enumerator().keysToValue(value.asString().toUtf8().constData(), &ok);
232 enumValue = p.enumerator().keyToValue(value.asString().toUtf8().constData(), &ok);
235 COMPILE_EXCEPTION(v, tr("Invalid property assignment: unknown enumeration"));
237 v->value = QQmlScript::Variant((double)enumValue);
241 int type = prop->type;
244 case QMetaType::QVariant:
246 case QVariant::String:
247 if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: string expected"));
249 case QVariant::StringList: // we expect a string literal. A string list is not a literal assignment.
250 if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: string or string list expected"));
252 case QVariant::ByteArray:
253 if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: byte array expected"));
256 if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: url expected"));
258 case QVariant::RegExp:
259 COMPILE_EXCEPTION(v, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax"));
263 bool ok = v->value.isNumber();
265 double n = v->value.asNumber();
266 if (double(uint(n)) != n)
269 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: unsigned int expected"));
274 bool ok = v->value.isNumber();
276 double n = v->value.asNumber();
277 if (double(int(n)) != n)
280 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: int expected"));
283 case QMetaType::Float:
284 if (!v->value.isNumber()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: number expected"));
286 case QVariant::Double:
287 if (!v->value.isNumber()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: number expected"));
289 case QVariant::Color:
292 QQmlStringConverters::colorFromString(value.asString(), &ok);
293 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: color expected"));
296 #ifndef QT_NO_DATESTRING
300 QQmlStringConverters::dateFromString(value.asString(), &ok);
301 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: date expected"));
307 QQmlStringConverters::timeFromString(value.asString(), &ok);
308 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: time expected"));
311 case QVariant::DateTime:
314 QQmlStringConverters::dateTimeFromString(value.asString(), &ok);
315 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: datetime expected"));
318 #endif // QT_NO_DATESTRING
319 case QVariant::Point:
320 case QVariant::PointF:
323 QQmlStringConverters::pointFFromString(value.asString(), &ok);
324 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: point expected"));
328 case QVariant::SizeF:
331 QQmlStringConverters::sizeFFromString(value.asString(), &ok);
332 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: size expected"));
336 case QVariant::RectF:
339 QQmlStringConverters::rectFFromString(value.asString(), &ok);
340 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: rect expected"));
345 if (!v->value.isBoolean()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: boolean expected"));
348 case QVariant::Vector3D:
350 QQmlInstruction::instr_storeVector3D::QVector3D v3;
351 if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, value.asString(), &v3, sizeof(v3)))
352 COMPILE_EXCEPTION(v, tr("Invalid property assignment: 3D vector expected"));
355 case QVariant::Vector4D:
357 QQmlInstruction::instr_storeVector4D::QVector4D v4;
358 if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, value.asString(), &v4, sizeof(v4)))
359 COMPILE_EXCEPTION(v, tr("Invalid property assignment: 4D vector expected"));
364 // check if assigning a literal value to a list property.
365 // in each case, check the singular, since an Array of the specified type
366 // will not go via this literal assignment codepath.
367 if (type == qMetaTypeId<QList<qreal> >()) {
368 if (!v->value.isNumber()) {
369 COMPILE_EXCEPTION(v, tr("Invalid property assignment: real or array of reals expected"));
372 } else if (type == qMetaTypeId<QList<int> >()) {
373 bool ok = v->value.isNumber();
375 double n = v->value.asNumber();
376 if (double(int(n)) != n)
379 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: int or array of ints expected"));
381 } else if (type == qMetaTypeId<QList<bool> >()) {
382 if (!v->value.isBoolean()) {
383 COMPILE_EXCEPTION(v, tr("Invalid property assignment: bool or array of bools expected"));
386 } else if (type == qMetaTypeId<QList<QString> >()) { // we expect a string literal. A string list is not a literal assignment.
387 if (!v->value.isString()) {
388 COMPILE_EXCEPTION(v, tr("Invalid property assignment: string or array of strings expected"));
391 } else if (type == qMetaTypeId<QList<QUrl> >()) {
392 if (!v->value.isString()) {
393 COMPILE_EXCEPTION(v, tr("Invalid property assignment: url or array of urls expected"));
396 } else if (type == qMetaTypeId<QJSValue>()) {
400 // otherwise, check for existence of string converter to custom type
401 QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(type);
403 COMPILE_EXCEPTION(v, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(type))));
411 Generate a store instruction for assigning literal \a v to property \a prop.
413 Any literal assignment that is approved in testLiteralAssignment() must have
414 a corresponding action in this method.
416 void QQmlCompiler::genLiteralAssignment(QQmlScript::Property *prop,
417 QQmlScript::Value *v)
419 if (prop->core.isEnum()) {
420 Q_ASSERT(v->value.isNumber());
422 int value = (int)v->value.asNumber();
424 Instruction::StoreInteger instr;
425 instr.propertyIndex = prop->index;
427 output->addInstruction(instr);
431 int type = prop->type;
433 case QMetaType::QVariant:
435 if (v->value.isNumber()) {
436 double n = v->value.asNumber();
437 if (double(int(n)) == n) {
438 if (prop->core.isVarProperty()) {
439 Instruction::StoreVarInteger instr;
440 instr.propertyIndex = prop->index;
441 instr.value = int(n);
442 output->addInstruction(instr);
444 Instruction::StoreVariantInteger instr;
445 instr.propertyIndex = prop->index;
446 instr.value = int(n);
447 output->addInstruction(instr);
450 if (prop->core.isVarProperty()) {
451 Instruction::StoreVarDouble instr;
452 instr.propertyIndex = prop->index;
454 output->addInstruction(instr);
456 Instruction::StoreVariantDouble instr;
457 instr.propertyIndex = prop->index;
459 output->addInstruction(instr);
462 } else if (v->value.isBoolean()) {
463 if (prop->core.isVarProperty()) {
464 Instruction::StoreVarBool instr;
465 instr.propertyIndex = prop->index;
466 instr.value = v->value.asBoolean();
467 output->addInstruction(instr);
469 Instruction::StoreVariantBool instr;
470 instr.propertyIndex = prop->index;
471 instr.value = v->value.asBoolean();
472 output->addInstruction(instr);
475 if (prop->core.isVarProperty()) {
476 Instruction::StoreVar instr;
477 instr.propertyIndex = prop->index;
478 instr.value = output->indexForString(v->value.asString());
479 output->addInstruction(instr);
481 Instruction::StoreVariant instr;
482 instr.propertyIndex = prop->index;
483 instr.value = output->indexForString(v->value.asString());
484 output->addInstruction(instr);
489 case QVariant::String:
491 Instruction::StoreString instr;
492 instr.propertyIndex = prop->index;
493 instr.value = output->indexForString(v->value.asString());
494 output->addInstruction(instr);
497 case QVariant::StringList:
499 Instruction::StoreStringList instr;
500 instr.propertyIndex = prop->index;
501 instr.value = output->indexForString(v->value.asString());
502 output->addInstruction(instr);
505 case QVariant::ByteArray:
507 Instruction::StoreByteArray instr;
508 instr.propertyIndex = prop->index;
509 instr.value = output->indexForByteArray(v->value.asString().toLatin1());
510 output->addInstruction(instr);
515 Instruction::StoreUrl instr;
516 QString string = v->value.asString();
517 // Encoded dir-separators defeat QUrl processing - decode them first
518 string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive);
519 QUrl u = string.isEmpty() ? QUrl() : output->url.resolved(QUrl(string));
520 instr.propertyIndex = prop->index;
521 instr.value = output->indexForUrl(u);
522 output->addInstruction(instr);
527 Instruction::StoreInteger instr;
528 instr.propertyIndex = prop->index;
529 instr.value = uint(v->value.asNumber());
530 output->addInstruction(instr);
535 Instruction::StoreInteger instr;
536 instr.propertyIndex = prop->index;
537 instr.value = int(v->value.asNumber());
538 output->addInstruction(instr);
541 case QMetaType::Float:
543 Instruction::StoreFloat instr;
544 instr.propertyIndex = prop->index;
545 instr.value = float(v->value.asNumber());
546 output->addInstruction(instr);
549 case QVariant::Double:
551 Instruction::StoreDouble instr;
552 instr.propertyIndex = prop->index;
553 instr.value = v->value.asNumber();
554 output->addInstruction(instr);
557 case QVariant::Color:
559 Instruction::StoreColor instr;
560 instr.propertyIndex = prop->index;
561 instr.value = QQmlStringConverters::rgbaFromString(v->value.asString());
562 output->addInstruction(instr);
565 #ifndef QT_NO_DATESTRING
568 Instruction::StoreDate instr;
569 QDate d = QQmlStringConverters::dateFromString(v->value.asString());
570 instr.propertyIndex = prop->index;
571 instr.value = d.toJulianDay();
572 output->addInstruction(instr);
577 Instruction::StoreTime instr;
578 QTime time = QQmlStringConverters::timeFromString(v->value.asString());
579 instr.propertyIndex = prop->index;
580 Q_ASSERT(sizeof(instr.time) == sizeof(QTime));
581 ::memcpy(&instr.time, &time, sizeof(QTime));
582 output->addInstruction(instr);
585 case QVariant::DateTime:
587 Instruction::StoreDateTime instr;
588 QDateTime dateTime = QQmlStringConverters::dateTimeFromString(v->value.asString());
589 QTime time = dateTime.time();
590 instr.propertyIndex = prop->index;
591 instr.date = dateTime.date().toJulianDay();
592 Q_ASSERT(sizeof(instr.time) == sizeof(QTime));
593 ::memcpy(&instr.time, &time, sizeof(QTime));
594 output->addInstruction(instr);
597 #endif // QT_NO_DATESTRING
598 case QVariant::Point:
600 Instruction::StorePoint instr;
602 QPoint point = QQmlStringConverters::pointFFromString(v->value.asString(), &ok).toPoint();
603 instr.propertyIndex = prop->index;
604 instr.point.xp = point.x();
605 instr.point.yp = point.y();
606 output->addInstruction(instr);
609 case QVariant::PointF:
611 Instruction::StorePointF instr;
613 QPointF point = QQmlStringConverters::pointFFromString(v->value.asString(), &ok);
614 instr.propertyIndex = prop->index;
615 instr.point.xp = point.x();
616 instr.point.yp = point.y();
617 output->addInstruction(instr);
622 Instruction::StoreSize instr;
624 QSize size = QQmlStringConverters::sizeFFromString(v->value.asString(), &ok).toSize();
625 instr.propertyIndex = prop->index;
626 instr.size.wd = size.width();
627 instr.size.ht = size.height();
628 output->addInstruction(instr);
631 case QVariant::SizeF:
633 Instruction::StoreSizeF instr;
635 QSizeF size = QQmlStringConverters::sizeFFromString(v->value.asString(), &ok);
636 instr.propertyIndex = prop->index;
637 instr.size.wd = size.width();
638 instr.size.ht = size.height();
639 output->addInstruction(instr);
644 Instruction::StoreRect instr;
646 QRect rect = QQmlStringConverters::rectFFromString(v->value.asString(), &ok).toRect();
647 instr.propertyIndex = prop->index;
648 instr.rect.x1 = rect.left();
649 instr.rect.y1 = rect.top();
650 instr.rect.x2 = rect.right();
651 instr.rect.y2 = rect.bottom();
652 output->addInstruction(instr);
655 case QVariant::RectF:
657 Instruction::StoreRectF instr;
659 QRectF rect = QQmlStringConverters::rectFFromString(v->value.asString(), &ok);
660 instr.propertyIndex = prop->index;
661 instr.rect.xp = rect.left();
662 instr.rect.yp = rect.top();
663 instr.rect.w = rect.width();
664 instr.rect.h = rect.height();
665 output->addInstruction(instr);
670 Instruction::StoreBool instr;
671 bool b = v->value.asBoolean();
672 instr.propertyIndex = prop->index;
674 output->addInstruction(instr);
677 case QVariant::Vector3D:
679 Instruction::StoreVector3D instr;
680 instr.propertyIndex = prop->index;
681 QQmlStringConverters::createFromString(QMetaType::QVector3D, v->value.asString(), &instr.vector, sizeof(instr.vector));
682 output->addInstruction(instr);
685 case QVariant::Vector4D:
687 Instruction::StoreVector4D instr;
688 instr.propertyIndex = prop->index;
689 QQmlStringConverters::createFromString(QMetaType::QVector4D, v->value.asString(), &instr.vector, sizeof(instr.vector));
690 output->addInstruction(instr);
695 // generate single literal value assignment to a list property if required
696 if (type == qMetaTypeId<QList<qreal> >()) {
697 Instruction::StoreDoubleQList instr;
698 instr.propertyIndex = prop->index;
699 instr.value = v->value.asNumber();
700 output->addInstruction(instr);
702 } else if (type == qMetaTypeId<QList<int> >()) {
703 Instruction::StoreIntegerQList instr;
704 instr.propertyIndex = prop->index;
705 instr.value = int(v->value.asNumber());
706 output->addInstruction(instr);
708 } else if (type == qMetaTypeId<QList<bool> >()) {
709 Instruction::StoreBoolQList instr;
710 bool b = v->value.asBoolean();
711 instr.propertyIndex = prop->index;
713 output->addInstruction(instr);
715 } else if (type == qMetaTypeId<QList<QUrl> >()) {
716 Instruction::StoreUrlQList instr;
717 QString string = v->value.asString();
718 QUrl u = string.isEmpty() ? QUrl() : output->url.resolved(QUrl(string));
719 instr.propertyIndex = prop->index;
720 instr.value = output->indexForUrl(u);
721 output->addInstruction(instr);
723 } else if (type == qMetaTypeId<QList<QString> >()) {
724 Instruction::StoreStringQList instr;
725 instr.propertyIndex = prop->index;
726 instr.value = output->indexForString(v->value.asString());
727 output->addInstruction(instr);
729 } else if (type == qMetaTypeId<QJSValue>()) {
730 if (v->value.isBoolean()) {
731 Instruction::StoreJSValueBool instr;
732 instr.propertyIndex = prop->index;
733 instr.value = v->value.asBoolean();
734 output->addInstruction(instr);
735 } else if (v->value.isNumber()) {
736 double n = v->value.asNumber();
737 if (double(int(n)) == n) {
738 Instruction::StoreJSValueInteger instr;
739 instr.propertyIndex = prop->index;
740 instr.value = int(n);
741 output->addInstruction(instr);
743 Instruction::StoreJSValueDouble instr;
744 instr.propertyIndex = prop->index;
746 output->addInstruction(instr);
749 Instruction::StoreJSValueString instr;
750 instr.propertyIndex = prop->index;
751 instr.value = output->indexForString(v->value.asString());
752 output->addInstruction(instr);
757 // otherwise, generate custom type literal assignment
758 Instruction::AssignCustomType instr;
759 instr.propertyIndex = prop->index;
760 instr.primitive = output->indexForString(v->value.asString());
762 output->addInstruction(instr);
769 Resets data by clearing the lists that the QQmlCompiler modifies.
771 void QQmlCompiler::reset(QQmlCompiledData *data)
774 data->primitives.clear();
776 data->bytecode.resize(0);
780 Compile \a unit, and store the output in \a out. \a engine is the QQmlEngine
781 with which the QQmlCompiledData will be associated.
783 Returns true on success, false on failure. On failure, the compile errors
784 are available from errors().
786 If the environment variant QML_COMPILER_DUMP is set
787 (eg. QML_COMPILER_DUMP=1) the compiled instructions will be dumped to stderr
788 on a successful compiler.
790 bool QQmlCompiler::compile(QQmlEngine *engine,
792 QQmlCompiledData *out)
799 QQmlScript::Object *root = unit->parser().tree();
802 this->engine = engine;
803 this->enginePrivate = QQmlEnginePrivate::get(engine);
805 this->unitRoot = root;
809 const QList<QQmlTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes();
810 QList<QQmlScript::TypeReference *> referencedTypes = unit->parser().referencedTypes();
812 for (int ii = 0; ii < resolvedTypes.count(); ++ii) {
813 QQmlCompiledData::TypeReference ref;
815 const QQmlTypeData::TypeReference &tref = resolvedTypes.at(ii);
816 QQmlScript::TypeReference *parserRef = referencedTypes.at(ii);
819 ref.type = tref.type;
820 if (!ref.type->isCreatable()) {
821 QString err = ref.type->noCreationReason();
823 err = tr( "Element is not creatable.");
824 COMPILE_EXCEPTION(parserRef->firstUse, err);
827 if (ref.type->containsRevisionedAttributes()) {
828 QQmlError cacheError;
829 ref.typePropertyCache = enginePrivate->cache(ref.type,
830 resolvedTypes.at(ii).minorVersion,
832 if (!ref.typePropertyCache)
833 COMPILE_EXCEPTION(parserRef->firstUse, cacheError.description());
834 ref.typePropertyCache->addref();
837 } else if (tref.typeData) {
838 ref.component = tref.typeData->compiledData();
839 ref.component->addref();
848 out->dumpInstructions();
851 Q_ASSERT(out->rootPropertyCache);
859 this->enginePrivate = 0;
861 this->cachedComponentTypeRef = -1;
862 this->cachedTranslationContextIndex = -1;
868 void QQmlCompiler::compileTree(QQmlScript::Object *tree)
870 compileState = pool->New<ComponentCompileState>();
872 compileState->root = tree;
874 componentStats->componentStat.lineNumber = tree->location.start.line;
876 // We generate the importCache before we build the tree so that
877 // it can be used in the binding compiler. Given we "expect" the
878 // QML compilation to succeed, this isn't a waste.
879 output->importCache = new QQmlTypeNameCache();
880 foreach (const QString &ns, unit->namespaces()) {
881 output->importCache->add(ns);
885 foreach (const QQmlTypeData::ScriptReference &script, unit->resolvedScripts()) {
886 QString qualifier = script.qualifier;
887 QString enclosingNamespace;
889 const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.'));
890 if (lastDotIndex != -1) {
891 enclosingNamespace = qualifier.left(lastDotIndex);
892 qualifier = qualifier.mid(lastDotIndex+1);
895 output->importCache->add(qualifier, scriptIndex++, enclosingNamespace);
898 unit->imports().populateCache(output->importCache);
900 if (!buildObject(tree, BindingContext()) || !completeComponentBuild())
903 Instruction::Init init;
904 init.bindingsSize = compileState->totalBindingsCount;
905 init.parserStatusSize = compileState->parserStatusCount;
906 init.contextCache = genContextCache();
907 init.objectStackSize = compileState->objectDepth.maxDepth();
908 init.listStackSize = compileState->listDepth.maxDepth();
909 if (compileState->compiledBindingData.isEmpty())
910 init.compiledBinding = -1;
912 init.compiledBinding = output->indexForByteArray(compileState->compiledBindingData);
913 output->addInstruction(init);
915 foreach (const QQmlTypeData::ScriptReference &script, unit->resolvedScripts()) {
916 Instruction::StoreImportedScript import;
917 import.value = output->scripts.count();
919 QQmlScriptData *scriptData = script.script->scriptData();
920 scriptData->addref();
921 output->scripts << scriptData;
922 output->addInstruction(import);
925 if (!compileState->v8BindingProgram.isEmpty()) {
926 Instruction::InitV8Bindings bindings;
927 int index = output->programs.count();
929 typedef QQmlCompiledData::V8Program V8Program;
930 output->programs.append(V8Program(compileState->v8BindingProgram, output));
932 bindings.programIndex = index;
933 bindings.line = compileState->v8BindingProgramLine;
934 output->addInstruction(bindings);
939 Instruction::SetDefault def;
940 output->addInstruction(def);
942 Instruction::Done done;
943 output->addInstruction(done);
945 Q_ASSERT(tree->metatype);
946 if (!tree->synthdata.isEmpty()) {
947 enginePrivate->registerCompositeType(output);
948 } else if (output->types.at(tree->type).component) {
949 output->metaTypeId = output->types.at(tree->type).component->metaTypeId;
950 output->listMetaTypeId = output->types.at(tree->type).component->listMetaTypeId;
952 Q_ASSERT(output->types.at(tree->type).type);
953 output->metaTypeId = output->types.at(tree->type).type->typeId();
954 output->listMetaTypeId = output->types.at(tree->type).type->qListTypeId();
956 if (!tree->synthdata.isEmpty())
957 enginePrivate->registerCompositeType(output);
960 static bool QStringList_contains(const QStringList &list, const QHashedStringRef &string)
962 for (int ii = 0; ii < list.count(); ++ii)
963 if (string == list.at(ii))
969 bool QQmlCompiler::buildObject(QQmlScript::Object *obj, const BindingContext &ctxt)
972 componentStats->componentStat.objects++;
974 Q_ASSERT (obj->type != -1);
975 QQmlCompiledData::TypeReference &tr = output->types[obj->type];
976 obj->metatype = tr.createPropertyCache(engine);
978 // This object is a "Component" element.
979 if (tr.type && obj->metatype->metaObject() == &QQmlComponent::staticMetaObject) {
980 COMPILE_CHECK(buildComponent(obj, ctxt));
985 typedef QQmlInstruction I;
986 const I *init = ((const I *)tr.component->bytecode.constData());
987 Q_ASSERT(init && tr.component->instructionType(init) == QQmlInstruction::Init);
989 // Adjust stack depths to include nested components
990 compileState->objectDepth.pushPop(init->init.objectStackSize);
991 compileState->listDepth.pushPop(init->init.listStackSize);
992 compileState->parserStatusCount += init->init.parserStatusSize;
993 compileState->totalBindingsCount += init->init.bindingsSize;
996 compileState->objectDepth.push();
998 // Object instantiations reset the binding context
999 BindingContext objCtxt(obj);
1001 // Create the synthesized meta object, ignoring aliases
1002 COMPILE_CHECK(checkDynamicMeta(obj));
1003 COMPILE_CHECK(mergeDynamicMetaProperties(obj));
1004 COMPILE_CHECK(buildDynamicMeta(obj, Normal));
1006 // Find the native type and check for the QQmlParserStatus interface
1007 QQmlType *type = toQmlType(obj);
1009 obj->parserStatusCast = type->parserStatusCast();
1010 if (obj->parserStatusCast != -1)
1011 compileState->parserStatusCount++;
1013 // Check if this is a custom parser type. Custom parser types allow
1014 // assignments to non-existent properties. These assignments are then
1015 // compiled by the type.
1016 bool isCustomParser = output->types.at(obj->type).type &&
1017 output->types.at(obj->type).type->customParser() != 0;
1018 QList<QQmlCustomParserProperty> customProps;
1020 // Fetch the list of deferred properties
1021 QStringList deferredList = deferredProperties(obj);
1023 // Must do id property first. This is to ensure that the id given to any
1024 // id reference created matches the order in which the objects are
1026 for (Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) {
1027 if (prop->name() == id_string) {
1028 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
1034 Property *defaultProperty = 0;
1035 Property *skipProperty = 0;
1036 if (obj->defaultProperty) {
1037 defaultProperty = obj->defaultProperty;
1039 Property *explicitProperty = 0;
1041 QString defaultPropertyName = obj->metatype->defaultPropertyName();
1042 if (!defaultPropertyName.isEmpty()) {
1043 QString *s = pool->NewString(defaultPropertyName);
1044 QHashedStringRef r(*s);
1046 if (obj->propertiesHashField.test(r.hash())) {
1047 for (Property *ep = obj->properties.first(); ep; ep = obj->properties.next(ep)) {
1048 if (ep->name() == r) {
1049 explicitProperty = ep;
1055 if (!explicitProperty)
1056 defaultProperty->setName(r);
1059 if (explicitProperty && !explicitProperty->value && !explicitProperty->values.isEmpty()) {
1061 skipProperty = explicitProperty; // We merge the values into defaultProperty
1063 // Find the correct insertion point
1064 Value *insertPos = 0;
1066 for (Value *v = defaultProperty->values.first(); v; v = Property::ValueList::next(v)) {
1067 if (!(v->location.start < explicitProperty->values.first()->location.start))
1072 defaultProperty->values.insertAfter(insertPos, explicitProperty->values);
1076 QQmlCustomParser *cp = 0;
1078 cp = output->types.at(obj->type).type->customParser();
1080 // Build all explicit properties specified
1081 for (Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) {
1083 if (prop == skipProperty)
1085 if (prop->name() == id_string)
1088 bool canDefer = false;
1089 if (isCustomParser) {
1090 if (doesPropertyExist(prop, obj) &&
1091 (!(cp->flags() & QQmlCustomParser::AcceptsAttachedProperties) ||
1092 !isAttachedPropertyName(prop->name()))) {
1093 int ids = compileState->ids.count();
1094 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
1095 canDefer = ids == compileState->ids.count();
1096 } else if (isSignalPropertyName(prop->name()) &&
1097 (cp->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
1098 COMPILE_CHECK(buildSignal(prop,obj,objCtxt));
1100 customProps << QQmlCustomParserNodePrivate::fromProperty(prop);
1103 if (isSignalPropertyName(prop->name())) {
1104 COMPILE_CHECK(buildSignal(prop,obj,objCtxt));
1106 int ids = compileState->ids.count();
1107 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
1108 canDefer = ids == compileState->ids.count();
1112 if (canDefer && !deferredList.isEmpty() && QStringList_contains(deferredList, prop->name()))
1113 prop->isDeferred = true;
1117 // Build the default property
1118 if (defaultProperty) {
1119 Property *prop = defaultProperty;
1121 bool canDefer = false;
1122 if (isCustomParser) {
1123 if (doesPropertyExist(prop, obj)) {
1124 int ids = compileState->ids.count();
1125 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
1126 canDefer = ids == compileState->ids.count();
1128 customProps << QQmlCustomParserNodePrivate::fromProperty(prop);
1131 int ids = compileState->ids.count();
1132 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
1133 canDefer = ids == compileState->ids.count();
1136 if (canDefer && !deferredList.isEmpty() && QStringList_contains(deferredList, prop->name()))
1137 prop->isDeferred = true;
1140 // Compile custom parser parts
1141 if (isCustomParser && !customProps.isEmpty()) {
1143 cp->compiler = this;
1145 obj->custom = cp->compile(customProps);
1148 foreach (QQmlError err, cp->errors()) {
1149 err.setUrl(output->url);
1154 compileState->objectDepth.pop();
1159 void QQmlCompiler::genObject(QQmlScript::Object *obj, bool parentToSuper)
1161 QQmlCompiledData::TypeReference &tr = output->types[obj->type];
1162 if (tr.type && obj->metatype->metaObject() == &QQmlComponent::staticMetaObject) {
1167 // Create the object
1168 if (obj->custom.isEmpty() && output->types.at(obj->type).type &&
1169 !output->types.at(obj->type).type->isExtendedType() && obj != compileState->root) {
1171 Instruction::CreateSimpleObject create;
1172 create.create = output->types.at(obj->type).type->createFunction();
1173 create.typeSize = output->types.at(obj->type).type->createSize();
1174 create.type = obj->type;
1175 create.line = obj->location.start.line;
1176 create.column = obj->location.start.column;
1177 create.parentToSuper = parentToSuper;
1178 output->addInstruction(create);
1182 if (output->types.at(obj->type).type) {
1183 Instruction::CreateCppObject create;
1184 create.line = obj->location.start.line;
1185 create.column = obj->location.start.column;
1187 if (!obj->custom.isEmpty())
1188 create.data = output->indexForByteArray(obj->custom);
1189 create.type = obj->type;
1190 create.isRoot = (compileState->root == obj);
1191 create.parentToSuper = parentToSuper;
1192 output->addInstruction(create);
1194 Instruction::CreateQMLObject create;
1195 create.type = obj->type;
1196 create.isRoot = (compileState->root == obj);
1198 if (!obj->bindingBitmask.isEmpty()) {
1199 Q_ASSERT(obj->bindingBitmask.size() % 4 == 0);
1200 create.bindingBits = output->indexForByteArray(obj->bindingBitmask);
1202 create.bindingBits = -1;
1204 output->addInstruction(create);
1206 Instruction::CompleteQMLObject complete;
1207 complete.line = obj->location.start.line;
1208 complete.column = obj->location.start.column;
1209 complete.isRoot = (compileState->root == obj);
1210 output->addInstruction(complete);
1214 // Setup the synthesized meta object if necessary
1215 if (!obj->synthdata.isEmpty()) {
1216 Instruction::StoreMetaObject meta;
1217 meta.aliasData = output->indexForByteArray(obj->synthdata);
1218 meta.propertyCache = output->propertyCaches.count();
1220 QQmlPropertyCache *propertyCache = obj->synthCache;
1221 Q_ASSERT(propertyCache);
1222 propertyCache->addref();
1224 if (obj == unitRoot) {
1225 propertyCache->addref();
1226 output->rootPropertyCache = propertyCache;
1229 output->propertyCaches << propertyCache;
1230 output->addInstruction(meta);
1231 } else if (obj == unitRoot) {
1232 output->rootPropertyCache = tr.createPropertyCache(engine);
1233 output->rootPropertyCache->addref();
1236 // Set the object id
1237 if (!obj->id.isEmpty()) {
1238 Instruction::SetId id;
1239 id.value = output->indexForString(obj->id);
1240 id.index = obj->idIndex;
1241 output->addInstruction(id);
1245 if (tr.type && obj->parserStatusCast != -1) {
1246 Instruction::BeginObject begin;
1247 begin.castValue = obj->parserStatusCast;
1248 output->addInstruction(begin);
1254 void QQmlCompiler::genObjectBody(QQmlScript::Object *obj)
1256 for (Property *prop = obj->scriptStringProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1257 Q_ASSERT(prop->scriptStringScope != -1);
1258 const QString &script = prop->values.first()->value.asScript();
1259 Instruction::StoreScriptString ss;
1260 ss.propertyIndex = prop->index;
1261 ss.value = output->indexForString(script);
1262 ss.scope = prop->scriptStringScope;
1263 // ss.bindingId = rewriteBinding(script, prop->name());
1264 ss.bindingId = rewriteBinding(prop->values.first()->value, QString()); // XXX
1265 ss.line = prop->location.start.line;
1266 ss.column = prop->location.start.column;
1267 ss.isStringLiteral = prop->values.first()->value.isString();
1268 ss.isNumberLiteral = prop->values.first()->value.isNumber();
1269 ss.numberValue = prop->values.first()->value.asNumber();
1270 output->addInstruction(ss);
1273 bool seenDefer = false;
1274 for (Property *prop = obj->valueProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1275 if (prop->isDeferred) {
1280 genValueProperty(prop, obj);
1283 Instruction::Defer defer;
1284 defer.deferCount = 0;
1285 int deferIdx = output->addInstruction(defer);
1286 int nextInstructionIndex = output->nextInstructionIndex();
1288 Instruction::DeferInit dinit;
1289 // XXX - these are now massive over allocations
1290 dinit.bindingsSize = compileState->totalBindingsCount;
1291 dinit.parserStatusSize = compileState->parserStatusCount;
1292 dinit.objectStackSize = compileState->objectDepth.maxDepth();
1293 dinit.listStackSize = compileState->listDepth.maxDepth();
1294 output->addInstruction(dinit);
1296 for (Property *prop = obj->valueProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1297 if (!prop->isDeferred)
1299 genValueProperty(prop, obj);
1302 Instruction::Done done;
1303 output->addInstruction(done);
1305 output->instruction(deferIdx)->defer.deferCount = output->nextInstructionIndex() - nextInstructionIndex;
1308 for (Property *prop = obj->signalProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1310 QQmlScript::Value *v = prop->values.first();
1312 if (v->type == Value::SignalObject) {
1314 genObject(v->object);
1316 Instruction::AssignSignalObject assign;
1317 assign.line = v->location.start.line;
1318 assign.signal = output->indexForString(prop->name().toString());
1319 output->addInstruction(assign);
1321 } else if (v->type == Value::SignalExpression) {
1323 Instruction::StoreSignal store;
1324 store.signalIndex = prop->index;
1326 const QList<QByteArray> ¶meterNameList = obj->metatype->signalParameterNames(prop->index);
1327 QQmlRewrite::RewriteSignalHandler rewriter;
1329 const QString &rewrite = rewriter(v->value.asAST(), v->value.asScript(),
1330 prop->name().toString(),
1331 obj->metatype->signalParameterStringForJS(prop->index, &count),
1333 store.value = output->indexForByteArray(rewrite.toUtf8());
1334 store.parameterCount =
1335 (rewriter.parameterAccess() == QQmlRewrite::RewriteSignalHandler::ParametersUnaccessed) ? 0 : count;
1336 store.context = v->signalExpressionContextStack;
1337 store.line = v->location.start.line;
1338 store.column = v->location.start.column;
1339 output->addInstruction(store);
1345 for (Property *prop = obj->attachedProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1346 Instruction::FetchAttached fetch;
1347 fetch.id = prop->index;
1348 fetch.line = prop->location.start.line;
1349 output->addInstruction(fetch);
1351 genObjectBody(prop->value);
1353 Instruction::PopFetchedObject pop;
1354 output->addInstruction(pop);
1357 for (Property *prop = obj->groupedProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1358 Instruction::FetchObject fetch;
1359 fetch.property = prop->index;
1360 fetch.line = prop->location.start.line;
1361 output->addInstruction(fetch);
1363 if (!prop->value->synthdata.isEmpty()) {
1364 Instruction::StoreMetaObject meta;
1365 meta.aliasData = output->indexForByteArray(prop->value->synthdata);
1366 meta.propertyCache = output->propertyCaches.count();
1367 QQmlPropertyCache *propertyCache = prop->value->synthCache;
1368 Q_ASSERT(propertyCache);
1369 propertyCache->addref();
1370 output->propertyCaches << propertyCache;
1371 output->addInstruction(meta);
1374 genObjectBody(prop->value);
1376 Instruction::PopFetchedObject pop;
1377 output->addInstruction(pop);
1380 for (Property *prop = obj->valueTypeProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1382 genValueTypeProperty(obj, prop);
1385 for (Property *prop = obj->valueProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1386 if (prop->isDeferred)
1389 genValueProperty(prop, obj);
1392 for (Property *prop = obj->valueTypeProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1394 genValueTypeProperty(obj, prop);
1398 void QQmlCompiler::genValueTypeProperty(QQmlScript::Object *obj,QQmlScript::Property *prop)
1400 Instruction::FetchValueType fetch;
1401 fetch.property = prop->index;
1402 fetch.type = prop->type;
1403 fetch.bindingSkipList = 0;
1405 if (obj->type == -1 || output->types.at(obj->type).component) {
1406 // We only have to do this if this is a composite type. If it is a builtin
1407 // type it can't possibly already have bindings that need to be cleared.
1408 for (Property *vprop = prop->value->valueProperties.first(); vprop; vprop = Object::PropertyList::next(vprop)) {
1409 if (!vprop->values.isEmpty()) {
1410 Q_ASSERT(vprop->index >= 0 && vprop->index < 32);
1411 fetch.bindingSkipList |= (1 << vprop->index);
1416 output->addInstruction(fetch);
1418 for (Property *vprop = prop->value->valueProperties.first(); vprop; vprop = Object::PropertyList::next(vprop)) {
1419 genPropertyAssignment(vprop, prop->value, prop);
1422 Instruction::PopValueType pop;
1423 pop.property = prop->index;
1424 pop.type = prop->type;
1425 pop.bindingSkipList = 0;
1426 output->addInstruction(pop);
1428 genPropertyAssignment(prop, obj);
1431 void QQmlCompiler::genComponent(QQmlScript::Object *obj)
1433 QQmlScript::Object *root = obj->defaultProperty->values.first()->object;
1436 Instruction::CreateComponent create;
1437 create.line = root->location.start.line;
1438 create.column = root->location.start.column;
1439 create.endLine = root->location.end.line;
1440 create.isRoot = (compileState->root == obj);
1441 int createInstruction = output->addInstruction(create);
1442 int nextInstructionIndex = output->nextInstructionIndex();
1444 ComponentCompileState *oldCompileState = compileState;
1445 compileState = componentState(root);
1447 Instruction::Init init;
1448 init.bindingsSize = compileState->totalBindingsCount;
1449 init.parserStatusSize = compileState->parserStatusCount;
1450 init.contextCache = genContextCache();
1451 init.objectStackSize = compileState->objectDepth.maxDepth();
1452 init.listStackSize = compileState->listDepth.maxDepth();
1453 if (compileState->compiledBindingData.isEmpty())
1454 init.compiledBinding = -1;
1456 init.compiledBinding = output->indexForByteArray(compileState->compiledBindingData);
1457 output->addInstruction(init);
1459 if (!compileState->v8BindingProgram.isEmpty()) {
1460 Instruction::InitV8Bindings bindings;
1461 int index = output->programs.count();
1463 typedef QQmlCompiledData::V8Program V8Program;
1464 output->programs.append(V8Program(compileState->v8BindingProgram, output));
1466 bindings.programIndex = index;
1467 bindings.line = compileState->v8BindingProgramLine;
1468 output->addInstruction(bindings);
1473 Instruction::SetDefault def;
1474 output->addInstruction(def);
1476 Instruction::Done done;
1477 output->addInstruction(done);
1479 output->instruction(createInstruction)->createComponent.count =
1480 output->nextInstructionIndex() - nextInstructionIndex;
1482 compileState = oldCompileState;
1484 if (!obj->id.isEmpty()) {
1485 Instruction::SetId id;
1486 id.value = output->indexForString(obj->id);
1487 id.index = obj->idIndex;
1488 output->addInstruction(id);
1491 if (obj == unitRoot) {
1492 output->rootPropertyCache = output->types[obj->type].createPropertyCache(engine);
1493 output->rootPropertyCache->addref();
1497 bool QQmlCompiler::buildComponent(QQmlScript::Object *obj,
1498 const BindingContext &ctxt)
1500 // The special "Component" element can only have the id property and a
1501 // default property, that actually defines the component's tree
1503 compileState->objectDepth.push();
1505 // Find, check and set the "id" property (if any)
1506 Property *idProp = 0;
1507 if (obj->properties.isMany() ||
1508 (obj->properties.isOne() && obj->properties.first()->name() != id_string))
1509 COMPILE_EXCEPTION(obj->properties.first(), tr("Component elements may not contain properties other than id"));
1511 if (!obj->properties.isEmpty())
1512 idProp = obj->properties.first();
1515 if (idProp->value || idProp->values.isMany() || idProp->values.first()->object)
1516 COMPILE_EXCEPTION(idProp, tr("Invalid component id specification"));
1517 COMPILE_CHECK(checkValidId(idProp->values.first(), idProp->values.first()->primitive()))
1519 QString idVal = idProp->values.first()->primitive();
1521 if (compileState->ids.value(idVal))
1522 COMPILE_EXCEPTION(idProp, tr("id is not unique"));
1528 // Check the Component tree is well formed
1529 if (obj->defaultProperty &&
1530 (obj->defaultProperty->value || obj->defaultProperty->values.isMany() ||
1531 (obj->defaultProperty->values.isOne() && !obj->defaultProperty->values.first()->object)))
1532 COMPILE_EXCEPTION(obj, tr("Invalid component body specification"));
1534 if (!obj->dynamicProperties.isEmpty())
1535 COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties."));
1536 if (!obj->dynamicSignals.isEmpty())
1537 COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals."));
1538 if (!obj->dynamicSlots.isEmpty())
1539 COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions."));
1541 QQmlScript::Object *root = 0;
1542 if (obj->defaultProperty && !obj->defaultProperty->values.isEmpty())
1543 root = obj->defaultProperty->values.first()->object;
1546 COMPILE_EXCEPTION(obj, tr("Cannot create empty component specification"));
1548 // Build the component tree
1549 COMPILE_CHECK(buildComponentFromRoot(root, ctxt));
1551 compileState->objectDepth.pop();
1556 bool QQmlCompiler::buildComponentFromRoot(QQmlScript::Object *obj,
1557 const BindingContext &ctxt)
1559 ComponentCompileState *oldComponentCompileState = compileState;
1560 compileState = pool->New<ComponentCompileState>();
1561 compileState->root = obj;
1562 compileState->nested = true;
1564 if (componentStats) {
1565 ComponentStat oldComponentStat = componentStats->componentStat;
1567 componentStats->componentStat = ComponentStat();
1568 componentStats->componentStat.lineNumber = obj->location.start.line;
1571 COMPILE_CHECK(buildObject(obj, ctxt));
1573 COMPILE_CHECK(completeComponentBuild());
1575 componentStats->componentStat = oldComponentStat;
1578 COMPILE_CHECK(buildObject(obj, ctxt));
1580 COMPILE_CHECK(completeComponentBuild());
1583 compileState = oldComponentCompileState;
1589 // Build a sub-object. A sub-object is one that was not created directly by
1590 // QML - such as a grouped property object, or an attached object. Sub-object's
1591 // can't have an id, involve a custom parser, have attached properties etc.
1592 bool QQmlCompiler::buildSubObject(QQmlScript::Object *obj, const BindingContext &ctxt)
1594 Q_ASSERT(obj->metatype);
1595 Q_ASSERT(!obj->defaultProperty);
1596 Q_ASSERT(ctxt.isSubContext()); // sub-objects must always be in a binding
1599 for (Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) {
1600 if (isSignalPropertyName(prop->name())) {
1601 COMPILE_CHECK(buildSignal(prop, obj, ctxt));
1603 COMPILE_CHECK(buildProperty(prop, obj, ctxt));
1610 int QQmlCompiler::componentTypeRef()
1612 if (cachedComponentTypeRef == -1) {
1613 QQmlType *t = QQmlMetaType::qmlType(Component_string, Component_module_string, 1, 0);
1614 for (int ii = output->types.count() - 1; ii >= 0; --ii) {
1615 if (output->types.at(ii).type == t) {
1616 cachedComponentTypeRef = ii;
1620 QQmlCompiledData::TypeReference ref;
1622 output->types << ref;
1623 cachedComponentTypeRef = output->types.count() - 1;
1625 return cachedComponentTypeRef;
1628 int QQmlCompiler::translationContextIndex()
1630 if (cachedTranslationContextIndex == -1) {
1631 // This code must match that in the qsTr() implementation
1632 const QString &path = output->name;
1633 int lastSlash = path.lastIndexOf(QLatin1Char('/'));
1634 QString context = (lastSlash > -1) ? path.mid(lastSlash + 1, path.length()-lastSlash-5) :
1636 QByteArray contextUtf8 = context.toUtf8();
1637 cachedTranslationContextIndex = output->indexForByteArray(contextUtf8);
1639 return cachedTranslationContextIndex;
1642 bool QQmlCompiler::buildSignal(QQmlScript::Property *prop, QQmlScript::Object *obj,
1643 const BindingContext &ctxt)
1645 Q_ASSERT(obj->metatype);
1647 const QHashedStringRef &propName = prop->name();
1649 Q_ASSERT(propName.startsWith(on_string));
1650 QString name = propName.mid(2, -1).toString();
1652 // Note that the property name could start with any alpha or '_' or '$' character,
1653 // so we need to do the lower-casing of the first alpha character.
1654 for (int firstAlphaIndex = 0; firstAlphaIndex < name.size(); ++firstAlphaIndex) {
1655 if (name.at(firstAlphaIndex).isUpper()) {
1656 name[firstAlphaIndex] = name.at(firstAlphaIndex).toLower();
1661 bool notInRevision = false;
1663 QQmlPropertyData *sig = signal(obj, QStringRef(&name), ¬InRevision);
1667 if (notInRevision && 0 == property(obj, propName, 0)) {
1668 Q_ASSERT(obj->type != -1);
1669 const QList<QQmlTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes();
1670 const QQmlTypeData::TypeReference &type = resolvedTypes.at(obj->type);
1672 COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(elementName(obj)).arg(prop->name().toString()).arg(type.type->module()).arg(type.majorVersion).arg(type.minorVersion));
1674 COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available due to component versioning.").arg(elementName(obj)).arg(prop->name().toString()));
1678 // If the "on<Signal>" name doesn't resolve into a signal, try it as a
1680 COMPILE_CHECK(buildProperty(prop, obj, ctxt));
1684 if (prop->value || !prop->values.isOne())
1685 COMPILE_EXCEPTION(prop, tr("Incorrectly specified signal assignment"));
1687 prop->index = propertyCacheForObject(obj)->methodIndexToSignalIndex(sig->coreIndex);
1690 obj->addSignalProperty(prop);
1692 if (prop->values.first()->object) {
1693 COMPILE_CHECK(buildObject(prop->values.first()->object, ctxt));
1694 prop->values.first()->type = Value::SignalObject;
1696 prop->values.first()->type = Value::SignalExpression;
1698 if (!prop->values.first()->value.isScript())
1699 COMPILE_EXCEPTION(prop, tr("Cannot assign a value to a signal (expecting a script to be run)"));
1701 QString script = prop->values.first()->value.asScript().trimmed();
1702 if (script.isEmpty())
1703 COMPILE_EXCEPTION(prop, tr("Empty signal assignment"));
1705 //all handlers should be on the original, rather than cloned signals in order
1706 //to ensure all parameters are available (see qqmlboundsignal constructor for more details)
1707 prop->index = obj->metatype->originalClone(prop->index);
1709 QString errorString;
1710 obj->metatype->signalParameterStringForJS(prop->index, 0, &errorString);
1711 if (!errorString.isEmpty())
1712 COMPILE_EXCEPTION(prop, errorString);
1714 prop->values.first()->signalExpressionContextStack = ctxt.stack;
1723 Returns true if (value) property \a prop exists on obj, false otherwise.
1725 bool QQmlCompiler::doesPropertyExist(QQmlScript::Property *prop,
1726 QQmlScript::Object *obj)
1728 if (prop->name().isEmpty())
1730 if(isAttachedPropertyName(prop->name()) || prop->name() == id_string)
1733 return property(obj, prop->name()) != 0;
1736 bool QQmlCompiler::buildProperty(QQmlScript::Property *prop,
1737 QQmlScript::Object *obj,
1738 const BindingContext &ctxt)
1740 if (prop->isEmpty())
1741 COMPILE_EXCEPTION(prop, tr("Empty property assignment"));
1743 if (isAttachedPropertyName(prop->name())) {
1744 // Setup attached property data
1746 if (ctxt.isSubContext()) {
1747 // Attached properties cannot be used on sub-objects. Sub-objects
1748 // always exist in a binding sub-context, which is what we test
1750 COMPILE_EXCEPTION(prop, tr("Attached properties cannot be used here"));
1754 QQmlImportNamespace *typeNamespace = 0;
1755 unit->imports().resolveType(prop->name(), &type, 0, 0, 0, &typeNamespace);
1757 if (typeNamespace) {
1758 COMPILE_CHECK(buildPropertyInNamespace(typeNamespace, prop, obj,
1761 } else if (!type || !type->attachedPropertiesType()) {
1762 COMPILE_EXCEPTION(prop, tr("Non-existent attached object"));
1766 COMPILE_EXCEPTION(prop, tr("Invalid attached object assignment"));
1768 Q_ASSERT(type->attachedPropertiesFunction());
1769 prop->index = type->attachedPropertiesId();
1770 prop->value->metatype = enginePrivate->cache(type->attachedPropertiesType());
1772 // Setup regular property data
1773 bool notInRevision = false;
1774 QQmlPropertyData *d =
1775 prop->name().isEmpty()?0:property(obj, prop->name(), ¬InRevision);
1777 if (d == 0 && notInRevision) {
1778 const QList<QQmlTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes();
1779 const QQmlTypeData::TypeReference &type = resolvedTypes.at(obj->type);
1781 COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(elementName(obj)).arg(prop->name().toString()).arg(type.type->module()).arg(type.majorVersion).arg(type.minorVersion));
1783 COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available due to component versioning.").arg(elementName(obj)).arg(prop->name().toString()));
1786 prop->index = d->coreIndex;
1788 } else if (prop->isDefault) {
1789 QString defaultPropertyName = obj->metatype->defaultPropertyName();
1791 if (!defaultPropertyName.isEmpty()) {
1792 prop->setName(defaultPropertyName);
1793 prop->core = *obj->metatype->defaultProperty();
1794 prop->index = prop->core.coreIndex;
1798 // We can't error here as the "id" property does not require a
1799 // successful index resolution
1800 if (prop->index != -1)
1801 prop->type = prop->core.propType;
1803 // Check if this is an alias
1804 if (prop->index != -1 &&
1806 prop->parent->type != -1 &&
1807 output->types.at(prop->parent->type).component) {
1809 QQmlPropertyCache *cache = output->types.at(prop->parent->type).component->rootPropertyCache;
1810 if (cache && cache->property(prop->index) && cache->property(prop->index)->isAlias())
1811 prop->isAlias = true;
1814 if (prop->index != -1 && !prop->values.isEmpty())
1815 prop->parent->setBindingBit(prop->index);
1818 if (!prop->isDefault && prop->name() == id_string && !ctxt.isSubContext()) {
1820 // The magic "id" behavior doesn't apply when "id" is resolved as a
1821 // default property or to sub-objects (which are always in binding
1823 COMPILE_CHECK(buildIdProperty(prop, obj));
1824 if (prop->type == QVariant::String &&
1825 prop->values.first()->value.isString())
1826 COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt));
1828 } else if (isAttachedPropertyName(prop->name())) {
1830 COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt));
1832 } else if (prop->index == -1) {
1834 if (prop->isDefault) {
1835 COMPILE_EXCEPTION(prop->values.first(), tr("Cannot assign to non-existent default property"));
1837 COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(prop->name().toString()));
1840 } else if (prop->value) {
1842 COMPILE_CHECK(buildGroupedProperty(prop, obj, ctxt));
1844 } else if (prop->core.isQList()) {
1846 COMPILE_CHECK(buildListProperty(prop, obj, ctxt));
1848 } else if (prop->type == qMetaTypeId<QQmlScriptString>()) {
1850 COMPILE_CHECK(buildScriptStringProperty(prop, obj, ctxt));
1854 COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt));
1861 bool QQmlCompiler::buildPropertyInNamespace(QQmlImportNamespace *ns,
1862 QQmlScript::Property *nsProp,
1863 QQmlScript::Object *obj,
1864 const BindingContext &ctxt)
1867 COMPILE_EXCEPTION(nsProp, tr("Invalid use of namespace"));
1869 for (Property *prop = nsProp->value->properties.first(); prop; prop = nsProp->value->properties.next(prop)) {
1871 if (!isAttachedPropertyName(prop->name()))
1872 COMPILE_EXCEPTION(prop, tr("Not an attached property name"));
1874 // Setup attached property data
1877 unit->imports().resolveType(ns, prop->name(), &type, 0, 0, 0);
1879 if (!type || !type->attachedPropertiesType())
1880 COMPILE_EXCEPTION(prop, tr("Non-existent attached object"));
1883 COMPILE_EXCEPTION(prop, tr("Invalid attached object assignment"));
1885 Q_ASSERT(type->attachedPropertiesFunction());
1886 prop->index = type->index();
1887 prop->value->metatype = enginePrivate->cache(type->attachedPropertiesType());
1889 COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt));
1895 void QQmlCompiler::genValueProperty(QQmlScript::Property *prop,
1896 QQmlScript::Object *obj)
1898 if (prop->core.isQList()) {
1899 genListProperty(prop, obj);
1901 genPropertyAssignment(prop, obj);
1905 void QQmlCompiler::genListProperty(QQmlScript::Property *prop,
1906 QQmlScript::Object *obj)
1908 int listType = enginePrivate->listType(prop->type);
1910 Instruction::FetchQList fetch;
1911 fetch.property = prop->index;
1912 bool listTypeIsInterface = QQmlMetaType::isInterface(listType);
1913 fetch.type = listType;
1914 output->addInstruction(fetch);
1916 for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
1918 if (v->type == Value::CreatedObject) {
1920 genObject(v->object);
1921 if (listTypeIsInterface) {
1922 Instruction::AssignObjectList assign;
1923 assign.line = prop->location.start.line;
1924 output->addInstruction(assign);
1926 Instruction::StoreObjectQList store;
1927 output->addInstruction(store);
1930 } else if (v->type == Value::PropertyBinding) {
1932 genBindingAssignment(v, prop, obj);
1938 Instruction::PopQList pop;
1939 output->addInstruction(pop);
1942 void QQmlCompiler::genPropertyAssignment(QQmlScript::Property *prop,
1943 QQmlScript::Object *obj,
1944 QQmlScript::Property *valueTypeProperty)
1946 for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
1948 Q_ASSERT(v->type == Value::CreatedObject ||
1949 v->type == Value::PropertyBinding ||
1950 v->type == Value::Literal);
1952 if (v->type == Value::CreatedObject) {
1954 genObject(v->object);
1956 if (QQmlMetaType::isInterface(prop->type)) {
1958 Instruction::StoreInterface store;
1959 store.line = v->object->location.start.line;
1960 store.propertyIndex = prop->index;
1961 output->addInstruction(store);
1963 } else if (prop->type == QMetaType::QVariant) {
1965 if (prop->core.isVarProperty()) {
1966 Instruction::StoreVarObject store;
1967 store.line = v->object->location.start.line;
1968 store.propertyIndex = prop->index;
1969 output->addInstruction(store);
1971 Instruction::StoreVariantObject store;
1972 store.line = v->object->location.start.line;
1973 store.propertyIndex = prop->index;
1974 output->addInstruction(store);
1980 Instruction::StoreObject store;
1981 store.line = v->object->location.start.line;
1982 store.propertyIndex = prop->index;
1983 output->addInstruction(store);
1986 } else if (v->type == Value::PropertyBinding) {
1988 genBindingAssignment(v, prop, obj, valueTypeProperty);
1990 } else if (v->type == Value::Literal) {
1992 genLiteralAssignment(prop, v);
1998 for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) {
2000 Q_ASSERT(v->type == Value::ValueSource ||
2001 v->type == Value::ValueInterceptor);
2003 if (v->type == Value::ValueSource) {
2004 genObject(v->object, valueTypeProperty?true:false);
2006 Instruction::StoreValueSource store;
2007 if (valueTypeProperty)
2008 store.property = genValueTypeData(prop, valueTypeProperty);
2010 store.property = prop->core;
2011 QQmlType *valueType = toQmlType(v->object);
2012 store.castValue = valueType->propertyValueSourceCast();
2013 output->addInstruction(store);
2015 } else if (v->type == Value::ValueInterceptor) {
2016 genObject(v->object, valueTypeProperty?true:false);
2018 Instruction::StoreValueInterceptor store;
2019 if (valueTypeProperty)
2020 store.property = genValueTypeData(prop, valueTypeProperty);
2022 store.property = prop->core;
2023 QQmlType *valueType = toQmlType(v->object);
2024 store.castValue = valueType->propertyValueInterceptorCast();
2025 output->addInstruction(store);
2031 bool QQmlCompiler::buildIdProperty(QQmlScript::Property *prop,
2032 QQmlScript::Object *obj)
2035 prop->values.isMany() ||
2036 prop->values.first()->object)
2037 COMPILE_EXCEPTION(prop, tr("Invalid use of id property"));
2039 QQmlScript::Value *idValue = prop->values.first();
2040 QString val = idValue->primitive();
2042 COMPILE_CHECK(checkValidId(idValue, val));
2044 if (compileState->ids.value(val))
2045 COMPILE_EXCEPTION(prop, tr("id is not unique"));
2047 prop->values.first()->type = Value::Id;
2055 void QQmlCompiler::addId(const QString &id, QQmlScript::Object *obj)
2058 Q_ASSERT(!compileState->ids.value(id));
2059 Q_ASSERT(obj->id == id);
2060 obj->idIndex = compileState->ids.count();
2061 compileState->ids.append(obj);
2064 void QQmlCompiler::addBindingReference(JSBindingReference *ref)
2066 Q_ASSERT(ref->value && !ref->value->bindingReference);
2067 ref->value->bindingReference = ref;
2068 compileState->totalBindingsCount++;
2069 compileState->bindings.prepend(ref);
2072 void QQmlCompiler::saveComponentState()
2074 Q_ASSERT(compileState->root);
2075 Q_ASSERT(compileState->root->componentCompileState == 0);
2077 compileState->root->componentCompileState = compileState;
2080 componentStats->savedComponentStats.append(componentStats->componentStat);
2083 QQmlCompilerTypes::ComponentCompileState *
2084 QQmlCompiler::componentState(QQmlScript::Object *obj)
2086 Q_ASSERT(obj->componentCompileState);
2087 return obj->componentCompileState;
2090 // Build attached property object. In this example,
2094 // GridView is an attached property object.
2095 bool QQmlCompiler::buildAttachedProperty(QQmlScript::Property *prop,
2096 QQmlScript::Object *obj,
2097 const BindingContext &ctxt)
2099 Q_ASSERT(prop->value);
2100 Q_ASSERT(prop->index != -1); // This is set in buildProperty()
2102 compileState->objectDepth.push();
2104 obj->addAttachedProperty(prop);
2106 COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr()));
2108 compileState->objectDepth.pop();
2114 // Build "grouped" properties. In this example:
2116 // font.pointSize: 12
2117 // font.family: "Helvetica"
2119 // font is a nested property. pointSize and family are not.
2120 bool QQmlCompiler::buildGroupedProperty(QQmlScript::Property *prop,
2121 QQmlScript::Object *obj,
2122 const BindingContext &ctxt)
2124 Q_ASSERT(prop->type != 0);
2125 Q_ASSERT(prop->index != -1);
2127 if (QQmlValueTypeFactory::isValueType(prop->type)) {
2128 QQmlValueType *valueType = QQmlValueTypeFactory::valueType(prop->type);
2129 if (prop->type >= 0 && valueType) {
2130 if (!prop->values.isEmpty()) {
2131 // Only error if we are assigning values, and not e.g. a property interceptor
2132 for (Property *dotProp = prop->value->properties.first(); dotProp; dotProp = prop->value->properties.next(dotProp)) {
2133 if (!dotProp->values.isEmpty()) {
2134 if (prop->values.first()->location < prop->value->location) {
2135 COMPILE_EXCEPTION(prop->value, tr( "Property has already been assigned a value"));
2137 COMPILE_EXCEPTION(prop->values.first(), tr( "Property has already been assigned a value"));
2143 if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration) {
2144 COMPILE_EXCEPTION(prop, tr( "Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
2147 if (prop->isAlias) {
2148 for (Property *vtProp = prop->value->properties.first(); vtProp; vtProp = prop->value->properties.next(vtProp)) {
2149 vtProp->isAlias = true;
2153 COMPILE_CHECK(buildValueTypeProperty(valueType, prop->value, obj, ctxt.incr()));
2155 // When building a value type where sub components are declared, this
2156 // code path is followed from buildProperty, even if there is a previous
2157 // assignment to the value type as a whole. Therefore we need to look
2158 // for (and build) assignments to the entire value type before looking
2159 // for any onValue assignments.
2160 for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
2162 COMPILE_EXCEPTION(v->object, tr("Objects cannot be assigned to value types"));
2164 COMPILE_CHECK(buildPropertyLiteralAssignment(prop, obj, v, ctxt));
2167 for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) {
2168 Q_ASSERT(v->object);
2169 COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, obj, v, ctxt));
2172 obj->addValueTypeProperty(prop);
2174 COMPILE_EXCEPTION(prop, tr("Invalid grouped property access"));
2177 // Load the nested property's meta type
2178 prop->value->metatype = enginePrivate->propertyCacheForType(prop->type);
2179 if (!prop->value->metatype)
2180 COMPILE_EXCEPTION(prop, tr("Invalid grouped property access"));
2182 if (!prop->values.isEmpty())
2183 COMPILE_EXCEPTION(prop->values.first(), tr( "Cannot assign a value directly to a grouped property"));
2185 obj->addGroupedProperty(prop);
2187 compileState->objectDepth.push();
2189 COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr()));
2191 compileState->objectDepth.pop();
2197 bool QQmlCompiler::buildValueTypeProperty(QObject *type,
2198 QQmlScript::Object *obj,
2199 QQmlScript::Object *baseObj,
2200 const BindingContext &ctxt)
2202 compileState->objectDepth.push();
2204 if (obj->defaultProperty)
2205 COMPILE_EXCEPTION(obj, tr("Invalid property use"));
2206 obj->metatype = enginePrivate->cache(type);
2208 for (Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) {
2210 QQmlPropertyData *d = property(obj, prop->name());
2212 COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(prop->name().toString()));
2214 prop->index = d->coreIndex;
2215 prop->type = d->propType;
2217 prop->isValueTypeSubProperty = true;
2220 COMPILE_EXCEPTION(prop, tr("Property assignment expected"));
2222 if (prop->values.isMany()) {
2223 COMPILE_EXCEPTION(prop, tr("Single property assignment expected"));
2224 } else if (!prop->values.isEmpty()) {
2225 QQmlScript::Value *value = prop->values.first();
2227 if (value->object) {
2228 COMPILE_EXCEPTION(prop, tr("Unexpected object assignment"));
2229 } else if (value->value.isScript()) {
2230 // ### Check for writability
2232 //optimization for <Type>.<EnumValue> enum assignments
2233 bool isEnumAssignment = false;
2235 if (prop->core.isEnum() || prop->core.propType == QMetaType::Int)
2236 COMPILE_CHECK(testQualifiedEnumAssignment(prop, obj, value, &isEnumAssignment));
2238 if (isEnumAssignment) {
2239 value->type = Value::Literal;
2241 JSBindingReference *reference = pool->New<JSBindingReference>();
2242 reference->expression = value->value;
2243 reference->property = prop;
2244 reference->value = value;
2245 reference->bindingContext = ctxt;
2246 reference->bindingContext.owner++;
2247 addBindingReference(reference);
2248 value->type = Value::PropertyBinding;
2251 COMPILE_CHECK(testLiteralAssignment(prop, value));
2252 value->type = Value::Literal;
2256 for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) {
2257 Q_ASSERT(v->object);
2259 COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, baseObj, v, ctxt));
2262 obj->addValueProperty(prop);
2265 compileState->objectDepth.pop();
2270 // Build assignments to QML lists. QML lists are properties of type
2271 // QQmlListProperty<T>. List properties can accept a list of
2272 // objects, or a single binding.
2273 bool QQmlCompiler::buildListProperty(QQmlScript::Property *prop,
2274 QQmlScript::Object *obj,
2275 const BindingContext &ctxt)
2277 Q_ASSERT(prop->core.isQList());
2279 compileState->listDepth.push();
2283 obj->addValueProperty(prop);
2285 int listType = enginePrivate->listType(t);
2286 bool listTypeIsInterface = QQmlMetaType::isInterface(listType);
2288 bool assignedBinding = false;
2289 for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
2291 v->type = Value::CreatedObject;
2292 COMPILE_CHECK(buildObject(v->object, ctxt));
2294 // We check object coercian here. We check interface assignment
2296 if (!listTypeIsInterface) {
2297 if (!canCoerce(listType, v->object)) {
2298 COMPILE_EXCEPTION(v, tr("Cannot assign object to list"));
2302 } else if (v->value.isScript()) {
2303 if (assignedBinding)
2304 COMPILE_EXCEPTION(v, tr("Can only assign one binding to lists"));
2306 assignedBinding = true;
2307 COMPILE_CHECK(buildBinding(v, prop, ctxt));
2308 v->type = Value::PropertyBinding;
2310 COMPILE_EXCEPTION(v, tr("Cannot assign primitives to lists"));
2314 compileState->listDepth.pop();
2319 // Compiles an assignment to a QQmlScriptString property
2320 bool QQmlCompiler::buildScriptStringProperty(QQmlScript::Property *prop,
2321 QQmlScript::Object *obj,
2322 const BindingContext &ctxt)
2324 if (prop->values.isMany())
2325 COMPILE_EXCEPTION(prop->values.first()->nextValue, tr( "Cannot assign multiple values to a script property"));
2327 if (prop->values.first()->object)
2328 COMPILE_EXCEPTION(prop->values.first(), tr( "Invalid property assignment: script expected"));
2330 prop->scriptStringScope = ctxt.stack;
2331 obj->addScriptStringProperty(prop);
2336 // Compile regular property assignments of the form "property: <value>"
2337 bool QQmlCompiler::buildPropertyAssignment(QQmlScript::Property *prop,
2338 QQmlScript::Object *obj,
2339 const BindingContext &ctxt)
2341 obj->addValueProperty(prop);
2343 if (prop->values.isMany())
2344 COMPILE_EXCEPTION(prop->values.first(), tr( "Cannot assign multiple values to a singular property") );
2346 for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
2349 COMPILE_CHECK(buildPropertyObjectAssignment(prop, obj, v, ctxt));
2353 COMPILE_CHECK(buildPropertyLiteralAssignment(prop, obj, v, ctxt));
2358 for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) {
2359 Q_ASSERT(v->object);
2360 COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, obj, v, ctxt));
2366 // Compile assigning a single object instance to a regular property
2367 bool QQmlCompiler::buildPropertyObjectAssignment(QQmlScript::Property *prop,
2368 QQmlScript::Object *obj,
2369 QQmlScript::Value *v,
2370 const BindingContext &ctxt)
2372 Q_ASSERT(prop->index != -1);
2373 Q_ASSERT(v->object->type != -1);
2375 if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration)
2376 COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
2378 if (QQmlMetaType::isInterface(prop->type)) {
2380 // Assigning an object to an interface ptr property
2381 COMPILE_CHECK(buildObject(v->object, ctxt));
2383 v->type = Value::CreatedObject;
2385 } else if (prop->type == QMetaType::QVariant) {
2387 // Assigning an object to a QVariant
2388 COMPILE_CHECK(buildObject(v->object, ctxt));
2390 v->type = Value::CreatedObject;
2392 // Normally buildObject() will set this up, but we need the static
2393 // meta object earlier to test for assignability. It doesn't matter
2394 // that there may still be outstanding synthesized meta object changes
2395 // on this type, as they are not relevant for assignability testing
2396 v->object->metatype = output->types[v->object->type].createPropertyCache(engine);
2397 Q_ASSERT(v->object->metatype);
2399 // We want to raw metaObject here as the raw metaobject is the
2400 // actual property type before we applied any extensions that might
2401 // effect the properties on the type, but don't effect assignability
2402 QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(prop->type);
2404 // Will be true if the assgned type inherits propertyMetaObject
2405 bool isAssignable = false;
2406 // Determine isAssignable value
2407 if (propertyMetaObject) {
2408 QQmlPropertyCache *c = v->object->metatype;
2409 while (c && !isAssignable) {
2410 isAssignable |= c == propertyMetaObject;
2416 // Simple assignment
2417 COMPILE_CHECK(buildObject(v->object, ctxt));
2419 v->type = Value::CreatedObject;
2420 } else if (propertyMetaObject && propertyMetaObject->metaObject() == &QQmlComponent::staticMetaObject) {
2421 // Automatic "Component" insertion
2422 QQmlScript::Object *root = v->object;
2423 QQmlScript::Object *component = pool->New<Object>();
2424 component->type = componentTypeRef();
2425 component->metatype = enginePrivate->cache(&QQmlComponent::staticMetaObject);
2426 component->location = root->location;
2427 QQmlScript::Value *componentValue = pool->New<Value>();
2428 componentValue->object = root;
2429 component->getDefaultProperty()->addValue(componentValue);
2430 v->object = component;
2431 COMPILE_CHECK(buildPropertyObjectAssignment(prop, obj, v, ctxt));
2433 COMPILE_EXCEPTION(v->object, tr("Cannot assign object to property"));
2440 // Compile assigning a single object instance to a regular property using the "on" syntax.
2444 // NumberAnimation on x { }
2446 bool QQmlCompiler::buildPropertyOnAssignment(QQmlScript::Property *prop,
2447 QQmlScript::Object *obj,
2448 QQmlScript::Object *baseObj,
2449 QQmlScript::Value *v,
2450 const BindingContext &ctxt)
2452 Q_ASSERT(prop->index != -1);
2453 Q_ASSERT(v->object->type != -1);
2457 if (!prop->core.isWritable())
2458 COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
2461 // Normally buildObject() will set this up, but we need the static
2462 // meta object earlier to test for assignability. It doesn't matter
2463 // that there may still be outstanding synthesized meta object changes
2464 // on this type, as they are not relevant for assignability testing
2465 v->object->metatype = output->types[v->object->type].createPropertyCache(engine);
2466 Q_ASSERT(v->object->metatype);
2468 // Will be true if the assigned type inherits QQmlPropertyValueSource
2469 bool isPropertyValue = false;
2470 // Will be true if the assigned type inherits QQmlPropertyValueInterceptor
2471 bool isPropertyInterceptor = false;
2472 if (QQmlType *valueType = toQmlType(v->object)) {
2473 isPropertyValue = valueType->propertyValueSourceCast() != -1;
2474 isPropertyInterceptor = valueType->propertyValueInterceptorCast() != -1;
2477 if (isPropertyValue || isPropertyInterceptor) {
2478 // Assign as a property value source
2479 COMPILE_CHECK(buildObject(v->object, ctxt));
2481 if (isPropertyInterceptor && baseObj->synthdata.isEmpty())
2482 buildDynamicMeta(baseObj, ForceCreation);
2483 v->type = isPropertyValue ? Value::ValueSource : Value::ValueInterceptor;
2485 COMPILE_EXCEPTION(v, tr("\"%1\" cannot operate on \"%2\"").arg(elementName(v->object)).arg(prop->name().toString()));
2491 // Compile assigning a literal or binding to a regular property
2492 bool QQmlCompiler::buildPropertyLiteralAssignment(QQmlScript::Property *prop,
2493 QQmlScript::Object *obj,
2494 QQmlScript::Value *v,
2495 const BindingContext &ctxt)
2497 Q_ASSERT(prop->index != -1);
2499 if (v->value.isScript()) {
2501 //optimization for <Type>.<EnumValue> enum assignments
2502 if (prop->core.isEnum() || prop->core.propType == QMetaType::Int) {
2503 bool isEnumAssignment = false;
2504 COMPILE_CHECK(testQualifiedEnumAssignment(prop, obj, v, &isEnumAssignment));
2505 if (isEnumAssignment) {
2506 v->type = Value::Literal;
2511 // Test for other binding optimizations
2512 if (!buildLiteralBinding(v, prop, ctxt))
2513 COMPILE_CHECK(buildBinding(v, prop, ctxt));
2515 v->type = Value::PropertyBinding;
2519 COMPILE_CHECK(testLiteralAssignment(prop, v));
2521 v->type = Value::Literal;
2527 struct StaticQtMetaObject : public QObject
2529 static const QMetaObject *get()
2530 { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; }
2533 bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop,
2534 QQmlScript::Object *obj,
2535 QQmlScript::Value *v,
2538 bool isIntProp = (prop->core.propType == QMetaType::Int) && !prop->core.isEnum();
2539 *isAssignment = false;
2540 if (!prop->core.isEnum() && !isIntProp)
2543 QMetaProperty mprop = obj->metatype->firstCppMetaObject()->property(prop->index);
2545 if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration)
2546 COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
2548 QString string = v->value.asString();
2549 if (!string.at(0).isUpper())
2552 int dot = string.indexOf(QLatin1Char('.'));
2553 if (dot == -1 || dot == string.length()-1)
2556 if (string.indexOf(QLatin1Char('.'), dot+1) != -1)
2559 QHashedStringRef typeName(string.constData(), dot);
2560 QString enumValue = string.mid(dot+1);
2563 // Allow enum assignment to ints.
2565 int enumval = evaluateEnum(typeName, enumValue.toUtf8(), &ok);
2567 v->type = Value::Literal;
2568 v->value = QQmlScript::Variant((double)enumval);
2569 *isAssignment = true;
2575 unit->imports().resolveType(typeName, &type, 0, 0, 0, 0);
2577 if (!type && typeName != QLatin1String("Qt"))
2583 if (type && toQmlType(obj) == type) {
2584 // When these two match, we can short cut the search
2585 if (mprop.isFlagType()) {
2586 value = mprop.enumerator().keysToValue(enumValue.toUtf8().constData(), &ok);
2588 value = mprop.enumerator().keyToValue(enumValue.toUtf8().constData(), &ok);
2591 // Otherwise we have to search the whole type
2593 value = type->enumValue(QHashedStringRef(enumValue), &ok);
2595 QByteArray enumName = enumValue.toUtf8();
2596 const QMetaObject *metaObject = StaticQtMetaObject::get();
2597 for (int ii = metaObject->enumeratorCount() - 1; !ok && ii >= 0; --ii) {
2598 QMetaEnum e = metaObject->enumerator(ii);
2599 value = e.keyToValue(enumName.constData(), &ok);
2607 v->type = Value::Literal;
2608 v->value = QQmlScript::Variant((double)value);
2609 *isAssignment = true;
2614 // Similar logic to above, but not knowing target property.
2615 int QQmlCompiler::evaluateEnum(const QHashedStringRef &scope, const QByteArray& enumValue, bool *ok) const
2617 Q_ASSERT_X(ok, "QQmlCompiler::evaluateEnum", "ok must not be a null pointer");
2620 if (scope != QLatin1String("Qt")) {
2622 unit->imports().resolveType(scope, &type, 0, 0, 0, 0);
2623 return type ? type->enumValue(QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1;
2626 const QMetaObject *mo = StaticQtMetaObject::get();
2627 int i = mo->enumeratorCount();
2629 int v = mo->enumerator(i).keyToValue(enumValue.constData(), ok);
2636 const QMetaObject *QQmlCompiler::resolveType(const QString& name) const
2638 QQmlType *qmltype = 0;
2639 if (!unit->imports().resolveType(name, &qmltype, 0, 0, 0, 0))
2643 return qmltype->metaObject();
2646 // similar to logic of completeComponentBuild, but also sticks data
2647 // into primitives at the end
2648 int QQmlCompiler::rewriteBinding(const QQmlScript::Variant& value, const QString& name)
2650 QQmlRewrite::RewriteBinding rewriteBinding;
2651 rewriteBinding.setName(QLatin1Char('$') + name.mid(name.lastIndexOf(QLatin1Char('.')) + 1));
2653 QString rewrite = rewriteBinding(value.asAST(), value.asScript(), 0);
2655 return output->indexForString(rewrite);
2658 QString QQmlCompiler::rewriteSignalHandler(const QQmlScript::Variant& value, const QString &name)
2660 QQmlRewrite::RewriteSignalHandler rewriteSignalHandler;
2661 return rewriteSignalHandler(value.asAST(), value.asScript(), name);
2664 // Ensures that the dynamic meta specification on obj is valid
2665 bool QQmlCompiler::checkDynamicMeta(QQmlScript::Object *obj)
2667 bool seenDefaultProperty = false;
2669 // We use a coarse grain, 31 bit hash to check if there are duplicates.
2670 // Calculating the hash for the names is not a waste as we have to test
2671 // them against the illegalNames set anyway.
2672 QHashField propNames;
2673 QHashField methodNames;
2676 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) {
2677 const QQmlScript::Object::DynamicProperty &prop = *p;
2679 if (prop.isDefaultProperty) {
2680 if (seenDefaultProperty)
2681 COMPILE_EXCEPTION(&prop, tr("Duplicate default property"));
2682 seenDefaultProperty = true;
2685 if (propNames.testAndSet(prop.name.hash())) {
2686 for (Object::DynamicProperty *p2 = obj->dynamicProperties.first(); p2 != p;
2687 p2 = obj->dynamicProperties.next(p2)) {
2688 if (p2->name == prop.name) {
2689 COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line,
2690 prop.nameLocation.column,
2691 tr("Duplicate property name"));
2696 if (prop.name.at(0).isUpper()) {
2697 COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line,
2698 prop.nameLocation.column,
2699 tr("Property names cannot begin with an upper case letter"));
2702 if (enginePrivate->v8engine()->illegalNames().contains(prop.name)) {
2703 COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line,
2704 prop.nameLocation.column,
2705 tr("Illegal property name"));
2709 for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) {
2710 const QQmlScript::Object::DynamicSignal &currSig = *s;
2712 if (methodNames.testAndSet(currSig.name.hash())) {
2713 for (Object::DynamicSignal *s2 = obj->dynamicSignals.first(); s2 != s;
2714 s2 = obj->dynamicSignals.next(s2)) {
2715 if (s2->name == currSig.name)
2716 COMPILE_EXCEPTION(&currSig, tr("Duplicate signal name"));
2720 if (currSig.name.at(0).isUpper())
2721 COMPILE_EXCEPTION(&currSig, tr("Signal names cannot begin with an upper case letter"));
2722 if (enginePrivate->v8engine()->illegalNames().contains(currSig.name))
2723 COMPILE_EXCEPTION(&currSig, tr("Illegal signal name"));
2726 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
2727 const QQmlScript::Object::DynamicSlot &currSlot = *s;
2729 if (methodNames.testAndSet(currSlot.name.hash())) {
2730 for (Object::DynamicSignal *s2 = obj->dynamicSignals.first(); s2;
2731 s2 = obj->dynamicSignals.next(s2)) {
2732 if (s2->name == currSlot.name)
2733 COMPILE_EXCEPTION(&currSlot, tr("Duplicate method name"));
2735 for (Object::DynamicSlot *s2 = obj->dynamicSlots.first(); s2 != s;
2736 s2 = obj->dynamicSlots.next(s2)) {
2737 if (s2->name == currSlot.name)
2738 COMPILE_EXCEPTION(&currSlot, tr("Duplicate method name"));
2742 if (currSlot.name.at(0).isUpper())
2743 COMPILE_EXCEPTION(&currSlot, tr("Method names cannot begin with an upper case letter"));
2744 if (enginePrivate->v8engine()->illegalNames().contains(currSlot.name))
2745 COMPILE_EXCEPTION(&currSlot, tr("Illegal method name"));
2751 bool QQmlCompiler::mergeDynamicMetaProperties(QQmlScript::Object *obj)
2753 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
2754 p = obj->dynamicProperties.next(p)) {
2756 if (!p->defaultValue || p->type == Object::DynamicProperty::Alias)
2759 Property *property = 0;
2760 if (p->isDefaultProperty) {
2761 property = obj->getDefaultProperty();
2763 property = obj->getProperty(p->name);
2764 if (!property->values.isEmpty())
2765 COMPILE_EXCEPTION(property, tr("Property value set multiple times"));
2769 property->isReadOnlyDeclaration = true;
2771 if (property->value)
2772 COMPILE_EXCEPTION(property, tr("Invalid property nesting"));
2774 property->values.append(p->defaultValue->values);
2779 #include <private/qqmljsparser_p.h>
2781 static QStringList astNodeToStringList(QQmlJS::AST::Node *node)
2783 if (node->kind == QQmlJS::AST::Node::Kind_IdentifierExpression) {
2785 static_cast<QQmlJS::AST::IdentifierExpression *>(node)->name.toString();
2786 return QStringList() << name;
2787 } else if (node->kind == QQmlJS::AST::Node::Kind_FieldMemberExpression) {
2788 QQmlJS::AST::FieldMemberExpression *expr = static_cast<QQmlJS::AST::FieldMemberExpression *>(node);
2790 QStringList rv = astNodeToStringList(expr->base);
2793 rv.append(expr->name.toString());
2796 return QStringList();
2799 static QAtomicInt classIndexCounter(0);
2801 bool QQmlCompiler::buildDynamicMeta(QQmlScript::Object *obj, DynamicMetaMode mode)
2804 Q_ASSERT(obj->metatype);
2806 if (mode != ForceCreation &&
2807 obj->dynamicProperties.isEmpty() &&
2808 obj->dynamicSignals.isEmpty() &&
2809 obj->dynamicSlots.isEmpty())
2812 Q_ASSERT(obj->synthCache == 0);
2815 Object::DynamicProperty::Type dtype;
2817 } builtinTypes[] = {
2818 { Object::DynamicProperty::Var, QMetaType::QVariant },
2819 { Object::DynamicProperty::Variant, QMetaType::QVariant },
2820 { Object::DynamicProperty::Int, QMetaType::Int },
2821 { Object::DynamicProperty::Bool, QMetaType::Bool },
2822 { Object::DynamicProperty::Real, QMetaType::Double },
2823 { Object::DynamicProperty::String, QMetaType::QString },
2824 { Object::DynamicProperty::Url, QMetaType::QUrl },
2825 { Object::DynamicProperty::Color, QMetaType::QColor },
2826 { Object::DynamicProperty::Font, QMetaType::QFont },
2827 { Object::DynamicProperty::Time, QMetaType::QTime },
2828 { Object::DynamicProperty::Date, QMetaType::QDate },
2829 { Object::DynamicProperty::DateTime, QMetaType::QDateTime },
2830 { Object::DynamicProperty::Rect, QMetaType::QRectF },
2831 { Object::DynamicProperty::Point, QMetaType::QPointF },
2832 { Object::DynamicProperty::Size, QMetaType::QSizeF },
2833 { Object::DynamicProperty::Vector2D, QMetaType::QVector2D },
2834 { Object::DynamicProperty::Vector3D, QMetaType::QVector3D },
2835 { Object::DynamicProperty::Vector4D, QMetaType::QVector4D },
2836 { Object::DynamicProperty::Matrix4x4, QMetaType::QMatrix4x4 },
2837 { Object::DynamicProperty::Quaternion, QMetaType::QQuaternion }
2839 static const int builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData);
2841 QByteArray newClassName;
2843 if (compileState->root == obj && !compileState->nested) {
2844 QString path = output->url.path();
2845 int lastSlash = path.lastIndexOf(QLatin1Char('/'));
2846 if (lastSlash > -1) {
2847 QString nameBase = path.mid(lastSlash + 1, path.length()-lastSlash-5);
2848 if (!nameBase.isEmpty() && nameBase.at(0).isUpper())
2849 newClassName = nameBase.toUtf8() + "_QMLTYPE_" +
2850 QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
2853 if (newClassName.isEmpty()) {
2854 newClassName = QQmlMetaObject(obj->metatype).className();
2855 newClassName.append("_QML_");
2856 newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)));
2858 QQmlPropertyCache *cache = obj->metatype->copyAndReserve(engine, obj->dynamicProperties.count(),
2859 obj->dynamicProperties.count() +
2860 obj->dynamicSignals.count() +
2861 obj->dynamicSlots.count(),
2862 obj->dynamicProperties.count() +
2863 obj->dynamicSignals.count());
2865 cache->_dynamicClassName = newClassName;
2867 int cStringNameCount = 0;
2870 int varPropCount = 0;
2872 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
2873 p = obj->dynamicProperties.next(p)) {
2875 if (p->type == Object::DynamicProperty::Alias)
2877 else if (p->type == Object::DynamicProperty::Var)
2880 if (p->name.isLatin1()) {
2881 p->nameIndex = cStringNameCount;
2882 cStringNameCount += p->name.length() + 7 /* strlen("Changed") */;
2885 // No point doing this for both the alias and non alias cases
2886 QQmlPropertyData *d = property(obj, p->name);
2887 if (d && d->isFinal())
2888 COMPILE_EXCEPTION(p, tr("Cannot override FINAL property"));
2891 for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) {
2892 if (s->name.isLatin1()) {
2893 s->nameIndex = cStringNameCount;
2894 cStringNameCount += s->name.length();
2898 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
2899 if (s->name.isLatin1()) {
2900 s->nameIndex = cStringNameCount;
2901 cStringNameCount += s->name.length();
2905 char *cStringData = 0;
2906 if (cStringNameCount) {
2907 cache->_dynamicStringData.resize(cStringNameCount);
2908 cStringData = cache->_dynamicStringData.data();
2910 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
2911 p = obj->dynamicProperties.next(p)) {
2913 if (p->nameIndex == -1) continue;
2915 char *myData = cStringData + p->nameIndex;
2916 for (int ii = 0; ii < p->name.length(); ++ii)
2917 *myData++ = p->name.at(ii).unicode();
2918 *myData++ = 'C'; *myData++ = 'h'; *myData++ = 'a'; *myData++ = 'n';
2919 *myData++ = 'g'; *myData++ = 'e'; *myData++ = 'd';
2922 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
2924 if (s->nameIndex == -1) continue;
2926 char *myData = cStringData + s->nameIndex;
2927 for (int ii = 0; ii < s->name.length(); ++ii)
2928 *myData++ = s->name.at(ii).unicode();
2931 for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s;
2932 s = obj->dynamicSignals.next(s)) {
2934 if (s->nameIndex == -1) continue;
2936 char *myData = cStringData + s->nameIndex;
2937 for (int ii = 0; ii < s->name.length(); ++ii)
2938 *myData++ = s->name.at(ii).unicode();
2942 QByteArray dynamicData;
2943 typedef QQmlVMEMetaData VMD;
2945 dynamicData = QByteArray(sizeof(QQmlVMEMetaData) +
2946 obj->dynamicProperties.count() * sizeof(VMD::PropertyData) +
2947 obj->dynamicSlots.count() * sizeof(VMD::MethodData) +
2948 aliasCount * sizeof(VMD::AliasData), 0);
2950 int effectivePropertyIndex = cache->propertyIndexCacheStart;
2951 int effectiveMethodIndex = cache->methodIndexCacheStart;
2953 // For property change signal override detection.
2954 // We prepopulate a set of signal names which already exist in the object,
2955 // and throw an error if there is a signal/method defined as an override.
2956 QSet<QString> seenSignals;
2957 seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged");
2958 QQmlPropertyCache *parentCache = cache;
2959 while ((parentCache = parentCache->parent())) {
2960 if (int pSigCount = parentCache->signalCount()) {
2961 int pSigOffset = parentCache->signalOffset();
2962 for (int i = pSigOffset; i < pSigCount; ++i) {
2963 QQmlPropertyData *currPSig = parentCache->signal(i);
2964 // XXX TODO: find a better way to get signal name from the property data :-/
2965 for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin();
2966 iter != parentCache->stringCache.end(); ++iter) {
2967 if (currPSig == (*iter).second) {
2968 seenSignals.insert(iter.key());
2976 // First set up notify signals for properties - first normal, then var, then alias
2977 enum { NSS_Normal = 0, NSS_Var = 1, NSS_Alias = 2 };
2978 for (int ii = NSS_Normal; ii <= NSS_Alias; ++ii) { // 0 == normal, 1 == var, 2 == alias
2980 if (ii == NSS_Var && varPropCount == 0) continue;
2981 else if (ii == NSS_Alias && aliasCount == 0) continue;
2983 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
2984 p = obj->dynamicProperties.next(p)) {
2986 if ((ii == NSS_Normal && (p->type == Object::DynamicProperty::Alias ||
2987 p->type == Object::DynamicProperty::Var)) ||
2988 ((ii == NSS_Var) && (p->type != Object::DynamicProperty::Var)) ||
2989 ((ii == NSS_Alias) && (p->type != Object::DynamicProperty::Alias)))
2992 quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction |
2993 QQmlPropertyData::IsVMESignal;
2995 QString changedSigName = p->name.toString() + QLatin1String("Changed");
2996 seenSignals.insert(changedSigName);
2998 if (p->nameIndex != -1) {
2999 QHashedCStringRef changedSignalName(cStringData + p->nameIndex,
3000 p->name.length() + 7 /* strlen("Changed") */);
3001 cache->appendSignal(changedSignalName, flags, effectiveMethodIndex++);
3003 cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
3009 for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) {
3010 int paramCount = s->parameterNames.count();
3012 QList<QByteArray> names;
3013 QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0);
3016 paramTypes[0] = paramCount;
3018 for (int i = 0; i < paramCount; ++i) {
3019 if (s->parameterTypes.at(i) < builtinTypeCount) {
3021 paramTypes[i + 1] = builtinTypes[s->parameterTypes.at(i)].metaType;
3022 names.append(s->parameterNames.at(i).toString().toUtf8());
3024 // lazily resolved type
3025 Q_ASSERT(s->parameterTypes.at(i) == Object::DynamicProperty::Custom);
3026 QQmlType *qmltype = 0;
3028 if (!unit->imports().resolveType(s->parameterTypeNames.at(i).toString(), &qmltype, &url, 0, 0, 0))
3029 COMPILE_EXCEPTION(s, tr("Invalid signal parameter type: %1").arg(s->parameterTypeNames.at(i).toString()));
3032 QQmlTypeData *tdata = enginePrivate->typeLoader.getType(QUrl(url));
3034 Q_ASSERT(tdata->isComplete());
3036 QQmlCompiledData *data = tdata->compiledData();
3038 paramTypes[i + 1] = data->metaTypeId;
3042 paramTypes[i + 1] = qmltype->typeId();
3044 names.append(s->parameterNames.at(i).toString().toUtf8());
3049 ((QQmlVMEMetaData *)dynamicData.data())->signalCount++;
3051 quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction |
3052 QQmlPropertyData::IsVMESignal;
3054 flags |= QQmlPropertyData::HasArguments;
3056 QString signalName = s->name.toString();
3057 if (seenSignals.contains(signalName)) {
3058 const QQmlScript::Object::DynamicSignal &currSig = *s;
3059 COMPILE_EXCEPTION(&currSig, tr("Duplicate signal name: invalid override of property change signal or superclass signal"));
3061 seenSignals.insert(signalName);
3063 if (s->nameIndex != -1) {
3064 QHashedCStringRef name(cStringData + s->nameIndex, s->name.length(), s->name.hash());
3065 cache->appendSignal(name, flags, effectiveMethodIndex++,
3066 paramCount?paramTypes.constData():0, names);
3068 cache->appendSignal(signalName, flags, effectiveMethodIndex++,
3069 paramCount?paramTypes.constData():0, names);
3075 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
3076 int paramCount = s->parameterNames.count();
3078 quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction;
3081 flags |= QQmlPropertyData::HasArguments;
3083 QString slotName = s->name.toString();
3084 if (seenSignals.contains(slotName)) {
3085 const QQmlScript::Object::DynamicSlot &currSlot = *s;
3086 COMPILE_EXCEPTION(&currSlot, tr("Duplicate method name: invalid override of property change signal or superclass signal"));
3088 // Note: we don't append slotName to the seenSignals list, since we don't
3089 // protect against overriding change signals or methods with properties.
3091 if (s->nameIndex != -1) {
3092 QHashedCStringRef name(cStringData + s->nameIndex, s->name.length(), s->name.hash());
3093 cache->appendMethod(name, flags, effectiveMethodIndex++, s->parameterNames);
3095 cache->appendMethod(slotName, flags, effectiveMethodIndex++, s->parameterNames);
3100 // Dynamic properties (except var and aliases)
3101 int effectiveSignalIndex = cache->signalHandlerIndexCacheStart;
3102 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
3103 p = obj->dynamicProperties.next(p)) {
3105 if (p->type == Object::DynamicProperty::Alias ||
3106 p->type == Object::DynamicProperty::Var)
3109 int propertyType = 0;
3110 int vmePropertyType = 0;
3111 quint32 propertyFlags = 0;
3113 if (p->type < builtinTypeCount) {
3114 propertyType = builtinTypes[p->type].metaType;
3115 vmePropertyType = propertyType;
3117 if (p->type == Object::DynamicProperty::Variant)
3118 propertyFlags |= QQmlPropertyData::IsQVariant;
3120 Q_ASSERT(p->type == Object::DynamicProperty::CustomList ||
3121 p->type == Object::DynamicProperty::Custom);
3123 QQmlType *qmltype = 0;
3125 if (!unit->imports().resolveType(p->customType.toString(), &qmltype, &url, 0, 0, 0))
3126 COMPILE_EXCEPTION(p, tr("Invalid property type"));
3129 QQmlTypeData *tdata = enginePrivate->typeLoader.getType(QUrl(url));
3131 Q_ASSERT(tdata->isComplete());
3133 QQmlCompiledData *data = tdata->compiledData();
3135 if (p->type == Object::DynamicProperty::Custom) {
3136 propertyType = data->metaTypeId;
3137 vmePropertyType = QMetaType::QObjectStar;
3139 propertyType = data->listMetaTypeId;
3140 vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >();
3145 if (p->type == Object::DynamicProperty::Custom) {
3146 propertyType = qmltype->typeId();
3147 vmePropertyType = QMetaType::QObjectStar;
3149 propertyType = qmltype->qListTypeId();
3150 vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >();
3154 if (p->type == Object::DynamicProperty::Custom)
3155 propertyFlags |= QQmlPropertyData::IsQObjectDerived;
3157 propertyFlags |= QQmlPropertyData::IsQList;
3160 if (!p->isReadOnly && p->type != Object::DynamicProperty::CustomList)
3161 propertyFlags |= QQmlPropertyData::IsWritable;
3163 if (p->nameIndex != -1) {
3164 QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(),
3166 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16();
3167 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3168 propertyType, effectiveSignalIndex);
3170 QString propertyName = p->name.toString();
3171 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName;
3172 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3173 propertyType, effectiveSignalIndex);
3176 effectiveSignalIndex++;
3178 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3179 (vmd->propertyData() + vmd->propertyCount)->propertyType = vmePropertyType;
3180 vmd->propertyCount++;
3183 // Now do var properties
3184 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p && varPropCount;
3185 p = obj->dynamicProperties.next(p)) {
3187 if (p->type != Object::DynamicProperty::Var)
3190 quint32 propertyFlags = QQmlPropertyData::IsVarProperty;
3192 propertyFlags |= QQmlPropertyData::IsWritable;
3194 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3195 (vmd->propertyData() + vmd->propertyCount)->propertyType = QMetaType::QVariant;
3196 vmd->propertyCount++;
3197 ((QQmlVMEMetaData *)dynamicData.data())->varPropertyCount++;
3199 if (p->nameIndex != -1) {
3200 QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(),
3202 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16();
3203 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3204 QMetaType::QVariant, effectiveSignalIndex);
3206 QString propertyName = p->name.toString();
3207 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName;
3208 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3209 QMetaType::QVariant, effectiveSignalIndex);
3212 effectiveSignalIndex++;
3215 // Alias property count. Actual data is setup in buildDynamicMetaAliases
3216 ((QQmlVMEMetaData *)dynamicData.data())->aliasCount = aliasCount;
3218 // Dynamic slot data - comes after the property data
3219 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
3220 int paramCount = s->parameterNames.count();
3224 if (paramCount) namesSize += s->parameterNamesLength() + (paramCount - 1 /* commas */);
3225 funcScript.reserve(strlen("(function ") + s->name.length() + 1 /* lparen */ +
3226 namesSize + 1 /* rparen */ + s->body.length() + 1 /* rparen */);
3227 funcScript = QLatin1String("(function ") + s->name.toString() + QLatin1Char('(');
3228 for (int jj = 0; jj < paramCount; ++jj) {
3229 if (jj) funcScript.append(QLatin1Char(','));
3230 funcScript.append(QLatin1String(s->parameterNames.at(jj)));
3232 funcScript += QLatin1Char(')') + s->body + QLatin1Char(')');
3234 QByteArray utf8 = funcScript.toUtf8();
3235 VMD::MethodData methodData = { s->parameterNames.count(),
3238 s->location.start.line };
3240 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3241 VMD::MethodData &md = *(vmd->methodData() + vmd->methodCount);
3245 dynamicData.append((const char *)utf8.constData(), utf8.length());
3249 compileState->aliasingObjects.append(obj);
3251 obj->synthdata = dynamicData;
3252 obj->synthCache = cache;
3253 obj->metatype = cache;
3258 bool QQmlCompiler::buildDynamicMetaAliases(QQmlScript::Object *obj)
3260 Q_ASSERT(obj->synthCache);
3262 QByteArray &dynamicData = obj->synthdata;
3264 QQmlPropertyCache *cache = obj->synthCache;
3265 char *cStringData = cache->_dynamicStringData.data();
3267 int effectiveSignalIndex = cache->signalHandlerIndexCacheStart + cache->propertyIndexCache.count();
3268 int effectivePropertyIndex = cache->propertyIndexCacheStart + cache->propertyIndexCache.count();
3269 int effectiveAliasIndex = 0;
3271 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
3272 p = obj->dynamicProperties.next(p)) {
3274 if (p->type != Object::DynamicProperty::Alias)
3277 if (!p->defaultValue)
3278 COMPILE_EXCEPTION(obj, tr("No property alias location"));
3280 if (!p->defaultValue->values.isOne() ||
3281 p->defaultValue->values.first()->object ||
3282 !p->defaultValue->values.first()->value.isScript())
3283 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3285 QQmlJS::AST::Node *node = p->defaultValue->values.first()->value.asAST();
3288 QStringList alias = astNodeToStringList(node);
3289 if (alias.count() < 1 || alias.count() > 3)
3290 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
3292 QQmlScript::Object *idObject = compileState->ids.value(alias.at(0));
3294 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias reference. Unable to find id \"%1\"").arg(alias.at(0)));
3298 int notifySignal = -1;
3301 bool writable = false;
3302 bool resettable = false;
3304 quint32 propertyFlags = QQmlPropertyData::IsAlias;
3306 if (alias.count() == 2 || alias.count() == 3) {
3307 QQmlPropertyData *property = this->property(idObject, alias.at(1));
3309 if (!property || property->coreIndex > 0x0000FFFF)
3310 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3312 propIdx = property->coreIndex;
3313 type = property->propType;
3315 writable = property->isWritable();
3316 resettable = property->isResettable();
3317 notifySignal = property->notifyIndex;
3319 if (alias.count() == 3) {
3320 QQmlValueType *valueType = QQmlValueTypeFactory::valueType(type);
3322 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3326 int valueTypeIndex =
3327 valueType->metaObject()->indexOfProperty(alias.at(2).toUtf8().constData());
3328 if (valueTypeIndex == -1)
3329 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3330 Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
3332 propIdx |= (valueTypeIndex << 16);
3333 if (valueType->metaObject()->property(valueTypeIndex).isEnumType())
3334 type = QVariant::Int;
3336 type = valueType->metaObject()->property(valueTypeIndex).userType();
3339 if (property->isEnum()) {
3340 type = QVariant::Int;
3343 propertyFlags |= property->getFlags() & QQmlPropertyData::PropTypeFlagMask;
3345 if (property->isVarProperty())
3346 propertyFlags |= QQmlPropertyData::IsQVariant;
3348 if (property->isQObject())
3349 flags |= QML_ALIAS_FLAG_PTR;
3353 Q_ASSERT(idObject->type != -1); // How else did it get an id?
3355 const QQmlCompiledData::TypeReference &ref = output->types.at(idObject->type);
3357 type = ref.type->typeId();
3359 type = ref.component->metaTypeId;
3361 flags |= QML_ALIAS_FLAG_PTR;
3362 propertyFlags |= QQmlPropertyData::IsQObjectDerived;
3365 QQmlVMEMetaData::AliasData aliasData = { idObject->idIndex, propIdx, propType, flags, notifySignal };
3367 typedef QQmlVMEMetaData VMD;
3368 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3369 *(vmd->aliasData() + effectiveAliasIndex++) = aliasData;
3371 if (!p->isReadOnly && writable)
3372 propertyFlags |= QQmlPropertyData::IsWritable;
3374 propertyFlags &= ~QQmlPropertyData::IsWritable;
3377 propertyFlags |= QQmlPropertyData::IsResettable;
3379 propertyFlags &= ~QQmlPropertyData::IsResettable;
3381 if (p->nameIndex != -1) {
3382 QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(),
3384 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16();
3385 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3386 type, effectiveSignalIndex++);
3388 QString propertyName = p->name.toString();
3389 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName;
3390 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3391 type, effectiveSignalIndex++);
3398 bool QQmlCompiler::checkValidId(QQmlScript::Value *v, const QString &val)
3401 COMPILE_EXCEPTION(v, tr( "Invalid empty ID"));
3403 QChar ch = val.at(0);
3404 if (ch.isLetter() && !ch.isLower())
3405 COMPILE_EXCEPTION(v, tr( "IDs cannot start with an uppercase letter"));
3407 QChar u(QLatin1Char('_'));
3408 if (!ch.isLetter() && ch != u)
3409 COMPILE_EXCEPTION(v, tr( "IDs must start with a letter or underscore"));
3411 for (int ii = 1; ii < val.count(); ++ii) {
3413 if (!ch.isLetterOrNumber() && ch != u)
3414 COMPILE_EXCEPTION(v, tr( "IDs must contain only letters, numbers, and underscores"));
3417 if (enginePrivate->v8engine()->illegalNames().contains(val))
3418 COMPILE_EXCEPTION(v, tr( "ID illegally masks global JavaScript property"));
3423 bool QQmlCompiler::buildBinding(QQmlScript::Value *value,
3424 QQmlScript::Property *prop,
3425 const BindingContext &ctxt)
3427 Q_ASSERT(prop->index != -1);
3428 Q_ASSERT(prop->parent);
3429 Q_ASSERT(prop->parent->metatype);
3431 if (!prop->core.isWritable() && !prop->core.isQList() && !prop->isReadOnlyDeclaration)
3432 COMPILE_EXCEPTION(prop, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
3434 JSBindingReference *reference = pool->New<JSBindingReference>();
3435 reference->expression = value->value;
3436 reference->property = prop;
3437 reference->value = value;
3438 reference->bindingContext = ctxt;
3439 addBindingReference(reference);
3444 bool QQmlCompiler::buildLiteralBinding(QQmlScript::Value *v,
3445 QQmlScript::Property *prop,
3446 const QQmlCompilerTypes::BindingContext &)
3448 Q_ASSERT(v->value.isScript());
3450 if (!prop->core.isWritable())
3453 AST::Node *binding = v->value.asAST();
3455 if (prop->type == QVariant::String) {
3456 if (AST::CallExpression *e = AST::cast<AST::CallExpression *>(binding)) {
3457 if (AST::IdentifierExpression *i = AST::cast<AST::IdentifierExpression *>(e->base)) {
3458 if (i->name == qsTrId_string) {
3459 AST::ArgumentList *arg1 = e->arguments?e->arguments:0;
3460 AST::ArgumentList *arg2 = arg1?arg1->next:0;
3462 if (arg1 && arg1->expression->kind == AST::Node::Kind_StringLiteral &&
3463 (!arg2 || arg2->expression->kind == AST::Node::Kind_NumericLiteral) &&
3464 (!arg2 || !arg2->next)) {
3469 text = AST::cast<AST::StringLiteral *>(arg1->expression)->value;
3470 if (arg2) n = (int)AST::cast<AST::NumericLiteral *>(arg2->expression)->value;
3472 TrBindingReference *reference = pool->New<TrBindingReference>();
3473 reference->dataType = BindingReference::TrId;
3474 reference->text = text;
3476 v->bindingReference = reference;
3480 } else if (i->name == qsTr_string) {
3482 AST::ArgumentList *arg1 = e->arguments?e->arguments:0;
3483 AST::ArgumentList *arg2 = arg1?arg1->next:0;
3484 AST::ArgumentList *arg3 = arg2?arg2->next:0;
3486 if (arg1 && arg1->expression->kind == AST::Node::Kind_StringLiteral &&
3487 (!arg2 || arg2->expression->kind == AST::Node::Kind_StringLiteral) &&
3488 (!arg3 || arg3->expression->kind == AST::Node::Kind_NumericLiteral) &&
3489 (!arg3 || !arg3->next)) {
3495 text = AST::cast<AST::StringLiteral *>(arg1->expression)->value;
3496 if (arg2) comment = AST::cast<AST::StringLiteral *>(arg2->expression)->value;
3497 if (arg3) n = (int)AST::cast<AST::NumericLiteral *>(arg3->expression)->value;
3499 TrBindingReference *reference = pool->New<TrBindingReference>();
3500 reference->dataType = BindingReference::Tr;
3501 reference->text = text;
3502 reference->comment = comment;
3504 v->bindingReference = reference;
3517 void QQmlCompiler::genBindingAssignment(QQmlScript::Value *binding,
3518 QQmlScript::Property *prop,
3519 QQmlScript::Object *obj,
3520 QQmlScript::Property *valueTypeProperty)
3523 Q_ASSERT(binding->bindingReference);
3525 const BindingReference &ref = *binding->bindingReference;
3526 #ifndef QT_NO_TRANSLATION
3527 if (ref.dataType == BindingReference::TrId) {
3528 const TrBindingReference &tr = static_cast<const TrBindingReference &>(ref);
3530 Instruction::StoreTrIdString store;
3531 store.propertyIndex = prop->core.coreIndex;
3532 store.text = output->indexForByteArray(tr.text.toUtf8());
3534 output->addInstruction(store);
3535 } else if (ref.dataType == BindingReference::Tr) {
3536 const TrBindingReference &tr = static_cast<const TrBindingReference &>(ref);
3538 Instruction::StoreTrString store;
3539 store.propertyIndex = prop->core.coreIndex;
3540 store.context = translationContextIndex();
3541 store.text = output->indexForByteArray(tr.text.toUtf8());
3542 store.comment = output->indexForByteArray(tr.comment.toUtf8());
3544 output->addInstruction(store);
3547 if (ref.dataType == BindingReference::V4) {
3548 const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);
3550 Instruction::StoreV4Binding store;
3551 store.value = js.compiledIndex;
3552 store.fallbackValue = js.sharedIndex;
3553 store.context = js.bindingContext.stack;
3554 store.owner = js.bindingContext.owner;
3555 store.isAlias = prop->isAlias;
3556 if (valueTypeProperty) {
3557 store.property = ((prop->index << 16) | valueTypeProperty->index);
3558 store.propType = valueTypeProperty->type;
3559 store.isRoot = (compileState->root == valueTypeProperty->parent);
3561 store.property = prop->index;
3563 store.isRoot = (compileState->root == obj);
3565 store.line = binding->location.start.line;
3566 store.column = binding->location.start.column;
3567 output->addInstruction(store);
3569 if (store.fallbackValue > -1) {
3570 //also create v8 instruction (needed to properly configure the fallback v8 binding)
3571 JSBindingReference &js = static_cast<JSBindingReference &>(*binding->bindingReference);
3572 js.dataType = BindingReference::V8;
3573 genBindingAssignment(binding, prop, obj, valueTypeProperty);
3575 } else if (ref.dataType == BindingReference::V8) {
3576 const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);
3578 Instruction::StoreV8Binding store;
3579 store.value = js.sharedIndex;
3580 store.context = js.bindingContext.stack;
3581 store.owner = js.bindingContext.owner;
3582 store.isAlias = prop->isAlias;
3583 store.isSafe = js.isSafe;
3584 if (valueTypeProperty) {
3585 store.isRoot = (compileState->root == valueTypeProperty->parent);
3587 store.isRoot = (compileState->root == obj);
3589 store.isFallback = js.compiledIndex > -1;
3590 store.line = binding->location.start.line;
3591 store.column = binding->location.start.column;
3593 Q_ASSERT(js.bindingContext.owner == 0 ||
3594 (js.bindingContext.owner != 0 && valueTypeProperty));
3595 if (js.bindingContext.owner) {
3596 store.property = genValueTypeData(prop, valueTypeProperty);
3598 store.property = prop->core;
3601 output->addInstruction(store);
3602 } else if (ref.dataType == BindingReference::QtScript) {
3603 const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);
3605 Instruction::StoreBinding store;
3606 store.value = output->indexForString(js.rewrittenExpression);
3607 store.context = js.bindingContext.stack;
3608 store.owner = js.bindingContext.owner;
3609 store.line = binding->location.start.line;
3610 store.column = binding->location.start.column;
3611 store.isAlias = prop->isAlias;
3613 if (valueTypeProperty) {
3614 store.isRoot = (compileState->root == valueTypeProperty->parent);
3616 store.isRoot = (compileState->root == obj);
3618 store.isFallback = false;
3620 Q_ASSERT(js.bindingContext.owner == 0 ||
3621 (js.bindingContext.owner != 0 && valueTypeProperty));
3622 if (js.bindingContext.owner) {
3623 store.property = genValueTypeData(prop, valueTypeProperty);
3625 store.property = prop->core;
3628 output->addInstruction(store);
3630 Q_ASSERT(!"Unhandled BindingReference::DataType type");
3634 int QQmlCompiler::genContextCache()
3636 if (compileState->ids.count() == 0)
3639 QQmlIntegerCache *cache = new QQmlIntegerCache();
3640 cache->reserve(compileState->ids.count());
3641 for (Object *o = compileState->ids.first(); o; o = compileState->ids.next(o))
3642 cache->add(o->id, o->idIndex);
3644 output->contextCaches.append(cache);
3645 return output->contextCaches.count() - 1;
3649 QQmlCompiler::genValueTypeData(QQmlScript::Property *valueTypeProp,
3650 QQmlScript::Property *prop)
3652 QQmlValueType *vt = QQmlValueTypeFactory::valueType(prop->type);
3654 return QQmlPropertyPrivate::saveValueType(prop->core, vt->metaObject(), valueTypeProp->index, engine);
3657 bool QQmlCompiler::completeComponentBuild()
3660 componentStats->componentStat.ids = compileState->ids.count();
3662 for (Object *aliasObject = compileState->aliasingObjects.first(); aliasObject;
3663 aliasObject = compileState->aliasingObjects.next(aliasObject))
3664 COMPILE_CHECK(buildDynamicMetaAliases(aliasObject));
3666 QV4Compiler::Expression expr(unit->imports());
3667 expr.component = compileState->root;
3668 expr.ids = &compileState->ids;
3669 expr.importCache = output->importCache;
3671 QV4Compiler bindingCompiler;
3673 QList<JSBindingReference*> sharedBindings;
3675 for (JSBindingReference *b = compileState->bindings.first(); b; b = b->nextReference) {
3677 JSBindingReference &binding = *b;
3680 expr.context = binding.bindingContext.object;
3681 expr.property = binding.property;
3682 expr.expression = binding.expression;
3684 bool needsFallback = false;
3685 int index = bindingCompiler.compile(expr, enginePrivate, &needsFallback);
3687 binding.dataType = BindingReference::V4;
3688 binding.compiledIndex = index;
3689 binding.sharedIndex = -1;
3691 componentStats->componentStat.optimizedBindings.append(b->value->location);
3696 // Drop through. We need to create a V8 binding in case the V4 binding is invalidated
3699 // Pre-rewrite the expression
3700 QString expression = binding.expression.asScript();
3702 QQmlRewrite::RewriteBinding rewriteBinding;
3703 rewriteBinding.setName(QLatin1Char('$')+binding.property->name().toString());
3704 bool isSharable = false;
3705 bool isSafe = false;
3706 binding.rewrittenExpression = rewriteBinding(binding.expression.asAST(), expression, &isSharable, &isSafe);
3707 binding.isSafe = isSafe;
3709 if (isSharable && binding.property->type != qMetaTypeId<QQmlBinding*>()) {
3710 sharedBindings.append(b);
3712 if (!needsFallback) {
3713 binding.dataType = BindingReference::V8;
3714 binding.compiledIndex = -1;
3717 componentStats->componentStat.sharedBindings.append(b->value->location);
3720 Q_ASSERT(!needsFallback);
3721 binding.dataType = BindingReference::QtScript;
3724 componentStats->componentStat.scriptBindings.append(b->value->location);
3728 if (!sharedBindings.isEmpty()) {
3730 static bool lt(const JSBindingReference *lhs, const JSBindingReference *rhs)
3732 return lhs->value->location.start.line < rhs->value->location.start.line;
3736 qSort(sharedBindings.begin(), sharedBindings.end(), Sort::lt);
3738 int startLineNumber = sharedBindings.at(0)->value->location.start.line;
3739 int lineNumber = startLineNumber;
3741 QByteArray functionArray("[", 1);
3742 for (int ii = 0; ii < sharedBindings.count(); ++ii) {
3744 JSBindingReference *reference = sharedBindings.at(ii);
3745 QQmlScript::Value *value = reference->value;
3746 const QString &expression = reference->rewrittenExpression;
3748 if (ii != 0) functionArray.append(",", 1);
3750 while (lineNumber < value->location.start.line) {
3752 functionArray.append("\n", 1);
3755 functionArray += expression.toUtf8();
3756 lineNumber += expression.count(QLatin1Char('\n'));
3758 reference->sharedIndex = ii;
3760 functionArray.append("]", 1);
3762 compileState->v8BindingProgram = functionArray;
3763 compileState->v8BindingProgramLine = startLineNumber;
3766 if (bindingCompiler.isValid())
3767 compileState->compiledBindingData = bindingCompiler.program();
3769 // Check pop()'s matched push()'s
3770 Q_ASSERT(compileState->objectDepth.depth() == 0);
3771 Q_ASSERT(compileState->listDepth.depth() == 0);
3773 saveComponentState();
3778 void QQmlCompiler::dumpStats()
3780 Q_ASSERT(componentStats);
3781 qWarning().nospace() << "QML Document: " << output->url.toString();
3782 for (int ii = 0; ii < componentStats->savedComponentStats.count(); ++ii) {
3783 const ComponentStat &stat = componentStats->savedComponentStats.at(ii);
3784 qWarning().nospace() << " Component Line " << stat.lineNumber;
3785 qWarning().nospace() << " Total Objects: " << stat.objects;
3786 qWarning().nospace() << " IDs Used: " << stat.ids;
3787 qWarning().nospace() << " Optimized Bindings: " << stat.optimizedBindings.count();
3791 for (int ii = 0; ii < stat.optimizedBindings.count(); ++ii) {
3792 if (0 == (ii % 10)) {
3793 if (ii) output.append("\n");
3798 output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.line));
3800 output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.column));
3801 output.append(") ");
3803 if (!output.isEmpty())
3804 qWarning().nospace() << output.constData();
3807 qWarning().nospace() << " Shared Bindings: " << stat.sharedBindings.count();
3810 for (int ii = 0; ii < stat.sharedBindings.count(); ++ii) {
3811 if (0 == (ii % 10)) {
3812 if (ii) output.append('\n');
3817 output.append(QByteArray::number(stat.sharedBindings.at(ii).start.line));
3819 output.append(QByteArray::number(stat.sharedBindings.at(ii).start.column));
3820 output.append(") ");
3822 if (!output.isEmpty())
3823 qWarning().nospace() << output.constData();
3826 qWarning().nospace() << " QScript Bindings: " << stat.scriptBindings.count();
3829 for (int ii = 0; ii < stat.scriptBindings.count(); ++ii) {
3830 if (0 == (ii % 10)) {
3831 if (ii) output.append('\n');
3836 output.append(QByteArray::number(stat.scriptBindings.at(ii).start.line));
3838 output.append(QByteArray::number(stat.scriptBindings.at(ii).start.column));
3839 output.append(") ");
3841 if (!output.isEmpty())
3842 qWarning().nospace() << output.constData();
3848 Returns true if from can be assigned to a (QObject) property of type
3851 bool QQmlCompiler::canCoerce(int to, QQmlScript::Object *from)
3853 QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to);
3854 QQmlPropertyCache *fromMo = from->metatype;
3859 fromMo = fromMo->parent();
3865 Returns the element name, as written in the QML file, for o.
3867 QString QQmlCompiler::elementName(QQmlScript::Object *o)
3870 if (o->type != -1) {
3871 return unit->parser().referencedTypes().at(o->type)->name;
3877 QQmlType *QQmlCompiler::toQmlType(QQmlScript::Object *from)
3879 if (from->type != -1 && output->types.at(from->type).type)
3880 return output->types.at(from->type).type;
3882 const QMetaObject *mo = from->metatype->firstCppMetaObject();
3884 while (!type && mo) {
3885 type = QQmlMetaType::qmlType(mo);
3886 mo = mo->superClass();
3891 QStringList QQmlCompiler::deferredProperties(QQmlScript::Object *obj)
3893 const QMetaObject *mo = obj->metatype->firstCppMetaObject();
3895 int idx = mo->indexOfClassInfo("DeferredPropertyNames");
3897 return QStringList();
3899 QMetaClassInfo classInfo = mo->classInfo(idx);
3900 QStringList rv = QString::fromUtf8(classInfo.value()).split(QLatin1Char(','));
3905 QQmlCompiler::propertyCacheForObject(QQmlScript::Object *object)
3907 if (object->synthCache)
3908 return object->synthCache;
3909 else if (object->type != -1)
3910 return output->types[object->type].createPropertyCache(engine);
3912 return object->metatype;
3916 QQmlCompiler::property(QQmlScript::Object *object, int index)
3918 QQmlPropertyCache *cache = propertyCacheForObject(object);
3920 return cache->property(index);
3924 QQmlCompiler::property(QQmlScript::Object *object, const QHashedStringRef &name, bool *notInRevision)
3926 if (notInRevision) *notInRevision = false;
3928 QQmlPropertyCache *cache = propertyCacheForObject(object);
3930 QQmlPropertyData *d = cache->property(name, 0, 0);
3932 // Find the first property
3933 while (d && d->isFunction())
3934 d = cache->overrideData(d);
3936 if (d && !cache->isAllowedInRevision(d)) {
3937 if (notInRevision) *notInRevision = true;
3944 // This code must match the semantics of QQmlPropertyPrivate::findSignalByName
3946 QQmlCompiler::signal(QQmlScript::Object *object, const QHashedStringRef &name, bool *notInRevision)
3948 if (notInRevision) *notInRevision = false;
3950 QQmlPropertyCache *cache = propertyCacheForObject(object);
3953 QQmlPropertyData *d = cache->property(name, 0, 0);
3954 if (notInRevision) *notInRevision = false;
3956 while (d && !(d->isFunction()))
3957 d = cache->overrideData(d);
3959 if (d && !cache->isAllowedInRevision(d)) {
3960 if (notInRevision) *notInRevision = true;
3962 } else if (d && d->isSignal()) {
3966 if (name.endsWith(Changed_string)) {
3967 QHashedStringRef propName = name.mid(0, name.length() - Changed_string.length());
3969 d = property(object, propName, notInRevision);
3971 return cache->signal(d->notifyIndex);
3977 // This code must match the semantics of QQmlPropertyPrivate::findSignalByName
3978 int QQmlCompiler::indexOfSignal(QQmlScript::Object *object, const QString &name,
3979 bool *notInRevision)
3981 QQmlPropertyData *d = signal(object, QStringRef(&name), notInRevision);
3982 return d?d->coreIndex:-1;
3985 int QQmlCompiler::indexOfProperty(QQmlScript::Object *object, const QString &name,
3986 bool *notInRevision)
3988 return indexOfProperty(object, QStringRef(&name), notInRevision);
3991 int QQmlCompiler::indexOfProperty(QQmlScript::Object *object, const QHashedStringRef &name,
3992 bool *notInRevision)
3994 QQmlPropertyData *d = property(object, name, notInRevision);
3995 return d?d->coreIndex:-1;