1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
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>)
79 Q_DECLARE_METATYPE(QJSValue)
83 DEFINE_BOOL_CONFIG_OPTION(compilerDump, QML_COMPILER_DUMP);
84 DEFINE_BOOL_CONFIG_OPTION(compilerStatDump, QML_COMPILER_STATS);
86 using namespace QQmlJS;
87 using namespace QQmlScript;
88 using namespace QQmlCompilerTypes;
90 static QString id_string(QLatin1String("id"));
91 static QString on_string(QLatin1String("on"));
92 static QString Changed_string(QLatin1String("Changed"));
93 static QString Component_string(QLatin1String("Component"));
94 static QString Component_module_string(QLatin1String("QML"));
95 static QString qsTr_string(QLatin1String("qsTr"));
96 static QString qsTrId_string(QLatin1String("qsTrId"));
99 Instantiate a new QQmlCompiler.
101 QQmlCompiler::QQmlCompiler(QQmlPool *pool)
102 : compileState(0), pool(pool), output(0), engine(0), enginePrivate(0), unitRoot(0), unit(0), cachedComponentTypeRef(-1),
103 cachedTranslationContextIndex(-1), componentStats(0)
105 if (compilerStatDump())
106 componentStats = pool->New<ComponentStats>();
110 Returns true if the last call to compile() caused errors.
114 bool QQmlCompiler::isError() const
116 return !exceptions.isEmpty();
120 Return the list of errors from the last call to compile(), or an empty list
121 if there were no errors.
123 QList<QQmlError> QQmlCompiler::errors() const
129 Returns true if \a name refers to an attached property, false otherwise.
131 Attached property names are those that start with a capital letter.
133 bool QQmlCompiler::isAttachedPropertyName(const QString &name)
135 return isAttachedPropertyName(QHashedStringRef(&name));
138 bool QQmlCompiler::isAttachedPropertyName(const QHashedStringRef &name)
140 return !name.isEmpty() && name.at(0).isUpper();
144 Returns true if \a name refers to a signal property, false otherwise.
146 Signal property names are those that start with "on", followed by a first
147 character which is either a capital letter or one or more underscores followed
148 by a capital letter, which is then followed by other allowed characters.
150 Note that although ECMA-262r3 supports dollarsigns and escaped unicode
151 character codes in property names, for simplicity and performance reasons
152 QML only supports letters, numbers and underscores.
154 bool QQmlCompiler::isSignalPropertyName(const QString &name)
156 return isSignalPropertyName(QStringRef(&name));
159 bool QQmlCompiler::isSignalPropertyName(const QHashedStringRef &name)
161 if (name.length() < 3) return false;
162 if (!name.startsWith(on_string)) return false;
163 int ns = name.length();
164 for (int i = 2; i < ns; ++i) {
165 const QChar curr = name.at(i);
166 if (curr.unicode() == '_') continue;
167 if (curr.isUpper()) return true;
170 return false; // consists solely of underscores - invalid.
174 \macro COMPILE_EXCEPTION
176 Inserts an error into the QQmlCompiler error list, and returns false
179 \a token is used to source the error line and column, and \a desc is the
180 error itself. \a desc can be an expression that can be piped into QDebug.
185 COMPILE_EXCEPTION(property, tr("Error for property \"%1\"").arg(property->name));
188 #define COMPILE_EXCEPTION_LOCATION(line, column, desc) \
191 error.setUrl(output->url); \
192 error.setLine(line); \
193 error.setColumn(column); \
194 error.setDescription(desc.trimmed()); \
195 exceptions << error; \
199 #define COMPILE_EXCEPTION(token, desc) \
200 COMPILE_EXCEPTION_LOCATION((token)->location.start.line, (token)->location.start.column, desc)
205 Returns false if \a is false, otherwise does nothing.
207 #define COMPILE_CHECK(a) \
209 if (!a) return false; \
213 Returns true if literal \a v can be assigned to property \a prop, otherwise
216 This test corresponds to action taken by genLiteralAssignment(). Any change
217 made here, must have a corresponding action in genLiteralAssigment().
219 bool QQmlCompiler::testLiteralAssignment(QQmlScript::Property *prop, QQmlScript::Value *v)
221 const QQmlScript::Variant &value = v->value;
223 if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration)
224 COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
226 if (prop->core.isEnum()) {
227 QMetaProperty p = prop->parent->metatype->firstCppMetaObject()->property(prop->index);
230 if (p.isFlagType()) {
231 enumValue = p.enumerator().keysToValue(value.asString().toUtf8().constData(), &ok);
233 enumValue = p.enumerator().keyToValue(value.asString().toUtf8().constData(), &ok);
236 COMPILE_EXCEPTION(v, tr("Invalid property assignment: unknown enumeration"));
238 v->value = QQmlScript::Variant((double)enumValue);
242 int type = prop->type;
245 case QMetaType::QVariant:
247 case QVariant::String:
248 if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: string expected"));
250 case QVariant::StringList: // we expect a string literal. A string list is not a literal assignment.
251 if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: string or string list expected"));
253 case QVariant::ByteArray:
254 if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: byte array expected"));
257 if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: url expected"));
259 case QVariant::RegExp:
260 COMPILE_EXCEPTION(v, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax"));
264 bool ok = v->value.isNumber();
266 double n = v->value.asNumber();
267 if (double(uint(n)) != n)
270 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: unsigned int expected"));
275 bool ok = v->value.isNumber();
277 double n = v->value.asNumber();
278 if (double(int(n)) != n)
281 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: int expected"));
284 case QMetaType::Float:
285 if (!v->value.isNumber()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: number expected"));
287 case QVariant::Double:
288 if (!v->value.isNumber()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: number expected"));
290 case QVariant::Color:
293 QQmlStringConverters::colorFromString(value.asString(), &ok);
294 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: color expected"));
297 #ifndef QT_NO_DATESTRING
301 QQmlStringConverters::dateFromString(value.asString(), &ok);
302 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: date expected"));
308 QQmlStringConverters::timeFromString(value.asString(), &ok);
309 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: time expected"));
312 case QVariant::DateTime:
315 QQmlStringConverters::dateTimeFromString(value.asString(), &ok);
316 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: datetime expected"));
319 #endif // QT_NO_DATESTRING
320 case QVariant::Point:
321 case QVariant::PointF:
324 QQmlStringConverters::pointFFromString(value.asString(), &ok);
325 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: point expected"));
329 case QVariant::SizeF:
332 QQmlStringConverters::sizeFFromString(value.asString(), &ok);
333 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: size expected"));
337 case QVariant::RectF:
340 QQmlStringConverters::rectFFromString(value.asString(), &ok);
341 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: rect expected"));
346 if (!v->value.isBoolean()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: boolean expected"));
349 case QVariant::Vector3D:
351 QQmlInstruction::instr_storeVector3D::QVector3D v3;
352 if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, value.asString(), &v3, sizeof(v3)))
353 COMPILE_EXCEPTION(v, tr("Invalid property assignment: 3D vector expected"));
356 case QVariant::Vector4D:
358 QQmlInstruction::instr_storeVector4D::QVector4D v4;
359 if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, value.asString(), &v4, sizeof(v4)))
360 COMPILE_EXCEPTION(v, tr("Invalid property assignment: 4D vector expected"));
365 // check if assigning a literal value to a list property.
366 // in each case, check the singular, since an Array of the specified type
367 // will not go via this literal assignment codepath.
368 if (type == qMetaTypeId<QList<qreal> >()) {
369 if (!v->value.isNumber()) {
370 COMPILE_EXCEPTION(v, tr("Invalid property assignment: real or array of reals expected"));
373 } else if (type == qMetaTypeId<QList<int> >()) {
374 bool ok = v->value.isNumber();
376 double n = v->value.asNumber();
377 if (double(int(n)) != n)
380 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: int or array of ints expected"));
382 } else if (type == qMetaTypeId<QList<bool> >()) {
383 if (!v->value.isBoolean()) {
384 COMPILE_EXCEPTION(v, tr("Invalid property assignment: bool or array of bools expected"));
387 } else if (type == qMetaTypeId<QList<QString> >()) { // we expect a string literal. A string list is not a literal assignment.
388 if (!v->value.isString()) {
389 COMPILE_EXCEPTION(v, tr("Invalid property assignment: string or array of strings expected"));
392 } else if (type == qMetaTypeId<QList<QUrl> >()) {
393 if (!v->value.isString()) {
394 COMPILE_EXCEPTION(v, tr("Invalid property assignment: url or array of urls expected"));
397 } else if (type == qMetaTypeId<QJSValue>()) {
401 // otherwise, check for existence of string converter to custom type
402 QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(type);
404 COMPILE_EXCEPTION(v, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(type))));
412 Generate a store instruction for assigning literal \a v to property \a prop.
414 Any literal assignment that is approved in testLiteralAssignment() must have
415 a corresponding action in this method.
417 void QQmlCompiler::genLiteralAssignment(QQmlScript::Property *prop,
418 QQmlScript::Value *v)
420 if (prop->core.isEnum()) {
421 Q_ASSERT(v->value.isNumber());
423 int value = (int)v->value.asNumber();
425 Instruction::StoreInteger instr;
426 instr.propertyIndex = prop->index;
428 output->addInstruction(instr);
432 int type = prop->type;
434 case QMetaType::QVariant:
436 if (v->value.isNumber()) {
437 double n = v->value.asNumber();
438 if (double(int(n)) == n) {
439 if (prop->core.isVarProperty()) {
440 Instruction::StoreVarInteger instr;
441 instr.propertyIndex = prop->index;
442 instr.value = int(n);
443 output->addInstruction(instr);
445 Instruction::StoreVariantInteger instr;
446 instr.propertyIndex = prop->index;
447 instr.value = int(n);
448 output->addInstruction(instr);
451 if (prop->core.isVarProperty()) {
452 Instruction::StoreVarDouble instr;
453 instr.propertyIndex = prop->index;
455 output->addInstruction(instr);
457 Instruction::StoreVariantDouble instr;
458 instr.propertyIndex = prop->index;
460 output->addInstruction(instr);
463 } else if (v->value.isBoolean()) {
464 if (prop->core.isVarProperty()) {
465 Instruction::StoreVarBool instr;
466 instr.propertyIndex = prop->index;
467 instr.value = v->value.asBoolean();
468 output->addInstruction(instr);
470 Instruction::StoreVariantBool instr;
471 instr.propertyIndex = prop->index;
472 instr.value = v->value.asBoolean();
473 output->addInstruction(instr);
476 if (prop->core.isVarProperty()) {
477 Instruction::StoreVar instr;
478 instr.propertyIndex = prop->index;
479 instr.value = output->indexForString(v->value.asString());
480 output->addInstruction(instr);
482 Instruction::StoreVariant instr;
483 instr.propertyIndex = prop->index;
484 instr.value = output->indexForString(v->value.asString());
485 output->addInstruction(instr);
490 case QVariant::String:
492 Instruction::StoreString instr;
493 instr.propertyIndex = prop->index;
494 instr.value = output->indexForString(v->value.asString());
495 output->addInstruction(instr);
498 case QVariant::StringList:
500 Instruction::StoreStringList instr;
501 instr.propertyIndex = prop->index;
502 instr.value = output->indexForString(v->value.asString());
503 output->addInstruction(instr);
506 case QVariant::ByteArray:
508 Instruction::StoreByteArray instr;
509 instr.propertyIndex = prop->index;
510 instr.value = output->indexForByteArray(v->value.asString().toLatin1());
511 output->addInstruction(instr);
516 Instruction::StoreUrl instr;
517 QString string = v->value.asString();
518 // Encoded dir-separators defeat QUrl processing - decode them first
519 string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive);
520 QUrl u = string.isEmpty() ? QUrl() : output->url.resolved(QUrl(string));
521 instr.propertyIndex = prop->index;
522 instr.value = output->indexForUrl(u);
523 output->addInstruction(instr);
528 Instruction::StoreInteger instr;
529 instr.propertyIndex = prop->index;
530 instr.value = uint(v->value.asNumber());
531 output->addInstruction(instr);
536 Instruction::StoreInteger instr;
537 instr.propertyIndex = prop->index;
538 instr.value = int(v->value.asNumber());
539 output->addInstruction(instr);
542 case QMetaType::Float:
544 Instruction::StoreFloat instr;
545 instr.propertyIndex = prop->index;
546 instr.value = float(v->value.asNumber());
547 output->addInstruction(instr);
550 case QVariant::Double:
552 Instruction::StoreDouble instr;
553 instr.propertyIndex = prop->index;
554 instr.value = v->value.asNumber();
555 output->addInstruction(instr);
558 case QVariant::Color:
560 Instruction::StoreColor instr;
561 instr.propertyIndex = prop->index;
562 instr.value = QQmlStringConverters::rgbaFromString(v->value.asString());
563 output->addInstruction(instr);
566 #ifndef QT_NO_DATESTRING
569 Instruction::StoreDate instr;
570 QDate d = QQmlStringConverters::dateFromString(v->value.asString());
571 instr.propertyIndex = prop->index;
572 instr.value = d.toJulianDay();
573 output->addInstruction(instr);
578 Instruction::StoreTime instr;
579 QTime time = QQmlStringConverters::timeFromString(v->value.asString());
580 instr.propertyIndex = prop->index;
581 Q_ASSERT(sizeof(instr.time) == sizeof(QTime));
582 ::memcpy(&instr.time, &time, sizeof(QTime));
583 output->addInstruction(instr);
586 case QVariant::DateTime:
588 Instruction::StoreDateTime instr;
589 QDateTime dateTime = QQmlStringConverters::dateTimeFromString(v->value.asString());
590 QTime time = dateTime.time();
591 instr.propertyIndex = prop->index;
592 instr.date = dateTime.date().toJulianDay();
593 Q_ASSERT(sizeof(instr.time) == sizeof(QTime));
594 ::memcpy(&instr.time, &time, sizeof(QTime));
595 output->addInstruction(instr);
598 #endif // QT_NO_DATESTRING
599 case QVariant::Point:
601 Instruction::StorePoint instr;
603 QPoint point = QQmlStringConverters::pointFFromString(v->value.asString(), &ok).toPoint();
604 instr.propertyIndex = prop->index;
605 instr.point.xp = point.x();
606 instr.point.yp = point.y();
607 output->addInstruction(instr);
610 case QVariant::PointF:
612 Instruction::StorePointF instr;
614 QPointF point = QQmlStringConverters::pointFFromString(v->value.asString(), &ok);
615 instr.propertyIndex = prop->index;
616 instr.point.xp = point.x();
617 instr.point.yp = point.y();
618 output->addInstruction(instr);
623 Instruction::StoreSize instr;
625 QSize size = QQmlStringConverters::sizeFFromString(v->value.asString(), &ok).toSize();
626 instr.propertyIndex = prop->index;
627 instr.size.wd = size.width();
628 instr.size.ht = size.height();
629 output->addInstruction(instr);
632 case QVariant::SizeF:
634 Instruction::StoreSizeF instr;
636 QSizeF size = QQmlStringConverters::sizeFFromString(v->value.asString(), &ok);
637 instr.propertyIndex = prop->index;
638 instr.size.wd = size.width();
639 instr.size.ht = size.height();
640 output->addInstruction(instr);
645 Instruction::StoreRect instr;
647 QRect rect = QQmlStringConverters::rectFFromString(v->value.asString(), &ok).toRect();
648 instr.propertyIndex = prop->index;
649 instr.rect.x1 = rect.left();
650 instr.rect.y1 = rect.top();
651 instr.rect.x2 = rect.right();
652 instr.rect.y2 = rect.bottom();
653 output->addInstruction(instr);
656 case QVariant::RectF:
658 Instruction::StoreRectF instr;
660 QRectF rect = QQmlStringConverters::rectFFromString(v->value.asString(), &ok);
661 instr.propertyIndex = prop->index;
662 instr.rect.xp = rect.left();
663 instr.rect.yp = rect.top();
664 instr.rect.w = rect.width();
665 instr.rect.h = rect.height();
666 output->addInstruction(instr);
671 Instruction::StoreBool instr;
672 bool b = v->value.asBoolean();
673 instr.propertyIndex = prop->index;
675 output->addInstruction(instr);
678 case QVariant::Vector3D:
680 Instruction::StoreVector3D instr;
681 instr.propertyIndex = prop->index;
682 QQmlStringConverters::createFromString(QMetaType::QVector3D, v->value.asString(), &instr.vector, sizeof(instr.vector));
683 output->addInstruction(instr);
686 case QVariant::Vector4D:
688 Instruction::StoreVector4D instr;
689 instr.propertyIndex = prop->index;
690 QQmlStringConverters::createFromString(QMetaType::QVector4D, v->value.asString(), &instr.vector, sizeof(instr.vector));
691 output->addInstruction(instr);
696 // generate single literal value assignment to a list property if required
697 if (type == qMetaTypeId<QList<qreal> >()) {
698 Instruction::StoreDoubleQList instr;
699 instr.propertyIndex = prop->index;
700 instr.value = v->value.asNumber();
701 output->addInstruction(instr);
703 } else if (type == qMetaTypeId<QList<int> >()) {
704 Instruction::StoreIntegerQList instr;
705 instr.propertyIndex = prop->index;
706 instr.value = int(v->value.asNumber());
707 output->addInstruction(instr);
709 } else if (type == qMetaTypeId<QList<bool> >()) {
710 Instruction::StoreBoolQList instr;
711 bool b = v->value.asBoolean();
712 instr.propertyIndex = prop->index;
714 output->addInstruction(instr);
716 } else if (type == qMetaTypeId<QList<QUrl> >()) {
717 Instruction::StoreUrlQList instr;
718 QString string = v->value.asString();
719 QUrl u = string.isEmpty() ? QUrl() : output->url.resolved(QUrl(string));
720 instr.propertyIndex = prop->index;
721 instr.value = output->indexForUrl(u);
722 output->addInstruction(instr);
724 } else if (type == qMetaTypeId<QList<QString> >()) {
725 Instruction::StoreStringQList instr;
726 instr.propertyIndex = prop->index;
727 instr.value = output->indexForString(v->value.asString());
728 output->addInstruction(instr);
730 } else if (type == qMetaTypeId<QJSValue>()) {
731 if (v->value.isBoolean()) {
732 Instruction::StoreJSValueBool instr;
733 instr.propertyIndex = prop->index;
734 instr.value = v->value.asBoolean();
735 output->addInstruction(instr);
736 } else if (v->value.isNumber()) {
737 double n = v->value.asNumber();
738 if (double(int(n)) == n) {
739 Instruction::StoreJSValueInteger instr;
740 instr.propertyIndex = prop->index;
741 instr.value = int(n);
742 output->addInstruction(instr);
744 Instruction::StoreJSValueDouble instr;
745 instr.propertyIndex = prop->index;
747 output->addInstruction(instr);
750 Instruction::StoreJSValueString instr;
751 instr.propertyIndex = prop->index;
752 instr.value = output->indexForString(v->value.asString());
753 output->addInstruction(instr);
758 // otherwise, generate custom type literal assignment
759 Instruction::AssignCustomType instr;
760 instr.propertyIndex = prop->index;
761 instr.primitive = output->indexForString(v->value.asString());
763 output->addInstruction(instr);
770 Resets data by clearing the lists that the QQmlCompiler modifies.
772 void QQmlCompiler::reset(QQmlCompiledData *data)
775 data->primitives.clear();
777 data->bytecode.resize(0);
781 Compile \a unit, and store the output in \a out. \a engine is the QQmlEngine
782 with which the QQmlCompiledData will be associated.
784 Returns true on success, false on failure. On failure, the compile errors
785 are available from errors().
787 If the environment variant QML_COMPILER_DUMP is set
788 (eg. QML_COMPILER_DUMP=1) the compiled instructions will be dumped to stderr
789 on a successful compiler.
791 bool QQmlCompiler::compile(QQmlEngine *engine,
793 QQmlCompiledData *out)
800 QQmlScript::Object *root = unit->parser().tree();
803 this->engine = engine;
804 this->enginePrivate = QQmlEnginePrivate::get(engine);
806 this->unitRoot = root;
810 const QList<QQmlTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes();
811 QList<QQmlScript::TypeReference *> referencedTypes = unit->parser().referencedTypes();
813 for (int ii = 0; ii < resolvedTypes.count(); ++ii) {
814 QQmlCompiledData::TypeReference ref;
816 const QQmlTypeData::TypeReference &tref = resolvedTypes.at(ii);
817 QQmlScript::TypeReference *parserRef = referencedTypes.at(ii);
820 ref.type = tref.type;
821 if (!ref.type->isCreatable()) {
822 QString err = ref.type->noCreationReason();
824 err = tr( "Element is not creatable.");
825 COMPILE_EXCEPTION(parserRef->firstUse, err);
828 if (ref.type->containsRevisionedAttributes()) {
829 QQmlError cacheError;
830 ref.typePropertyCache = enginePrivate->cache(ref.type,
831 resolvedTypes.at(ii).minorVersion,
833 if (!ref.typePropertyCache)
834 COMPILE_EXCEPTION(parserRef->firstUse, cacheError.description());
835 ref.typePropertyCache->addref();
838 } else if (tref.typeData) {
839 ref.component = tref.typeData->compiledData();
840 ref.component->addref();
849 out->dumpInstructions();
852 Q_ASSERT(out->rootPropertyCache);
860 this->enginePrivate = 0;
862 this->cachedComponentTypeRef = -1;
863 this->cachedTranslationContextIndex = -1;
869 void QQmlCompiler::compileTree(QQmlScript::Object *tree)
871 compileState = pool->New<ComponentCompileState>();
873 compileState->root = tree;
875 componentStats->componentStat.lineNumber = tree->location.start.line;
877 // We generate the importCache before we build the tree so that
878 // it can be used in the binding compiler. Given we "expect" the
879 // QML compilation to succeed, this isn't a waste.
880 output->importCache = new QQmlTypeNameCache();
881 foreach (const QString &ns, unit->namespaces()) {
882 output->importCache->add(ns);
886 foreach (const QQmlTypeData::ScriptReference &script, unit->resolvedScripts()) {
887 QString qualifier = script.qualifier;
888 QString enclosingNamespace;
890 const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.'));
891 if (lastDotIndex != -1) {
892 enclosingNamespace = qualifier.left(lastDotIndex);
893 qualifier = qualifier.mid(lastDotIndex+1);
896 output->importCache->add(qualifier, scriptIndex++, enclosingNamespace);
899 unit->imports().populateCache(output->importCache, engine);
901 if (!buildObject(tree, BindingContext()) || !completeComponentBuild())
904 Instruction::Init init;
905 init.bindingsSize = compileState->totalBindingsCount;
906 init.parserStatusSize = compileState->parserStatusCount;
907 init.contextCache = genContextCache();
908 init.objectStackSize = compileState->objectDepth.maxDepth();
909 init.listStackSize = compileState->listDepth.maxDepth();
910 if (compileState->compiledBindingData.isEmpty())
911 init.compiledBinding = -1;
913 init.compiledBinding = output->indexForByteArray(compileState->compiledBindingData);
914 output->addInstruction(init);
916 foreach (const QQmlTypeData::ScriptReference &script, unit->resolvedScripts()) {
917 Instruction::StoreImportedScript import;
918 import.value = output->scripts.count();
920 QQmlScriptData *scriptData = script.script->scriptData();
921 scriptData->addref();
922 output->scripts << scriptData;
923 output->addInstruction(import);
926 if (!compileState->v8BindingProgram.isEmpty()) {
927 Instruction::InitV8Bindings bindings;
928 int index = output->programs.count();
930 typedef QQmlCompiledData::V8Program V8Program;
931 output->programs.append(V8Program(compileState->v8BindingProgram, output));
933 bindings.programIndex = index;
934 bindings.line = compileState->v8BindingProgramLine;
935 output->addInstruction(bindings);
940 Instruction::SetDefault def;
941 output->addInstruction(def);
943 Instruction::Done done;
944 output->addInstruction(done);
946 Q_ASSERT(tree->metatype);
947 if (!tree->synthdata.isEmpty()) {
948 enginePrivate->registerCompositeType(output);
949 } else if (output->types.at(tree->type).component) {
950 output->metaTypeId = output->types.at(tree->type).component->metaTypeId;
951 output->listMetaTypeId = output->types.at(tree->type).component->listMetaTypeId;
953 Q_ASSERT(output->types.at(tree->type).type);
954 output->metaTypeId = output->types.at(tree->type).type->typeId();
955 output->listMetaTypeId = output->types.at(tree->type).type->qListTypeId();
957 if (!tree->synthdata.isEmpty())
958 enginePrivate->registerCompositeType(output);
961 static bool QStringList_contains(const QStringList &list, const QHashedStringRef &string)
963 for (int ii = 0; ii < list.count(); ++ii)
964 if (string == list.at(ii))
970 bool QQmlCompiler::buildObject(QQmlScript::Object *obj, const BindingContext &ctxt)
973 componentStats->componentStat.objects++;
975 Q_ASSERT (obj->type != -1);
976 QQmlCompiledData::TypeReference &tr = output->types[obj->type];
977 obj->metatype = tr.createPropertyCache(engine);
979 // This object is a "Component" element.
980 if (tr.type && obj->metatype->metaObject() == &QQmlComponent::staticMetaObject) {
981 COMPILE_CHECK(buildComponent(obj, ctxt));
986 typedef QQmlInstruction I;
987 const I *init = ((const I *)tr.component->bytecode.constData());
988 Q_ASSERT(init && tr.component->instructionType(init) == QQmlInstruction::Init);
990 // Adjust stack depths to include nested components
991 compileState->objectDepth.pushPop(init->init.objectStackSize);
992 compileState->listDepth.pushPop(init->init.listStackSize);
993 compileState->parserStatusCount += init->init.parserStatusSize;
994 compileState->totalBindingsCount += init->init.bindingsSize;
997 compileState->objectDepth.push();
999 // Object instantiations reset the binding context
1000 BindingContext objCtxt(obj);
1002 // Create the synthesized meta object, ignoring aliases
1003 COMPILE_CHECK(checkDynamicMeta(obj));
1004 COMPILE_CHECK(mergeDynamicMetaProperties(obj));
1005 COMPILE_CHECK(buildDynamicMeta(obj, Normal));
1007 // Find the native type and check for the QQmlParserStatus interface
1008 QQmlType *type = toQmlType(obj);
1010 obj->parserStatusCast = type->parserStatusCast();
1011 if (obj->parserStatusCast != -1)
1012 compileState->parserStatusCount++;
1014 // Check if this is a custom parser type. Custom parser types allow
1015 // assignments to non-existent properties. These assignments are then
1016 // compiled by the type.
1017 bool isCustomParser = output->types.at(obj->type).type &&
1018 output->types.at(obj->type).type->customParser() != 0;
1019 QList<QQmlCustomParserProperty> customProps;
1021 // Fetch the list of deferred properties
1022 QStringList deferredList = deferredProperties(obj);
1024 // Must do id property first. This is to ensure that the id given to any
1025 // id reference created matches the order in which the objects are
1027 for (Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) {
1028 if (prop->name() == id_string) {
1029 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
1035 Property *defaultProperty = 0;
1036 Property *skipProperty = 0;
1037 if (obj->defaultProperty) {
1038 defaultProperty = obj->defaultProperty;
1040 Property *explicitProperty = 0;
1042 QString defaultPropertyName = obj->metatype->defaultPropertyName();
1043 if (!defaultPropertyName.isEmpty()) {
1044 QString *s = pool->NewString(defaultPropertyName);
1045 QHashedStringRef r(*s);
1047 if (obj->propertiesHashField.test(r.hash())) {
1048 for (Property *ep = obj->properties.first(); ep; ep = obj->properties.next(ep)) {
1049 if (ep->name() == r) {
1050 explicitProperty = ep;
1056 if (!explicitProperty)
1057 defaultProperty->setName(r);
1060 if (explicitProperty && !explicitProperty->value && !explicitProperty->values.isEmpty()) {
1062 skipProperty = explicitProperty; // We merge the values into defaultProperty
1064 // Find the correct insertion point
1065 Value *insertPos = 0;
1067 for (Value *v = defaultProperty->values.first(); v; v = Property::ValueList::next(v)) {
1068 if (!(v->location.start < explicitProperty->values.first()->location.start))
1073 defaultProperty->values.insertAfter(insertPos, explicitProperty->values);
1077 QQmlCustomParser *cp = 0;
1079 cp = output->types.at(obj->type).type->customParser();
1081 // Build all explicit properties specified
1082 for (Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) {
1084 if (prop == skipProperty)
1086 if (prop->name() == id_string)
1089 bool canDefer = false;
1090 if (isCustomParser) {
1091 if (doesPropertyExist(prop, obj) &&
1092 (!(cp->flags() & QQmlCustomParser::AcceptsAttachedProperties) ||
1093 !isAttachedPropertyName(prop->name()))) {
1094 int ids = compileState->ids.count();
1095 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
1096 canDefer = ids == compileState->ids.count();
1097 } else if (isSignalPropertyName(prop->name()) &&
1098 (cp->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
1099 COMPILE_CHECK(buildSignal(prop,obj,objCtxt));
1101 customProps << QQmlCustomParserNodePrivate::fromProperty(prop);
1104 if (isSignalPropertyName(prop->name())) {
1105 COMPILE_CHECK(buildSignal(prop,obj,objCtxt));
1107 int ids = compileState->ids.count();
1108 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
1109 canDefer = ids == compileState->ids.count();
1113 if (canDefer && !deferredList.isEmpty() && QStringList_contains(deferredList, prop->name()))
1114 prop->isDeferred = true;
1118 // Build the default property
1119 if (defaultProperty) {
1120 Property *prop = defaultProperty;
1122 bool canDefer = false;
1123 if (isCustomParser) {
1124 if (doesPropertyExist(prop, obj)) {
1125 int ids = compileState->ids.count();
1126 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
1127 canDefer = ids == compileState->ids.count();
1129 customProps << QQmlCustomParserNodePrivate::fromProperty(prop);
1132 int ids = compileState->ids.count();
1133 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
1134 canDefer = ids == compileState->ids.count();
1137 if (canDefer && !deferredList.isEmpty() && QStringList_contains(deferredList, prop->name()))
1138 prop->isDeferred = true;
1141 // Compile custom parser parts
1142 if (isCustomParser && !customProps.isEmpty()) {
1144 cp->compiler = this;
1146 obj->custom = cp->compile(customProps);
1149 foreach (QQmlError err, cp->errors()) {
1150 err.setUrl(output->url);
1155 compileState->objectDepth.pop();
1160 void QQmlCompiler::genObject(QQmlScript::Object *obj, bool parentToSuper)
1162 QQmlCompiledData::TypeReference &tr = output->types[obj->type];
1163 if (tr.type && obj->metatype->metaObject() == &QQmlComponent::staticMetaObject) {
1168 // Create the object
1169 if (obj->custom.isEmpty() && output->types.at(obj->type).type &&
1170 !output->types.at(obj->type).type->isExtendedType() && obj != compileState->root) {
1172 Instruction::CreateSimpleObject create;
1173 create.create = output->types.at(obj->type).type->createFunction();
1174 create.typeSize = output->types.at(obj->type).type->createSize();
1175 create.type = obj->type;
1176 create.line = obj->location.start.line;
1177 create.column = obj->location.start.column;
1178 create.parentToSuper = parentToSuper;
1179 output->addInstruction(create);
1183 if (output->types.at(obj->type).type) {
1184 Instruction::CreateCppObject create;
1185 create.line = obj->location.start.line;
1186 create.column = obj->location.start.column;
1188 if (!obj->custom.isEmpty())
1189 create.data = output->indexForByteArray(obj->custom);
1190 create.type = obj->type;
1191 create.isRoot = (compileState->root == obj);
1192 create.parentToSuper = parentToSuper;
1193 output->addInstruction(create);
1195 Instruction::CreateQMLObject create;
1196 create.type = obj->type;
1197 create.isRoot = (compileState->root == obj);
1199 if (!obj->bindingBitmask.isEmpty()) {
1200 Q_ASSERT(obj->bindingBitmask.size() % 4 == 0);
1201 create.bindingBits = output->indexForByteArray(obj->bindingBitmask);
1203 create.bindingBits = -1;
1205 output->addInstruction(create);
1207 Instruction::CompleteQMLObject complete;
1208 complete.line = obj->location.start.line;
1209 complete.column = obj->location.start.column;
1210 complete.isRoot = (compileState->root == obj);
1211 output->addInstruction(complete);
1215 // Setup the synthesized meta object if necessary
1216 if (!obj->synthdata.isEmpty()) {
1217 Instruction::StoreMetaObject meta;
1218 meta.aliasData = output->indexForByteArray(obj->synthdata);
1219 meta.propertyCache = output->propertyCaches.count();
1221 QQmlPropertyCache *propertyCache = obj->synthCache;
1222 Q_ASSERT(propertyCache);
1223 propertyCache->addref();
1225 if (obj == unitRoot) {
1226 propertyCache->addref();
1227 output->rootPropertyCache = propertyCache;
1230 output->propertyCaches << propertyCache;
1231 output->addInstruction(meta);
1232 } else if (obj == unitRoot) {
1233 output->rootPropertyCache = tr.createPropertyCache(engine);
1234 output->rootPropertyCache->addref();
1237 // Set the object id
1238 if (!obj->id.isEmpty()) {
1239 Instruction::SetId id;
1240 id.value = output->indexForString(obj->id);
1241 id.index = obj->idIndex;
1242 output->addInstruction(id);
1246 if (tr.type && obj->parserStatusCast != -1) {
1247 Instruction::BeginObject begin;
1248 begin.castValue = obj->parserStatusCast;
1249 output->addInstruction(begin);
1255 void QQmlCompiler::genObjectBody(QQmlScript::Object *obj)
1257 for (Property *prop = obj->scriptStringProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1258 Q_ASSERT(prop->scriptStringScope != -1);
1259 const QString &script = prop->values.first()->value.asScript();
1260 Instruction::StoreScriptString ss;
1261 ss.propertyIndex = prop->index;
1262 ss.value = output->indexForString(script);
1263 ss.scope = prop->scriptStringScope;
1264 // ss.bindingId = rewriteBinding(script, prop->name());
1265 ss.bindingId = rewriteBinding(prop->values.first()->value, QString()); // XXX
1266 ss.line = prop->location.start.line;
1267 ss.column = prop->location.start.column;
1268 ss.isStringLiteral = prop->values.first()->value.isString();
1269 ss.isNumberLiteral = prop->values.first()->value.isNumber();
1270 ss.numberValue = prop->values.first()->value.asNumber();
1271 output->addInstruction(ss);
1274 bool seenDefer = false;
1275 for (Property *prop = obj->valueProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1276 if (prop->isDeferred) {
1281 genValueProperty(prop, obj);
1284 Instruction::Defer defer;
1285 defer.deferCount = 0;
1286 int deferIdx = output->addInstruction(defer);
1287 int nextInstructionIndex = output->nextInstructionIndex();
1289 Instruction::DeferInit dinit;
1290 // XXX - these are now massive over allocations
1291 dinit.bindingsSize = compileState->totalBindingsCount;
1292 dinit.parserStatusSize = compileState->parserStatusCount;
1293 dinit.objectStackSize = compileState->objectDepth.maxDepth();
1294 dinit.listStackSize = compileState->listDepth.maxDepth();
1295 output->addInstruction(dinit);
1297 for (Property *prop = obj->valueProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1298 if (!prop->isDeferred)
1300 genValueProperty(prop, obj);
1303 Instruction::Done done;
1304 output->addInstruction(done);
1306 output->instruction(deferIdx)->defer.deferCount = output->nextInstructionIndex() - nextInstructionIndex;
1309 for (Property *prop = obj->signalProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1311 QQmlScript::Value *v = prop->values.first();
1313 if (v->type == Value::SignalObject) {
1315 genObject(v->object);
1317 Instruction::AssignSignalObject assign;
1318 assign.line = v->location.start.line;
1319 assign.signal = output->indexForString(prop->name().toString());
1320 output->addInstruction(assign);
1322 } else if (v->type == Value::SignalExpression) {
1324 Instruction::StoreSignal store;
1325 store.signalIndex = prop->index;
1326 const QString &rewrite = rewriteSignalHandler(v->value, prop->name().toString());
1327 store.value = output->indexForByteArray(rewrite.toUtf8());
1328 store.context = v->signalExpressionContextStack;
1329 store.line = v->location.start.line;
1330 store.column = v->location.start.column;
1331 output->addInstruction(store);
1337 for (Property *prop = obj->attachedProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1338 Instruction::FetchAttached fetch;
1339 fetch.id = prop->index;
1340 fetch.line = prop->location.start.line;
1341 output->addInstruction(fetch);
1343 genObjectBody(prop->value);
1345 Instruction::PopFetchedObject pop;
1346 output->addInstruction(pop);
1349 for (Property *prop = obj->groupedProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1350 Instruction::FetchObject fetch;
1351 fetch.property = prop->index;
1352 fetch.line = prop->location.start.line;
1353 output->addInstruction(fetch);
1355 if (!prop->value->synthdata.isEmpty()) {
1356 Instruction::StoreMetaObject meta;
1357 meta.aliasData = output->indexForByteArray(prop->value->synthdata);
1358 meta.propertyCache = output->propertyCaches.count();
1359 QQmlPropertyCache *propertyCache = prop->value->synthCache;
1360 Q_ASSERT(propertyCache);
1361 propertyCache->addref();
1362 output->propertyCaches << propertyCache;
1363 output->addInstruction(meta);
1366 genObjectBody(prop->value);
1368 Instruction::PopFetchedObject pop;
1369 output->addInstruction(pop);
1372 for (Property *prop = obj->valueTypeProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1374 genValueTypeProperty(obj, prop);
1377 for (Property *prop = obj->valueProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1378 if (prop->isDeferred)
1381 genValueProperty(prop, obj);
1384 for (Property *prop = obj->valueTypeProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1386 genValueTypeProperty(obj, prop);
1390 void QQmlCompiler::genValueTypeProperty(QQmlScript::Object *obj,QQmlScript::Property *prop)
1392 Instruction::FetchValueType fetch;
1393 fetch.property = prop->index;
1394 fetch.type = prop->type;
1395 fetch.bindingSkipList = 0;
1397 if (obj->type == -1 || output->types.at(obj->type).component) {
1398 // We only have to do this if this is a composite type. If it is a builtin
1399 // type it can't possibly already have bindings that need to be cleared.
1400 for (Property *vprop = prop->value->valueProperties.first(); vprop; vprop = Object::PropertyList::next(vprop)) {
1401 if (!vprop->values.isEmpty()) {
1402 Q_ASSERT(vprop->index >= 0 && vprop->index < 32);
1403 fetch.bindingSkipList |= (1 << vprop->index);
1408 output->addInstruction(fetch);
1410 for (Property *vprop = prop->value->valueProperties.first(); vprop; vprop = Object::PropertyList::next(vprop)) {
1411 genPropertyAssignment(vprop, prop->value, prop);
1414 Instruction::PopValueType pop;
1415 pop.property = prop->index;
1416 pop.type = prop->type;
1417 pop.bindingSkipList = 0;
1418 output->addInstruction(pop);
1420 genPropertyAssignment(prop, obj);
1423 void QQmlCompiler::genComponent(QQmlScript::Object *obj)
1425 QQmlScript::Object *root = obj->defaultProperty->values.first()->object;
1428 Instruction::CreateComponent create;
1429 create.line = root->location.start.line;
1430 create.column = root->location.start.column;
1431 create.endLine = root->location.end.line;
1432 create.isRoot = (compileState->root == obj);
1433 int createInstruction = output->addInstruction(create);
1434 int nextInstructionIndex = output->nextInstructionIndex();
1436 ComponentCompileState *oldCompileState = compileState;
1437 compileState = componentState(root);
1439 Instruction::Init init;
1440 init.bindingsSize = compileState->totalBindingsCount;
1441 init.parserStatusSize = compileState->parserStatusCount;
1442 init.contextCache = genContextCache();
1443 init.objectStackSize = compileState->objectDepth.maxDepth();
1444 init.listStackSize = compileState->listDepth.maxDepth();
1445 if (compileState->compiledBindingData.isEmpty())
1446 init.compiledBinding = -1;
1448 init.compiledBinding = output->indexForByteArray(compileState->compiledBindingData);
1449 output->addInstruction(init);
1451 if (!compileState->v8BindingProgram.isEmpty()) {
1452 Instruction::InitV8Bindings bindings;
1453 int index = output->programs.count();
1455 typedef QQmlCompiledData::V8Program V8Program;
1456 output->programs.append(V8Program(compileState->v8BindingProgram, output));
1458 bindings.programIndex = index;
1459 bindings.line = compileState->v8BindingProgramLine;
1460 output->addInstruction(bindings);
1465 Instruction::SetDefault def;
1466 output->addInstruction(def);
1468 Instruction::Done done;
1469 output->addInstruction(done);
1471 output->instruction(createInstruction)->createComponent.count =
1472 output->nextInstructionIndex() - nextInstructionIndex;
1474 compileState = oldCompileState;
1476 if (!obj->id.isEmpty()) {
1477 Instruction::SetId id;
1478 id.value = output->indexForString(obj->id);
1479 id.index = obj->idIndex;
1480 output->addInstruction(id);
1483 if (obj == unitRoot) {
1484 output->rootPropertyCache = output->types[obj->type].createPropertyCache(engine);
1485 output->rootPropertyCache->addref();
1489 bool QQmlCompiler::buildComponent(QQmlScript::Object *obj,
1490 const BindingContext &ctxt)
1492 // The special "Component" element can only have the id property and a
1493 // default property, that actually defines the component's tree
1495 compileState->objectDepth.push();
1497 // Find, check and set the "id" property (if any)
1498 Property *idProp = 0;
1499 if (obj->properties.isMany() ||
1500 (obj->properties.isOne() && obj->properties.first()->name() != id_string))
1501 COMPILE_EXCEPTION(obj->properties.first(), tr("Component elements may not contain properties other than id"));
1503 if (!obj->properties.isEmpty())
1504 idProp = obj->properties.first();
1507 if (idProp->value || idProp->values.isMany() || idProp->values.first()->object)
1508 COMPILE_EXCEPTION(idProp, tr("Invalid component id specification"));
1509 COMPILE_CHECK(checkValidId(idProp->values.first(), idProp->values.first()->primitive()))
1511 QString idVal = idProp->values.first()->primitive();
1513 if (compileState->ids.value(idVal))
1514 COMPILE_EXCEPTION(idProp, tr("id is not unique"));
1520 // Check the Component tree is well formed
1521 if (obj->defaultProperty &&
1522 (obj->defaultProperty->value || obj->defaultProperty->values.isMany() ||
1523 (obj->defaultProperty->values.isOne() && !obj->defaultProperty->values.first()->object)))
1524 COMPILE_EXCEPTION(obj, tr("Invalid component body specification"));
1526 if (!obj->dynamicProperties.isEmpty())
1527 COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties."));
1528 if (!obj->dynamicSignals.isEmpty())
1529 COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals."));
1530 if (!obj->dynamicSlots.isEmpty())
1531 COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions."));
1533 QQmlScript::Object *root = 0;
1534 if (obj->defaultProperty && !obj->defaultProperty->values.isEmpty())
1535 root = obj->defaultProperty->values.first()->object;
1538 COMPILE_EXCEPTION(obj, tr("Cannot create empty component specification"));
1540 // Build the component tree
1541 COMPILE_CHECK(buildComponentFromRoot(root, ctxt));
1543 compileState->objectDepth.pop();
1548 bool QQmlCompiler::buildComponentFromRoot(QQmlScript::Object *obj,
1549 const BindingContext &ctxt)
1551 ComponentCompileState *oldComponentCompileState = compileState;
1552 compileState = pool->New<ComponentCompileState>();
1553 compileState->root = obj;
1554 compileState->nested = true;
1556 if (componentStats) {
1557 ComponentStat oldComponentStat = componentStats->componentStat;
1559 componentStats->componentStat = ComponentStat();
1560 componentStats->componentStat.lineNumber = obj->location.start.line;
1563 COMPILE_CHECK(buildObject(obj, ctxt));
1565 COMPILE_CHECK(completeComponentBuild());
1567 componentStats->componentStat = oldComponentStat;
1570 COMPILE_CHECK(buildObject(obj, ctxt));
1572 COMPILE_CHECK(completeComponentBuild());
1575 compileState = oldComponentCompileState;
1581 // Build a sub-object. A sub-object is one that was not created directly by
1582 // QML - such as a grouped property object, or an attached object. Sub-object's
1583 // can't have an id, involve a custom parser, have attached properties etc.
1584 bool QQmlCompiler::buildSubObject(QQmlScript::Object *obj, const BindingContext &ctxt)
1586 Q_ASSERT(obj->metatype);
1587 Q_ASSERT(!obj->defaultProperty);
1588 Q_ASSERT(ctxt.isSubContext()); // sub-objects must always be in a binding
1591 for (Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) {
1592 if (isSignalPropertyName(prop->name())) {
1593 COMPILE_CHECK(buildSignal(prop, obj, ctxt));
1595 COMPILE_CHECK(buildProperty(prop, obj, ctxt));
1602 int QQmlCompiler::componentTypeRef()
1604 if (cachedComponentTypeRef == -1) {
1605 QQmlType *t = QQmlMetaType::qmlType(Component_string, Component_module_string, 1, 0);
1606 for (int ii = output->types.count() - 1; ii >= 0; --ii) {
1607 if (output->types.at(ii).type == t) {
1608 cachedComponentTypeRef = ii;
1612 QQmlCompiledData::TypeReference ref;
1614 output->types << ref;
1615 cachedComponentTypeRef = output->types.count() - 1;
1617 return cachedComponentTypeRef;
1620 int QQmlCompiler::translationContextIndex()
1622 if (cachedTranslationContextIndex == -1) {
1623 // This code must match that in the qsTr() implementation
1624 const QString &path = output->name;
1625 int lastSlash = path.lastIndexOf(QLatin1Char('/'));
1626 QString context = (lastSlash > -1) ? path.mid(lastSlash + 1, path.length()-lastSlash-5) :
1628 QByteArray contextUtf8 = context.toUtf8();
1629 cachedTranslationContextIndex = output->indexForByteArray(contextUtf8);
1631 return cachedTranslationContextIndex;
1634 bool QQmlCompiler::buildSignal(QQmlScript::Property *prop, QQmlScript::Object *obj,
1635 const BindingContext &ctxt)
1637 Q_ASSERT(obj->metatype);
1639 const QHashedStringRef &propName = prop->name();
1641 Q_ASSERT(propName.startsWith(on_string));
1642 QString name = propName.mid(2, -1).toString();
1644 // Note that the property name could start with any alpha or '_' or '$' character,
1645 // so we need to do the lower-casing of the first alpha character.
1646 for (int firstAlphaIndex = 0; firstAlphaIndex < name.size(); ++firstAlphaIndex) {
1647 if (name.at(firstAlphaIndex).isUpper()) {
1648 name[firstAlphaIndex] = name.at(firstAlphaIndex).toLower();
1653 bool notInRevision = false;
1655 QQmlPropertyData *sig = signal(obj, QStringRef(&name), ¬InRevision);
1659 if (notInRevision && 0 == property(obj, propName, 0)) {
1660 Q_ASSERT(obj->type != -1);
1661 const QList<QQmlTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes();
1662 const QQmlTypeData::TypeReference &type = resolvedTypes.at(obj->type);
1664 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));
1666 COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available due to component versioning.").arg(elementName(obj)).arg(prop->name().toString()));
1670 // If the "on<Signal>" name doesn't resolve into a signal, try it as a
1672 COMPILE_CHECK(buildProperty(prop, obj, ctxt));
1676 if (prop->value || !prop->values.isOne())
1677 COMPILE_EXCEPTION(prop, tr("Incorrectly specified signal assignment"));
1679 prop->index = propertyCacheForObject(obj)->methodIndexToSignalIndex(sig->coreIndex);
1682 obj->addSignalProperty(prop);
1684 if (prop->values.first()->object) {
1685 COMPILE_CHECK(buildObject(prop->values.first()->object, ctxt));
1686 prop->values.first()->type = Value::SignalObject;
1688 prop->values.first()->type = Value::SignalExpression;
1690 if (!prop->values.first()->value.isScript())
1691 COMPILE_EXCEPTION(prop, tr("Cannot assign a value to a signal (expecting a script to be run)"));
1693 QString script = prop->values.first()->value.asScript().trimmed();
1694 if (script.isEmpty())
1695 COMPILE_EXCEPTION(prop, tr("Empty signal assignment"));
1697 prop->values.first()->signalExpressionContextStack = ctxt.stack;
1706 Returns true if (value) property \a prop exists on obj, false otherwise.
1708 bool QQmlCompiler::doesPropertyExist(QQmlScript::Property *prop,
1709 QQmlScript::Object *obj)
1711 if (prop->name().isEmpty())
1713 if(isAttachedPropertyName(prop->name()) || prop->name() == id_string)
1716 return property(obj, prop->name()) != 0;
1719 bool QQmlCompiler::buildProperty(QQmlScript::Property *prop,
1720 QQmlScript::Object *obj,
1721 const BindingContext &ctxt)
1723 if (prop->isEmpty())
1724 COMPILE_EXCEPTION(prop, tr("Empty property assignment"));
1726 if (isAttachedPropertyName(prop->name())) {
1727 // Setup attached property data
1729 if (ctxt.isSubContext()) {
1730 // Attached properties cannot be used on sub-objects. Sub-objects
1731 // always exist in a binding sub-context, which is what we test
1733 COMPILE_EXCEPTION(prop, tr("Attached properties cannot be used here"));
1737 QQmlImportNamespace *typeNamespace = 0;
1738 unit->imports().resolveType(prop->name(), &type, 0, 0, 0, &typeNamespace);
1740 if (typeNamespace) {
1741 COMPILE_CHECK(buildPropertyInNamespace(typeNamespace, prop, obj,
1744 } else if (!type || !type->attachedPropertiesType()) {
1745 COMPILE_EXCEPTION(prop, tr("Non-existent attached object"));
1749 COMPILE_EXCEPTION(prop, tr("Invalid attached object assignment"));
1751 Q_ASSERT(type->attachedPropertiesFunction());
1752 prop->index = type->attachedPropertiesId();
1753 prop->value->metatype = enginePrivate->cache(type->attachedPropertiesType());
1755 // Setup regular property data
1756 bool notInRevision = false;
1757 QQmlPropertyData *d =
1758 prop->name().isEmpty()?0:property(obj, prop->name(), ¬InRevision);
1760 if (d == 0 && notInRevision) {
1761 const QList<QQmlTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes();
1762 const QQmlTypeData::TypeReference &type = resolvedTypes.at(obj->type);
1764 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));
1766 COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available due to component versioning.").arg(elementName(obj)).arg(prop->name().toString()));
1769 prop->index = d->coreIndex;
1771 } else if (prop->isDefault) {
1772 QString defaultPropertyName = obj->metatype->defaultPropertyName();
1774 if (!defaultPropertyName.isEmpty()) {
1775 prop->setName(defaultPropertyName);
1776 prop->core = *obj->metatype->defaultProperty();
1777 prop->index = prop->core.coreIndex;
1781 // We can't error here as the "id" property does not require a
1782 // successful index resolution
1783 if (prop->index != -1)
1784 prop->type = prop->core.propType;
1786 // Check if this is an alias
1787 if (prop->index != -1 &&
1789 prop->parent->type != -1 &&
1790 output->types.at(prop->parent->type).component) {
1792 QQmlPropertyCache *cache = output->types.at(prop->parent->type).component->rootPropertyCache;
1793 if (cache && cache->property(prop->index) && cache->property(prop->index)->isAlias())
1794 prop->isAlias = true;
1797 if (prop->index != -1 && !prop->values.isEmpty())
1798 prop->parent->setBindingBit(prop->index);
1801 if (!prop->isDefault && prop->name() == id_string && !ctxt.isSubContext()) {
1803 // The magic "id" behavior doesn't apply when "id" is resolved as a
1804 // default property or to sub-objects (which are always in binding
1806 COMPILE_CHECK(buildIdProperty(prop, obj));
1807 if (prop->type == QVariant::String &&
1808 prop->values.first()->value.isString())
1809 COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt));
1811 } else if (isAttachedPropertyName(prop->name())) {
1813 COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt));
1815 } else if (prop->index == -1) {
1817 if (prop->isDefault) {
1818 COMPILE_EXCEPTION(prop->values.first(), tr("Cannot assign to non-existent default property"));
1820 COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(prop->name().toString()));
1823 } else if (prop->value) {
1825 COMPILE_CHECK(buildGroupedProperty(prop, obj, ctxt));
1827 } else if (prop->core.isQList()) {
1829 COMPILE_CHECK(buildListProperty(prop, obj, ctxt));
1831 } else if (prop->type == qMetaTypeId<QQmlScriptString>()) {
1833 COMPILE_CHECK(buildScriptStringProperty(prop, obj, ctxt));
1837 COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt));
1844 bool QQmlCompiler::buildPropertyInNamespace(QQmlImportNamespace *ns,
1845 QQmlScript::Property *nsProp,
1846 QQmlScript::Object *obj,
1847 const BindingContext &ctxt)
1850 COMPILE_EXCEPTION(nsProp, tr("Invalid use of namespace"));
1852 for (Property *prop = nsProp->value->properties.first(); prop; prop = nsProp->value->properties.next(prop)) {
1854 if (!isAttachedPropertyName(prop->name()))
1855 COMPILE_EXCEPTION(prop, tr("Not an attached property name"));
1857 // Setup attached property data
1860 unit->imports().resolveType(ns, prop->name(), &type, 0, 0, 0);
1862 if (!type || !type->attachedPropertiesType())
1863 COMPILE_EXCEPTION(prop, tr("Non-existent attached object"));
1866 COMPILE_EXCEPTION(prop, tr("Invalid attached object assignment"));
1868 Q_ASSERT(type->attachedPropertiesFunction());
1869 prop->index = type->index();
1870 prop->value->metatype = enginePrivate->cache(type->attachedPropertiesType());
1872 COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt));
1878 void QQmlCompiler::genValueProperty(QQmlScript::Property *prop,
1879 QQmlScript::Object *obj)
1881 if (prop->core.isQList()) {
1882 genListProperty(prop, obj);
1884 genPropertyAssignment(prop, obj);
1888 void QQmlCompiler::genListProperty(QQmlScript::Property *prop,
1889 QQmlScript::Object *obj)
1891 int listType = enginePrivate->listType(prop->type);
1893 Instruction::FetchQList fetch;
1894 fetch.property = prop->index;
1895 bool listTypeIsInterface = QQmlMetaType::isInterface(listType);
1896 fetch.type = listType;
1897 output->addInstruction(fetch);
1899 for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
1901 if (v->type == Value::CreatedObject) {
1903 genObject(v->object);
1904 if (listTypeIsInterface) {
1905 Instruction::AssignObjectList assign;
1906 assign.line = prop->location.start.line;
1907 output->addInstruction(assign);
1909 Instruction::StoreObjectQList store;
1910 output->addInstruction(store);
1913 } else if (v->type == Value::PropertyBinding) {
1915 genBindingAssignment(v, prop, obj);
1921 Instruction::PopQList pop;
1922 output->addInstruction(pop);
1925 void QQmlCompiler::genPropertyAssignment(QQmlScript::Property *prop,
1926 QQmlScript::Object *obj,
1927 QQmlScript::Property *valueTypeProperty)
1929 for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
1931 Q_ASSERT(v->type == Value::CreatedObject ||
1932 v->type == Value::PropertyBinding ||
1933 v->type == Value::Literal);
1935 if (v->type == Value::CreatedObject) {
1937 genObject(v->object);
1939 if (QQmlMetaType::isInterface(prop->type)) {
1941 Instruction::StoreInterface store;
1942 store.line = v->object->location.start.line;
1943 store.propertyIndex = prop->index;
1944 output->addInstruction(store);
1946 } else if (prop->type == QMetaType::QVariant) {
1948 if (prop->core.isVarProperty()) {
1949 Instruction::StoreVarObject store;
1950 store.line = v->object->location.start.line;
1951 store.propertyIndex = prop->index;
1952 output->addInstruction(store);
1954 Instruction::StoreVariantObject store;
1955 store.line = v->object->location.start.line;
1956 store.propertyIndex = prop->index;
1957 output->addInstruction(store);
1963 Instruction::StoreObject store;
1964 store.line = v->object->location.start.line;
1965 store.propertyIndex = prop->index;
1966 output->addInstruction(store);
1969 } else if (v->type == Value::PropertyBinding) {
1971 genBindingAssignment(v, prop, obj, valueTypeProperty);
1973 } else if (v->type == Value::Literal) {
1975 genLiteralAssignment(prop, v);
1981 for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) {
1983 Q_ASSERT(v->type == Value::ValueSource ||
1984 v->type == Value::ValueInterceptor);
1986 if (v->type == Value::ValueSource) {
1987 genObject(v->object, valueTypeProperty?true:false);
1989 Instruction::StoreValueSource store;
1990 if (valueTypeProperty)
1991 store.property = genValueTypeData(prop, valueTypeProperty);
1993 store.property = prop->core;
1994 QQmlType *valueType = toQmlType(v->object);
1995 store.castValue = valueType->propertyValueSourceCast();
1996 output->addInstruction(store);
1998 } else if (v->type == Value::ValueInterceptor) {
1999 genObject(v->object, valueTypeProperty?true:false);
2001 Instruction::StoreValueInterceptor store;
2002 if (valueTypeProperty)
2003 store.property = genValueTypeData(prop, valueTypeProperty);
2005 store.property = prop->core;
2006 QQmlType *valueType = toQmlType(v->object);
2007 store.castValue = valueType->propertyValueInterceptorCast();
2008 output->addInstruction(store);
2014 bool QQmlCompiler::buildIdProperty(QQmlScript::Property *prop,
2015 QQmlScript::Object *obj)
2018 prop->values.isMany() ||
2019 prop->values.first()->object)
2020 COMPILE_EXCEPTION(prop, tr("Invalid use of id property"));
2022 QQmlScript::Value *idValue = prop->values.first();
2023 QString val = idValue->primitive();
2025 COMPILE_CHECK(checkValidId(idValue, val));
2027 if (compileState->ids.value(val))
2028 COMPILE_EXCEPTION(prop, tr("id is not unique"));
2030 prop->values.first()->type = Value::Id;
2038 void QQmlCompiler::addId(const QString &id, QQmlScript::Object *obj)
2041 Q_ASSERT(!compileState->ids.value(id));
2042 Q_ASSERT(obj->id == id);
2043 obj->idIndex = compileState->ids.count();
2044 compileState->ids.append(obj);
2047 void QQmlCompiler::addBindingReference(JSBindingReference *ref)
2049 Q_ASSERT(ref->value && !ref->value->bindingReference);
2050 ref->value->bindingReference = ref;
2051 compileState->totalBindingsCount++;
2052 compileState->bindings.prepend(ref);
2055 void QQmlCompiler::saveComponentState()
2057 Q_ASSERT(compileState->root);
2058 Q_ASSERT(compileState->root->componentCompileState == 0);
2060 compileState->root->componentCompileState = compileState;
2063 componentStats->savedComponentStats.append(componentStats->componentStat);
2066 QQmlCompilerTypes::ComponentCompileState *
2067 QQmlCompiler::componentState(QQmlScript::Object *obj)
2069 Q_ASSERT(obj->componentCompileState);
2070 return obj->componentCompileState;
2073 // Build attached property object. In this example,
2077 // GridView is an attached property object.
2078 bool QQmlCompiler::buildAttachedProperty(QQmlScript::Property *prop,
2079 QQmlScript::Object *obj,
2080 const BindingContext &ctxt)
2082 Q_ASSERT(prop->value);
2083 Q_ASSERT(prop->index != -1); // This is set in buildProperty()
2085 compileState->objectDepth.push();
2087 obj->addAttachedProperty(prop);
2089 COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr()));
2091 compileState->objectDepth.pop();
2097 // Build "grouped" properties. In this example:
2099 // font.pointSize: 12
2100 // font.family: "Helvetica"
2102 // font is a nested property. pointSize and family are not.
2103 bool QQmlCompiler::buildGroupedProperty(QQmlScript::Property *prop,
2104 QQmlScript::Object *obj,
2105 const BindingContext &ctxt)
2107 Q_ASSERT(prop->type != 0);
2108 Q_ASSERT(prop->index != -1);
2110 if (QQmlValueTypeFactory::isValueType(prop->type)) {
2111 QQmlValueType *valueType = QQmlValueTypeFactory::valueType(prop->type);
2112 if (prop->type >= 0 && valueType) {
2113 if (!prop->values.isEmpty()) {
2114 // Only error if we are assigning values, and not e.g. a property interceptor
2115 for (Property *dotProp = prop->value->properties.first(); dotProp; dotProp = prop->value->properties.next(dotProp)) {
2116 if (!dotProp->values.isEmpty()) {
2117 if (prop->values.first()->location < prop->value->location) {
2118 COMPILE_EXCEPTION(prop->value, tr( "Property has already been assigned a value"));
2120 COMPILE_EXCEPTION(prop->values.first(), tr( "Property has already been assigned a value"));
2126 if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration) {
2127 COMPILE_EXCEPTION(prop, tr( "Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
2130 if (prop->isAlias) {
2131 for (Property *vtProp = prop->value->properties.first(); vtProp; vtProp = prop->value->properties.next(vtProp)) {
2132 vtProp->isAlias = true;
2136 COMPILE_CHECK(buildValueTypeProperty(valueType, prop->value, obj, ctxt.incr()));
2138 // When building a value type where sub components are declared, this
2139 // code path is followed from buildProperty, even if there is a previous
2140 // assignment to the value type as a whole. Therefore we need to look
2141 // for (and build) assignments to the entire value type before looking
2142 // for any onValue assignments.
2143 for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
2145 COMPILE_EXCEPTION(v->object, tr("Objects cannot be assigned to value types"));
2147 COMPILE_CHECK(buildPropertyLiteralAssignment(prop, obj, v, ctxt));
2150 for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) {
2151 Q_ASSERT(v->object);
2152 COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, obj, v, ctxt));
2155 obj->addValueTypeProperty(prop);
2157 COMPILE_EXCEPTION(prop, tr("Invalid grouped property access"));
2160 // Load the nested property's meta type
2161 prop->value->metatype = enginePrivate->propertyCacheForType(prop->type);
2162 if (!prop->value->metatype)
2163 COMPILE_EXCEPTION(prop, tr("Invalid grouped property access"));
2165 if (!prop->values.isEmpty())
2166 COMPILE_EXCEPTION(prop->values.first(), tr( "Cannot assign a value directly to a grouped property"));
2168 obj->addGroupedProperty(prop);
2170 compileState->objectDepth.push();
2172 COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr()));
2174 compileState->objectDepth.pop();
2180 bool QQmlCompiler::buildValueTypeProperty(QObject *type,
2181 QQmlScript::Object *obj,
2182 QQmlScript::Object *baseObj,
2183 const BindingContext &ctxt)
2185 compileState->objectDepth.push();
2187 if (obj->defaultProperty)
2188 COMPILE_EXCEPTION(obj, tr("Invalid property use"));
2189 obj->metatype = enginePrivate->cache(type);
2191 for (Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) {
2193 QQmlPropertyData *d = property(obj, prop->name());
2195 COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(prop->name().toString()));
2197 prop->index = d->coreIndex;
2198 prop->type = d->propType;
2200 prop->isValueTypeSubProperty = true;
2203 COMPILE_EXCEPTION(prop, tr("Property assignment expected"));
2205 if (prop->values.isMany()) {
2206 COMPILE_EXCEPTION(prop, tr("Single property assignment expected"));
2207 } else if (!prop->values.isEmpty()) {
2208 QQmlScript::Value *value = prop->values.first();
2210 if (value->object) {
2211 COMPILE_EXCEPTION(prop, tr("Unexpected object assignment"));
2212 } else if (value->value.isScript()) {
2213 // ### Check for writability
2215 //optimization for <Type>.<EnumValue> enum assignments
2216 bool isEnumAssignment = false;
2218 if (prop->core.isEnum() || prop->core.propType == QMetaType::Int)
2219 COMPILE_CHECK(testQualifiedEnumAssignment(prop, obj, value, &isEnumAssignment));
2221 if (isEnumAssignment) {
2222 value->type = Value::Literal;
2224 JSBindingReference *reference = pool->New<JSBindingReference>();
2225 reference->expression = value->value;
2226 reference->property = prop;
2227 reference->value = value;
2228 reference->bindingContext = ctxt;
2229 reference->bindingContext.owner++;
2230 addBindingReference(reference);
2231 value->type = Value::PropertyBinding;
2234 COMPILE_CHECK(testLiteralAssignment(prop, value));
2235 value->type = Value::Literal;
2239 for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) {
2240 Q_ASSERT(v->object);
2242 COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, baseObj, v, ctxt));
2245 obj->addValueProperty(prop);
2248 compileState->objectDepth.pop();
2253 // Build assignments to QML lists. QML lists are properties of type
2254 // QQmlListProperty<T>. List properties can accept a list of
2255 // objects, or a single binding.
2256 bool QQmlCompiler::buildListProperty(QQmlScript::Property *prop,
2257 QQmlScript::Object *obj,
2258 const BindingContext &ctxt)
2260 Q_ASSERT(prop->core.isQList());
2262 compileState->listDepth.push();
2266 obj->addValueProperty(prop);
2268 int listType = enginePrivate->listType(t);
2269 bool listTypeIsInterface = QQmlMetaType::isInterface(listType);
2271 bool assignedBinding = false;
2272 for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
2274 v->type = Value::CreatedObject;
2275 COMPILE_CHECK(buildObject(v->object, ctxt));
2277 // We check object coercian here. We check interface assignment
2279 if (!listTypeIsInterface) {
2280 if (!canCoerce(listType, v->object)) {
2281 COMPILE_EXCEPTION(v, tr("Cannot assign object to list"));
2285 } else if (v->value.isScript()) {
2286 if (assignedBinding)
2287 COMPILE_EXCEPTION(v, tr("Can only assign one binding to lists"));
2289 assignedBinding = true;
2290 COMPILE_CHECK(buildBinding(v, prop, ctxt));
2291 v->type = Value::PropertyBinding;
2293 COMPILE_EXCEPTION(v, tr("Cannot assign primitives to lists"));
2297 compileState->listDepth.pop();
2302 // Compiles an assignment to a QQmlScriptString property
2303 bool QQmlCompiler::buildScriptStringProperty(QQmlScript::Property *prop,
2304 QQmlScript::Object *obj,
2305 const BindingContext &ctxt)
2307 if (prop->values.isMany())
2308 COMPILE_EXCEPTION(prop->values.first()->nextValue, tr( "Cannot assign multiple values to a script property"));
2310 if (prop->values.first()->object)
2311 COMPILE_EXCEPTION(prop->values.first(), tr( "Invalid property assignment: script expected"));
2313 prop->scriptStringScope = ctxt.stack;
2314 obj->addScriptStringProperty(prop);
2319 // Compile regular property assignments of the form "property: <value>"
2320 bool QQmlCompiler::buildPropertyAssignment(QQmlScript::Property *prop,
2321 QQmlScript::Object *obj,
2322 const BindingContext &ctxt)
2324 obj->addValueProperty(prop);
2326 if (prop->values.isMany())
2327 COMPILE_EXCEPTION(prop->values.first(), tr( "Cannot assign multiple values to a singular property") );
2329 for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
2332 COMPILE_CHECK(buildPropertyObjectAssignment(prop, obj, v, ctxt));
2336 COMPILE_CHECK(buildPropertyLiteralAssignment(prop, obj, v, ctxt));
2341 for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) {
2342 Q_ASSERT(v->object);
2343 COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, obj, v, ctxt));
2349 // Compile assigning a single object instance to a regular property
2350 bool QQmlCompiler::buildPropertyObjectAssignment(QQmlScript::Property *prop,
2351 QQmlScript::Object *obj,
2352 QQmlScript::Value *v,
2353 const BindingContext &ctxt)
2355 Q_ASSERT(prop->index != -1);
2356 Q_ASSERT(v->object->type != -1);
2358 if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration)
2359 COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
2361 if (QQmlMetaType::isInterface(prop->type)) {
2363 // Assigning an object to an interface ptr property
2364 COMPILE_CHECK(buildObject(v->object, ctxt));
2366 v->type = Value::CreatedObject;
2368 } else if (prop->type == QMetaType::QVariant) {
2370 // Assigning an object to a QVariant
2371 COMPILE_CHECK(buildObject(v->object, ctxt));
2373 v->type = Value::CreatedObject;
2375 // Normally buildObject() will set this up, but we need the static
2376 // meta object earlier to test for assignability. It doesn't matter
2377 // that there may still be outstanding synthesized meta object changes
2378 // on this type, as they are not relevant for assignability testing
2379 v->object->metatype = output->types[v->object->type].createPropertyCache(engine);
2380 Q_ASSERT(v->object->metatype);
2382 // We want to raw metaObject here as the raw metaobject is the
2383 // actual property type before we applied any extensions that might
2384 // effect the properties on the type, but don't effect assignability
2385 QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(prop->type);
2387 // Will be true if the assgned type inherits propertyMetaObject
2388 bool isAssignable = false;
2389 // Determine isAssignable value
2390 if (propertyMetaObject) {
2391 QQmlPropertyCache *c = v->object->metatype;
2392 while (c && !isAssignable) {
2393 isAssignable |= c == propertyMetaObject;
2399 // Simple assignment
2400 COMPILE_CHECK(buildObject(v->object, ctxt));
2402 v->type = Value::CreatedObject;
2403 } else if (propertyMetaObject && propertyMetaObject->metaObject() == &QQmlComponent::staticMetaObject) {
2404 // Automatic "Component" insertion
2405 QQmlScript::Object *root = v->object;
2406 QQmlScript::Object *component = pool->New<Object>();
2407 component->type = componentTypeRef();
2408 component->metatype = enginePrivate->cache(&QQmlComponent::staticMetaObject);
2409 component->location = root->location;
2410 QQmlScript::Value *componentValue = pool->New<Value>();
2411 componentValue->object = root;
2412 component->getDefaultProperty()->addValue(componentValue);
2413 v->object = component;
2414 COMPILE_CHECK(buildPropertyObjectAssignment(prop, obj, v, ctxt));
2416 COMPILE_EXCEPTION(v->object, tr("Cannot assign object to property"));
2423 // Compile assigning a single object instance to a regular property using the "on" syntax.
2427 // NumberAnimation on x { }
2429 bool QQmlCompiler::buildPropertyOnAssignment(QQmlScript::Property *prop,
2430 QQmlScript::Object *obj,
2431 QQmlScript::Object *baseObj,
2432 QQmlScript::Value *v,
2433 const BindingContext &ctxt)
2435 Q_ASSERT(prop->index != -1);
2436 Q_ASSERT(v->object->type != -1);
2440 if (!prop->core.isWritable())
2441 COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
2444 // Normally buildObject() will set this up, but we need the static
2445 // meta object earlier to test for assignability. It doesn't matter
2446 // that there may still be outstanding synthesized meta object changes
2447 // on this type, as they are not relevant for assignability testing
2448 v->object->metatype = output->types[v->object->type].createPropertyCache(engine);
2449 Q_ASSERT(v->object->metatype);
2451 // Will be true if the assigned type inherits QQmlPropertyValueSource
2452 bool isPropertyValue = false;
2453 // Will be true if the assigned type inherits QQmlPropertyValueInterceptor
2454 bool isPropertyInterceptor = false;
2455 if (QQmlType *valueType = toQmlType(v->object)) {
2456 isPropertyValue = valueType->propertyValueSourceCast() != -1;
2457 isPropertyInterceptor = valueType->propertyValueInterceptorCast() != -1;
2460 if (isPropertyValue || isPropertyInterceptor) {
2461 // Assign as a property value source
2462 COMPILE_CHECK(buildObject(v->object, ctxt));
2464 if (isPropertyInterceptor && baseObj->synthdata.isEmpty())
2465 buildDynamicMeta(baseObj, ForceCreation);
2466 v->type = isPropertyValue ? Value::ValueSource : Value::ValueInterceptor;
2468 COMPILE_EXCEPTION(v, tr("\"%1\" cannot operate on \"%2\"").arg(elementName(v->object)).arg(prop->name().toString()));
2474 // Compile assigning a literal or binding to a regular property
2475 bool QQmlCompiler::buildPropertyLiteralAssignment(QQmlScript::Property *prop,
2476 QQmlScript::Object *obj,
2477 QQmlScript::Value *v,
2478 const BindingContext &ctxt)
2480 Q_ASSERT(prop->index != -1);
2482 if (v->value.isScript()) {
2484 //optimization for <Type>.<EnumValue> enum assignments
2485 if (prop->core.isEnum() || prop->core.propType == QMetaType::Int) {
2486 bool isEnumAssignment = false;
2487 COMPILE_CHECK(testQualifiedEnumAssignment(prop, obj, v, &isEnumAssignment));
2488 if (isEnumAssignment) {
2489 v->type = Value::Literal;
2494 // Test for other binding optimizations
2495 if (!buildLiteralBinding(v, prop, ctxt))
2496 COMPILE_CHECK(buildBinding(v, prop, ctxt));
2498 v->type = Value::PropertyBinding;
2502 COMPILE_CHECK(testLiteralAssignment(prop, v));
2504 v->type = Value::Literal;
2510 struct StaticQtMetaObject : public QObject
2512 static const QMetaObject *get()
2513 { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; }
2516 bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop,
2517 QQmlScript::Object *obj,
2518 QQmlScript::Value *v,
2521 bool isIntProp = (prop->core.propType == QMetaType::Int) && !prop->core.isEnum();
2522 *isAssignment = false;
2523 if (!prop->core.isEnum() && !isIntProp)
2526 QMetaProperty mprop = obj->metatype->firstCppMetaObject()->property(prop->index);
2528 if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration)
2529 COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
2531 QString string = v->value.asString();
2532 if (!string.at(0).isUpper())
2535 int dot = string.indexOf(QLatin1Char('.'));
2536 if (dot == -1 || dot == string.length()-1)
2539 if (string.indexOf(QLatin1Char('.'), dot+1) != -1)
2542 QHashedStringRef typeName(string.constData(), dot);
2543 QString enumValue = string.mid(dot+1);
2546 // Allow enum assignment to ints.
2548 int enumval = evaluateEnum(typeName, enumValue.toUtf8(), &ok);
2550 v->type = Value::Literal;
2551 v->value = QQmlScript::Variant((double)enumval);
2552 *isAssignment = true;
2558 unit->imports().resolveType(typeName, &type, 0, 0, 0, 0);
2560 if (!type && typeName != QLatin1String("Qt"))
2566 if (type && toQmlType(obj) == type) {
2567 // When these two match, we can short cut the search
2568 if (mprop.isFlagType()) {
2569 value = mprop.enumerator().keysToValue(enumValue.toUtf8().constData(), &ok);
2571 value = mprop.enumerator().keyToValue(enumValue.toUtf8().constData(), &ok);
2574 // Otherwise we have to search the whole type
2576 value = type->enumValue(QHashedStringRef(enumValue), &ok);
2578 QByteArray enumName = enumValue.toUtf8();
2579 const QMetaObject *metaObject = StaticQtMetaObject::get();
2580 for (int ii = metaObject->enumeratorCount() - 1; !ok && ii >= 0; --ii) {
2581 QMetaEnum e = metaObject->enumerator(ii);
2582 value = e.keyToValue(enumName.constData(), &ok);
2590 v->type = Value::Literal;
2591 v->value = QQmlScript::Variant((double)value);
2592 *isAssignment = true;
2597 // Similar logic to above, but not knowing target property.
2598 int QQmlCompiler::evaluateEnum(const QHashedStringRef &scope, const QByteArray& enumValue, bool *ok) const
2600 Q_ASSERT_X(ok, "QQmlCompiler::evaluateEnum", "ok must not be a null pointer");
2603 if (scope != QLatin1String("Qt")) {
2605 unit->imports().resolveType(scope, &type, 0, 0, 0, 0);
2606 return type ? type->enumValue(QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1;
2609 const QMetaObject *mo = StaticQtMetaObject::get();
2610 int i = mo->enumeratorCount();
2612 int v = mo->enumerator(i).keyToValue(enumValue.constData(), ok);
2619 const QMetaObject *QQmlCompiler::resolveType(const QString& name) const
2621 QQmlType *qmltype = 0;
2622 if (!unit->imports().resolveType(name, &qmltype, 0, 0, 0, 0))
2626 return qmltype->metaObject();
2629 // similar to logic of completeComponentBuild, but also sticks data
2630 // into primitives at the end
2631 int QQmlCompiler::rewriteBinding(const QQmlScript::Variant& value, const QString& name)
2633 QQmlRewrite::RewriteBinding rewriteBinding;
2634 rewriteBinding.setName(QLatin1Char('$') + name.mid(name.lastIndexOf(QLatin1Char('.')) + 1));
2636 QString rewrite = rewriteBinding(value.asAST(), value.asScript(), 0);
2638 return output->indexForString(rewrite);
2641 QString QQmlCompiler::rewriteSignalHandler(const QQmlScript::Variant& value, const QString &name)
2643 QQmlRewrite::RewriteSignalHandler rewriteSignalHandler;
2644 return rewriteSignalHandler(value.asAST(), value.asScript(), name);
2647 // Ensures that the dynamic meta specification on obj is valid
2648 bool QQmlCompiler::checkDynamicMeta(QQmlScript::Object *obj)
2650 bool seenDefaultProperty = false;
2652 // We use a coarse grain, 31 bit hash to check if there are duplicates.
2653 // Calculating the hash for the names is not a waste as we have to test
2654 // them against the illegalNames set anyway.
2655 QHashField propNames;
2656 QHashField methodNames;
2659 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) {
2660 const QQmlScript::Object::DynamicProperty &prop = *p;
2662 if (prop.isDefaultProperty) {
2663 if (seenDefaultProperty)
2664 COMPILE_EXCEPTION(&prop, tr("Duplicate default property"));
2665 seenDefaultProperty = true;
2668 if (propNames.testAndSet(prop.name.hash())) {
2669 for (Object::DynamicProperty *p2 = obj->dynamicProperties.first(); p2 != p;
2670 p2 = obj->dynamicProperties.next(p2)) {
2671 if (p2->name == prop.name) {
2672 COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line,
2673 prop.nameLocation.column,
2674 tr("Duplicate property name"));
2679 if (prop.name.at(0).isUpper()) {
2680 COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line,
2681 prop.nameLocation.column,
2682 tr("Property names cannot begin with an upper case letter"));
2685 if (enginePrivate->v8engine()->illegalNames().contains(prop.name)) {
2686 COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line,
2687 prop.nameLocation.column,
2688 tr("Illegal property name"));
2692 for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) {
2693 const QQmlScript::Object::DynamicSignal &currSig = *s;
2695 if (methodNames.testAndSet(currSig.name.hash())) {
2696 for (Object::DynamicSignal *s2 = obj->dynamicSignals.first(); s2 != s;
2697 s2 = obj->dynamicSignals.next(s2)) {
2698 if (s2->name == currSig.name)
2699 COMPILE_EXCEPTION(&currSig, tr("Duplicate signal name"));
2703 if (currSig.name.at(0).isUpper())
2704 COMPILE_EXCEPTION(&currSig, tr("Signal names cannot begin with an upper case letter"));
2705 if (enginePrivate->v8engine()->illegalNames().contains(currSig.name))
2706 COMPILE_EXCEPTION(&currSig, tr("Illegal signal name"));
2709 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
2710 const QQmlScript::Object::DynamicSlot &currSlot = *s;
2712 if (methodNames.testAndSet(currSlot.name.hash())) {
2713 for (Object::DynamicSignal *s2 = obj->dynamicSignals.first(); s2;
2714 s2 = obj->dynamicSignals.next(s2)) {
2715 if (s2->name == currSlot.name)
2716 COMPILE_EXCEPTION(&currSlot, tr("Duplicate method name"));
2718 for (Object::DynamicSlot *s2 = obj->dynamicSlots.first(); s2 != s;
2719 s2 = obj->dynamicSlots.next(s2)) {
2720 if (s2->name == currSlot.name)
2721 COMPILE_EXCEPTION(&currSlot, tr("Duplicate method name"));
2725 if (currSlot.name.at(0).isUpper())
2726 COMPILE_EXCEPTION(&currSlot, tr("Method names cannot begin with an upper case letter"));
2727 if (enginePrivate->v8engine()->illegalNames().contains(currSlot.name))
2728 COMPILE_EXCEPTION(&currSlot, tr("Illegal method name"));
2734 bool QQmlCompiler::mergeDynamicMetaProperties(QQmlScript::Object *obj)
2736 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
2737 p = obj->dynamicProperties.next(p)) {
2739 if (!p->defaultValue || p->type == Object::DynamicProperty::Alias)
2742 Property *property = 0;
2743 if (p->isDefaultProperty) {
2744 property = obj->getDefaultProperty();
2746 property = obj->getProperty(p->name);
2747 if (!property->values.isEmpty())
2748 COMPILE_EXCEPTION(property, tr("Property value set multiple times"));
2752 property->isReadOnlyDeclaration = true;
2754 if (property->value)
2755 COMPILE_EXCEPTION(property, tr("Invalid property nesting"));
2757 property->values.append(p->defaultValue->values);
2762 #include <private/qqmljsparser_p.h>
2764 static QStringList astNodeToStringList(QQmlJS::AST::Node *node)
2766 if (node->kind == QQmlJS::AST::Node::Kind_IdentifierExpression) {
2768 static_cast<QQmlJS::AST::IdentifierExpression *>(node)->name.toString();
2769 return QStringList() << name;
2770 } else if (node->kind == QQmlJS::AST::Node::Kind_FieldMemberExpression) {
2771 QQmlJS::AST::FieldMemberExpression *expr = static_cast<QQmlJS::AST::FieldMemberExpression *>(node);
2773 QStringList rv = astNodeToStringList(expr->base);
2776 rv.append(expr->name.toString());
2779 return QStringList();
2782 static QAtomicInt classIndexCounter(0);
2784 bool QQmlCompiler::buildDynamicMeta(QQmlScript::Object *obj, DynamicMetaMode mode)
2787 Q_ASSERT(obj->metatype);
2789 if (mode != ForceCreation &&
2790 obj->dynamicProperties.isEmpty() &&
2791 obj->dynamicSignals.isEmpty() &&
2792 obj->dynamicSlots.isEmpty())
2795 Q_ASSERT(obj->synthCache == 0);
2798 Object::DynamicProperty::Type dtype;
2800 } builtinTypes[] = {
2801 { Object::DynamicProperty::Var, QMetaType::QVariant },
2802 { Object::DynamicProperty::Variant, QMetaType::QVariant },
2803 { Object::DynamicProperty::Int, QMetaType::Int },
2804 { Object::DynamicProperty::Bool, QMetaType::Bool },
2805 { Object::DynamicProperty::Real, QMetaType::Double },
2806 { Object::DynamicProperty::String, QMetaType::QString },
2807 { Object::DynamicProperty::Url, QMetaType::QUrl },
2808 { Object::DynamicProperty::Color, QMetaType::QColor },
2809 { Object::DynamicProperty::Font, QMetaType::QFont },
2810 { Object::DynamicProperty::Time, QMetaType::QTime },
2811 { Object::DynamicProperty::Date, QMetaType::QDate },
2812 { Object::DynamicProperty::DateTime, QMetaType::QDateTime },
2813 { Object::DynamicProperty::Rect, QMetaType::QRectF },
2814 { Object::DynamicProperty::Point, QMetaType::QPointF },
2815 { Object::DynamicProperty::Size, QMetaType::QSizeF },
2816 { Object::DynamicProperty::Vector2D, QMetaType::QVector2D },
2817 { Object::DynamicProperty::Vector3D, QMetaType::QVector3D },
2818 { Object::DynamicProperty::Vector4D, QMetaType::QVector4D },
2819 { Object::DynamicProperty::Matrix4x4, QMetaType::QMatrix4x4 },
2820 { Object::DynamicProperty::Quaternion, QMetaType::QQuaternion }
2822 static const int builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData);
2824 QByteArray newClassName;
2826 if (compileState->root == obj && !compileState->nested) {
2827 QString path = output->url.path();
2828 int lastSlash = path.lastIndexOf(QLatin1Char('/'));
2829 if (lastSlash > -1) {
2830 QString nameBase = path.mid(lastSlash + 1, path.length()-lastSlash-5);
2831 if (!nameBase.isEmpty() && nameBase.at(0).isUpper())
2832 newClassName = nameBase.toUtf8() + "_QMLTYPE_" +
2833 QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
2836 if (newClassName.isEmpty()) {
2837 newClassName = QQmlMetaObject(obj->metatype).className();
2838 newClassName.append("_QML_");
2839 newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)));
2841 QQmlPropertyCache *cache = obj->metatype->copyAndReserve(engine, obj->dynamicProperties.count(),
2842 obj->dynamicProperties.count() +
2843 obj->dynamicSignals.count() +
2844 obj->dynamicSlots.count(),
2845 obj->dynamicProperties.count() +
2846 obj->dynamicSignals.count());
2848 cache->_dynamicClassName = newClassName;
2850 int cStringNameCount = 0;
2853 int varPropCount = 0;
2855 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
2856 p = obj->dynamicProperties.next(p)) {
2858 if (p->type == Object::DynamicProperty::Alias)
2860 else if (p->type == Object::DynamicProperty::Var)
2863 if (p->name.isLatin1()) {
2864 p->nameIndex = cStringNameCount;
2865 cStringNameCount += p->name.length() + 7 /* strlen("Changed") */;
2868 // No point doing this for both the alias and non alias cases
2869 QQmlPropertyData *d = property(obj, p->name);
2870 if (d && d->isFinal())
2871 COMPILE_EXCEPTION(p, tr("Cannot override FINAL property"));
2874 for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) {
2875 if (s->name.isLatin1()) {
2876 s->nameIndex = cStringNameCount;
2877 cStringNameCount += s->name.length();
2881 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
2882 if (s->name.isLatin1()) {
2883 s->nameIndex = cStringNameCount;
2884 cStringNameCount += s->name.length();
2888 char *cStringData = 0;
2889 if (cStringNameCount) {
2890 cache->_dynamicStringData.resize(cStringNameCount);
2891 cStringData = cache->_dynamicStringData.data();
2893 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
2894 p = obj->dynamicProperties.next(p)) {
2896 if (p->nameIndex == -1) continue;
2898 char *myData = cStringData + p->nameIndex;
2899 for (int ii = 0; ii < p->name.length(); ++ii)
2900 *myData++ = p->name.at(ii).unicode();
2901 *myData++ = 'C'; *myData++ = 'h'; *myData++ = 'a'; *myData++ = 'n';
2902 *myData++ = 'g'; *myData++ = 'e'; *myData++ = 'd';
2905 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
2907 if (s->nameIndex == -1) continue;
2909 char *myData = cStringData + s->nameIndex;
2910 for (int ii = 0; ii < s->name.length(); ++ii)
2911 *myData++ = s->name.at(ii).unicode();
2914 for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s;
2915 s = obj->dynamicSignals.next(s)) {
2917 if (s->nameIndex == -1) continue;
2919 char *myData = cStringData + s->nameIndex;
2920 for (int ii = 0; ii < s->name.length(); ++ii)
2921 *myData++ = s->name.at(ii).unicode();
2925 QByteArray dynamicData;
2926 typedef QQmlVMEMetaData VMD;
2928 dynamicData = QByteArray(sizeof(QQmlVMEMetaData) +
2929 obj->dynamicProperties.count() * sizeof(VMD::PropertyData) +
2930 obj->dynamicSlots.count() * sizeof(VMD::MethodData) +
2931 aliasCount * sizeof(VMD::AliasData), 0);
2933 int effectivePropertyIndex = cache->propertyIndexCacheStart;
2934 int effectiveMethodIndex = cache->methodIndexCacheStart;
2936 // For property change signal override detection.
2937 // We prepopulate a set of signal names which already exist in the object,
2938 // and throw an error if there is a signal/method defined as an override.
2939 QSet<QString> seenSignals;
2940 seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged");
2941 QQmlPropertyCache *parentCache = cache;
2942 while ((parentCache = parentCache->parent())) {
2943 if (int pSigCount = parentCache->signalCount()) {
2944 int pSigOffset = parentCache->signalOffset();
2945 for (int i = 0; i < pSigCount; ++i) {
2946 QQmlPropertyData *currPSig = parentCache->signal(pSigOffset+i);
2947 if (!currPSig) continue;
2948 // XXX TODO: find a better way to get signal name from the property data :-/
2949 for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin();
2950 iter != parentCache->stringCache.end(); ++iter) {
2951 if (currPSig == iter.value()) {
2952 seenSignals.insert(iter.key());
2960 // First set up notify signals for properties - first normal, then var, then alias
2961 enum { NSS_Normal = 0, NSS_Var = 1, NSS_Alias = 2 };
2962 for (int ii = NSS_Normal; ii <= NSS_Alias; ++ii) { // 0 == normal, 1 == var, 2 == alias
2964 if (ii == NSS_Var && varPropCount == 0) continue;
2965 else if (ii == NSS_Alias && aliasCount == 0) continue;
2967 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
2968 p = obj->dynamicProperties.next(p)) {
2970 if ((ii == NSS_Normal && (p->type == Object::DynamicProperty::Alias ||
2971 p->type == Object::DynamicProperty::Var)) ||
2972 ((ii == NSS_Var) && (p->type != Object::DynamicProperty::Var)) ||
2973 ((ii == NSS_Alias) && (p->type != Object::DynamicProperty::Alias)))
2976 quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction |
2977 QQmlPropertyData::IsVMESignal;
2979 QString changedSigName = p->name.toString() + QLatin1String("Changed");
2980 seenSignals.insert(changedSigName);
2982 if (p->nameIndex != -1) {
2983 QHashedCStringRef changedSignalName(cStringData + p->nameIndex,
2984 p->name.length() + 7 /* strlen("Changed") */);
2985 cache->appendSignal(changedSignalName, flags, effectiveMethodIndex++);
2987 cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
2993 for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) {
2994 int paramCount = s->parameterNames.count();
2996 QList<QByteArray> names;
2997 QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0);
3000 paramTypes[0] = paramCount;
3002 for (int i = 0; i < paramCount; ++i) {
3003 if (s->parameterTypes.at(i) < builtinTypeCount) {
3005 paramTypes[i + 1] = builtinTypes[s->parameterTypes.at(i)].metaType;
3006 names.append(s->parameterNames.at(i).toString().toUtf8());
3008 // lazily resolved type
3009 Q_ASSERT(s->parameterTypes.at(i) == Object::DynamicProperty::Custom);
3010 QQmlType *qmltype = 0;
3012 if (!unit->imports().resolveType(s->parameterTypeNames.at(i).toString(), &qmltype, &url, 0, 0, 0))
3013 COMPILE_EXCEPTION(s, tr("Invalid signal parameter type: %1").arg(s->parameterTypeNames.at(i).toString()));
3016 QQmlTypeData *tdata = enginePrivate->typeLoader.getType(QUrl(url));
3018 Q_ASSERT(tdata->isComplete());
3020 QQmlCompiledData *data = tdata->compiledData();
3022 paramTypes[i + 1] = data->metaTypeId;
3026 paramTypes[i + 1] = qmltype->typeId();
3028 names.append(s->parameterNames.at(i).toString().toUtf8());
3033 ((QQmlVMEMetaData *)dynamicData.data())->signalCount++;
3035 quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction |
3036 QQmlPropertyData::IsVMESignal;
3038 flags |= QQmlPropertyData::HasArguments;
3040 QString signalName = s->name.toString();
3041 if (seenSignals.contains(signalName)) {
3042 const QQmlScript::Object::DynamicSignal &currSig = *s;
3043 COMPILE_EXCEPTION(&currSig, tr("Duplicate signal name: invalid override of property change signal or superclass signal"));
3045 seenSignals.insert(signalName);
3047 if (s->nameIndex != -1) {
3048 QHashedCStringRef name(cStringData + s->nameIndex, s->name.length(), s->name.hash());
3049 cache->appendSignal(name, flags, effectiveMethodIndex++,
3050 paramCount?paramTypes.constData():0, names);
3052 cache->appendSignal(signalName, flags, effectiveMethodIndex++,
3053 paramCount?paramTypes.constData():0, names);
3059 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
3060 int paramCount = s->parameterNames.count();
3062 quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction;
3065 flags |= QQmlPropertyData::HasArguments;
3067 QString slotName = s->name.toString();
3068 if (seenSignals.contains(slotName)) {
3069 const QQmlScript::Object::DynamicSlot &currSlot = *s;
3070 COMPILE_EXCEPTION(&currSlot, tr("Duplicate method name: invalid override of property change signal or superclass signal"));
3072 // Note: we don't append slotName to the seenSignals list, since we don't
3073 // protect against overriding change signals or methods with properties.
3075 if (s->nameIndex != -1) {
3076 QHashedCStringRef name(cStringData + s->nameIndex, s->name.length(), s->name.hash());
3077 cache->appendMethod(name, flags, effectiveMethodIndex++, s->parameterNames);
3079 cache->appendMethod(slotName, flags, effectiveMethodIndex++, s->parameterNames);
3084 // Dynamic properties (except var and aliases)
3085 int effectiveSignalIndex = cache->signalHandlerIndexCacheStart;
3086 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
3087 p = obj->dynamicProperties.next(p)) {
3089 if (p->type == Object::DynamicProperty::Alias ||
3090 p->type == Object::DynamicProperty::Var)
3093 int propertyType = 0;
3094 int vmePropertyType = 0;
3095 quint32 propertyFlags = 0;
3097 if (p->type < builtinTypeCount) {
3098 propertyType = builtinTypes[p->type].metaType;
3099 vmePropertyType = propertyType;
3101 if (p->type == Object::DynamicProperty::Variant)
3102 propertyFlags |= QQmlPropertyData::IsQVariant;
3104 Q_ASSERT(p->type == Object::DynamicProperty::CustomList ||
3105 p->type == Object::DynamicProperty::Custom);
3107 QQmlType *qmltype = 0;
3109 if (!unit->imports().resolveType(p->customType.toString(), &qmltype, &url, 0, 0, 0))
3110 COMPILE_EXCEPTION(p, tr("Invalid property type"));
3113 QQmlTypeData *tdata = enginePrivate->typeLoader.getType(QUrl(url));
3115 Q_ASSERT(tdata->isComplete());
3117 QQmlCompiledData *data = tdata->compiledData();
3119 if (p->type == Object::DynamicProperty::Custom) {
3120 propertyType = data->metaTypeId;
3121 vmePropertyType = QMetaType::QObjectStar;
3123 propertyType = data->listMetaTypeId;
3124 vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >();
3129 if (p->type == Object::DynamicProperty::Custom) {
3130 propertyType = qmltype->typeId();
3131 vmePropertyType = QMetaType::QObjectStar;
3133 propertyType = qmltype->qListTypeId();
3134 vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >();
3138 if (p->type == Object::DynamicProperty::Custom)
3139 propertyFlags |= QQmlPropertyData::IsQObjectDerived;
3141 propertyFlags |= QQmlPropertyData::IsQList;
3144 if (!p->isReadOnly && p->type != Object::DynamicProperty::CustomList)
3145 propertyFlags |= QQmlPropertyData::IsWritable;
3147 if (p->nameIndex != -1) {
3148 QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(),
3150 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16();
3151 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3152 propertyType, effectiveSignalIndex);
3154 QString propertyName = p->name.toString();
3155 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName;
3156 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3157 propertyType, effectiveSignalIndex);
3160 effectiveSignalIndex++;
3162 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3163 (vmd->propertyData() + vmd->propertyCount)->propertyType = vmePropertyType;
3164 vmd->propertyCount++;
3167 // Now do var properties
3168 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p && varPropCount;
3169 p = obj->dynamicProperties.next(p)) {
3171 if (p->type != Object::DynamicProperty::Var)
3174 quint32 propertyFlags = QQmlPropertyData::IsVarProperty;
3176 propertyFlags |= QQmlPropertyData::IsWritable;
3178 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3179 (vmd->propertyData() + vmd->propertyCount)->propertyType = QMetaType::QVariant;
3180 vmd->propertyCount++;
3181 ((QQmlVMEMetaData *)dynamicData.data())->varPropertyCount++;
3183 if (p->nameIndex != -1) {
3184 QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(),
3186 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16();
3187 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3188 QMetaType::QVariant, effectiveSignalIndex);
3190 QString propertyName = p->name.toString();
3191 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName;
3192 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3193 QMetaType::QVariant, effectiveSignalIndex);
3196 effectiveSignalIndex++;
3199 // Alias property count. Actual data is setup in buildDynamicMetaAliases
3200 ((QQmlVMEMetaData *)dynamicData.data())->aliasCount = aliasCount;
3202 // Dynamic slot data - comes after the property data
3203 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
3204 int paramCount = s->parameterNames.count();
3208 if (paramCount) namesSize += s->parameterNamesLength() + (paramCount - 1 /* commas */);
3209 funcScript.reserve(strlen("(function ") + s->name.length() + 1 /* lparen */ +
3210 namesSize + 1 /* rparen */ + s->body.length() + 1 /* rparen */);
3211 funcScript = QLatin1String("(function ") + s->name.toString() + QLatin1Char('(');
3212 for (int jj = 0; jj < paramCount; ++jj) {
3213 if (jj) funcScript.append(QLatin1Char(','));
3214 funcScript.append(QLatin1String(s->parameterNames.at(jj)));
3216 funcScript += QLatin1Char(')') + s->body + QLatin1Char(')');
3218 QByteArray utf8 = funcScript.toUtf8();
3219 VMD::MethodData methodData = { s->parameterNames.count(),
3222 s->location.start.line };
3224 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3225 VMD::MethodData &md = *(vmd->methodData() + vmd->methodCount);
3229 dynamicData.append((const char *)utf8.constData(), utf8.length());
3233 compileState->aliasingObjects.append(obj);
3235 obj->synthdata = dynamicData;
3236 obj->synthCache = cache;
3237 obj->metatype = cache;
3242 bool QQmlCompiler::buildDynamicMetaAliases(QQmlScript::Object *obj)
3244 Q_ASSERT(obj->synthCache);
3246 QByteArray &dynamicData = obj->synthdata;
3248 QQmlPropertyCache *cache = obj->synthCache;
3249 char *cStringData = cache->_dynamicStringData.data();
3251 int effectiveSignalIndex = cache->signalHandlerIndexCacheStart + cache->propertyIndexCache.count();
3252 int effectivePropertyIndex = cache->propertyIndexCacheStart + cache->propertyIndexCache.count();
3253 int effectiveAliasIndex = 0;
3255 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
3256 p = obj->dynamicProperties.next(p)) {
3258 if (p->type != Object::DynamicProperty::Alias)
3261 if (!p->defaultValue)
3262 COMPILE_EXCEPTION(obj, tr("No property alias location"));
3264 if (!p->defaultValue->values.isOne() ||
3265 p->defaultValue->values.first()->object ||
3266 !p->defaultValue->values.first()->value.isScript())
3267 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3269 QQmlJS::AST::Node *node = p->defaultValue->values.first()->value.asAST();
3272 QStringList alias = astNodeToStringList(node);
3273 if (alias.count() < 1 || alias.count() > 3)
3274 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
3276 QQmlScript::Object *idObject = compileState->ids.value(alias.at(0));
3278 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias reference. Unable to find id \"%1\"").arg(alias.at(0)));
3282 int notifySignal = -1;
3285 bool writable = false;
3286 bool resettable = false;
3288 quint32 propertyFlags = QQmlPropertyData::IsAlias;
3290 if (alias.count() == 2 || alias.count() == 3) {
3291 QQmlPropertyData *property = this->property(idObject, alias.at(1));
3293 if (!property || property->coreIndex > 0x0000FFFF)
3294 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3296 propIdx = property->coreIndex;
3297 type = property->propType;
3299 writable = property->isWritable();
3300 resettable = property->isResettable();
3301 notifySignal = property->notifyIndex;
3303 if (alias.count() == 3) {
3304 QQmlValueType *valueType = QQmlValueTypeFactory::valueType(type);
3306 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3310 int valueTypeIndex =
3311 valueType->metaObject()->indexOfProperty(alias.at(2).toUtf8().constData());
3312 if (valueTypeIndex == -1)
3313 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3314 Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
3316 propIdx |= (valueTypeIndex << 16);
3317 if (valueType->metaObject()->property(valueTypeIndex).isEnumType())
3318 type = QVariant::Int;
3320 type = valueType->metaObject()->property(valueTypeIndex).userType();
3323 if (property->isEnum()) {
3324 type = QVariant::Int;
3327 propertyFlags |= property->getFlags() & QQmlPropertyData::PropTypeFlagMask;
3329 if (property->isVarProperty())
3330 propertyFlags |= QQmlPropertyData::IsQVariant;
3332 if (property->isQObject())
3333 flags |= QML_ALIAS_FLAG_PTR;
3337 Q_ASSERT(idObject->type != -1); // How else did it get an id?
3339 const QQmlCompiledData::TypeReference &ref = output->types.at(idObject->type);
3341 type = ref.type->typeId();
3343 type = ref.component->metaTypeId;
3345 flags |= QML_ALIAS_FLAG_PTR;
3346 propertyFlags |= QQmlPropertyData::IsQObjectDerived;
3349 QQmlVMEMetaData::AliasData aliasData = { idObject->idIndex, propIdx, propType, flags, notifySignal };
3351 typedef QQmlVMEMetaData VMD;
3352 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3353 *(vmd->aliasData() + effectiveAliasIndex++) = aliasData;
3355 if (!p->isReadOnly && writable)
3356 propertyFlags |= QQmlPropertyData::IsWritable;
3358 propertyFlags &= ~QQmlPropertyData::IsWritable;
3361 propertyFlags |= QQmlPropertyData::IsResettable;
3363 propertyFlags &= ~QQmlPropertyData::IsResettable;
3365 if (p->nameIndex != -1) {
3366 QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(),
3368 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16();
3369 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3370 type, effectiveSignalIndex++);
3372 QString propertyName = p->name.toString();
3373 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName;
3374 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3375 type, effectiveSignalIndex++);
3382 bool QQmlCompiler::checkValidId(QQmlScript::Value *v, const QString &val)
3385 COMPILE_EXCEPTION(v, tr( "Invalid empty ID"));
3387 QChar ch = val.at(0);
3388 if (ch.isLetter() && !ch.isLower())
3389 COMPILE_EXCEPTION(v, tr( "IDs cannot start with an uppercase letter"));
3391 QChar u(QLatin1Char('_'));
3392 if (!ch.isLetter() && ch != u)
3393 COMPILE_EXCEPTION(v, tr( "IDs must start with a letter or underscore"));
3395 for (int ii = 1; ii < val.count(); ++ii) {
3397 if (!ch.isLetterOrNumber() && ch != u)
3398 COMPILE_EXCEPTION(v, tr( "IDs must contain only letters, numbers, and underscores"));
3401 if (enginePrivate->v8engine()->illegalNames().contains(val))
3402 COMPILE_EXCEPTION(v, tr( "ID illegally masks global JavaScript property"));
3407 bool QQmlCompiler::buildBinding(QQmlScript::Value *value,
3408 QQmlScript::Property *prop,
3409 const BindingContext &ctxt)
3411 Q_ASSERT(prop->index != -1);
3412 Q_ASSERT(prop->parent);
3413 Q_ASSERT(prop->parent->metatype);
3415 if (!prop->core.isWritable() && !prop->core.isQList() && !prop->isReadOnlyDeclaration)
3416 COMPILE_EXCEPTION(prop, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
3418 JSBindingReference *reference = pool->New<JSBindingReference>();
3419 reference->expression = value->value;
3420 reference->property = prop;
3421 reference->value = value;
3422 reference->bindingContext = ctxt;
3423 addBindingReference(reference);
3428 bool QQmlCompiler::buildLiteralBinding(QQmlScript::Value *v,
3429 QQmlScript::Property *prop,
3430 const QQmlCompilerTypes::BindingContext &)
3432 Q_ASSERT(v->value.isScript());
3434 if (!prop->core.isWritable())
3437 AST::Node *binding = v->value.asAST();
3439 if (prop->type == QVariant::String) {
3440 if (AST::CallExpression *e = AST::cast<AST::CallExpression *>(binding)) {
3441 if (AST::IdentifierExpression *i = AST::cast<AST::IdentifierExpression *>(e->base)) {
3442 if (i->name == qsTrId_string) {
3443 AST::ArgumentList *arg1 = e->arguments?e->arguments:0;
3444 AST::ArgumentList *arg2 = arg1?arg1->next:0;
3446 if (arg1 && arg1->expression->kind == AST::Node::Kind_StringLiteral &&
3447 (!arg2 || arg2->expression->kind == AST::Node::Kind_NumericLiteral) &&
3448 (!arg2 || !arg2->next)) {
3453 text = AST::cast<AST::StringLiteral *>(arg1->expression)->value;
3454 if (arg2) n = (int)AST::cast<AST::NumericLiteral *>(arg2->expression)->value;
3456 TrBindingReference *reference = pool->New<TrBindingReference>();
3457 reference->dataType = BindingReference::TrId;
3458 reference->text = text;
3460 v->bindingReference = reference;
3464 } else if (i->name == qsTr_string) {
3466 AST::ArgumentList *arg1 = e->arguments?e->arguments:0;
3467 AST::ArgumentList *arg2 = arg1?arg1->next:0;
3468 AST::ArgumentList *arg3 = arg2?arg2->next:0;
3470 if (arg1 && arg1->expression->kind == AST::Node::Kind_StringLiteral &&
3471 (!arg2 || arg2->expression->kind == AST::Node::Kind_StringLiteral) &&
3472 (!arg3 || arg3->expression->kind == AST::Node::Kind_NumericLiteral) &&
3473 (!arg3 || !arg3->next)) {
3479 text = AST::cast<AST::StringLiteral *>(arg1->expression)->value;
3480 if (arg2) comment = AST::cast<AST::StringLiteral *>(arg2->expression)->value;
3481 if (arg3) n = (int)AST::cast<AST::NumericLiteral *>(arg3->expression)->value;
3483 TrBindingReference *reference = pool->New<TrBindingReference>();
3484 reference->dataType = BindingReference::Tr;
3485 reference->text = text;
3486 reference->comment = comment;
3488 v->bindingReference = reference;
3501 void QQmlCompiler::genBindingAssignment(QQmlScript::Value *binding,
3502 QQmlScript::Property *prop,
3503 QQmlScript::Object *obj,
3504 QQmlScript::Property *valueTypeProperty)
3507 Q_ASSERT(binding->bindingReference);
3509 const BindingReference &ref = *binding->bindingReference;
3510 if (ref.dataType == BindingReference::TrId) {
3511 const TrBindingReference &tr = static_cast<const TrBindingReference &>(ref);
3513 Instruction::StoreTrIdString store;
3514 store.propertyIndex = prop->core.coreIndex;
3515 store.text = output->indexForByteArray(tr.text.toUtf8());
3517 output->addInstruction(store);
3518 } else if (ref.dataType == BindingReference::Tr) {
3519 const TrBindingReference &tr = static_cast<const TrBindingReference &>(ref);
3521 Instruction::StoreTrString store;
3522 store.propertyIndex = prop->core.coreIndex;
3523 store.context = translationContextIndex();
3524 store.text = output->indexForByteArray(tr.text.toUtf8());
3525 store.comment = output->indexForByteArray(tr.comment.toUtf8());
3527 output->addInstruction(store);
3528 } else if (ref.dataType == BindingReference::V4) {
3529 const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);
3531 Instruction::StoreV4Binding store;
3532 store.value = js.compiledIndex;
3533 store.fallbackValue = js.sharedIndex;
3534 store.context = js.bindingContext.stack;
3535 store.owner = js.bindingContext.owner;
3536 store.isAlias = prop->isAlias;
3537 if (valueTypeProperty) {
3538 store.property = ((prop->index << 16) | valueTypeProperty->index);
3539 store.propType = valueTypeProperty->type;
3540 store.isRoot = (compileState->root == valueTypeProperty->parent);
3542 store.property = prop->index;
3544 store.isRoot = (compileState->root == obj);
3546 store.line = binding->location.start.line;
3547 store.column = binding->location.start.column;
3548 output->addInstruction(store);
3550 if (store.fallbackValue > -1) {
3551 //also create v8 instruction (needed to properly configure the fallback v8 binding)
3552 JSBindingReference &js = static_cast<JSBindingReference &>(*binding->bindingReference);
3553 js.dataType = BindingReference::V8;
3554 genBindingAssignment(binding, prop, obj, valueTypeProperty);
3556 } else if (ref.dataType == BindingReference::V8) {
3557 const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);
3559 Instruction::StoreV8Binding store;
3560 store.value = js.sharedIndex;
3561 store.context = js.bindingContext.stack;
3562 store.owner = js.bindingContext.owner;
3563 store.isAlias = prop->isAlias;
3564 if (valueTypeProperty) {
3565 store.isRoot = (compileState->root == valueTypeProperty->parent);
3567 store.isRoot = (compileState->root == obj);
3569 store.isFallback = js.compiledIndex > -1;
3570 store.line = binding->location.start.line;
3571 store.column = binding->location.start.column;
3573 Q_ASSERT(js.bindingContext.owner == 0 ||
3574 (js.bindingContext.owner != 0 && valueTypeProperty));
3575 if (js.bindingContext.owner) {
3576 store.property = genValueTypeData(prop, valueTypeProperty);
3578 store.property = prop->core;
3581 output->addInstruction(store);
3582 } else if (ref.dataType == BindingReference::QtScript) {
3583 const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);
3585 Instruction::StoreBinding store;
3586 store.value = output->indexForString(js.rewrittenExpression);
3587 store.context = js.bindingContext.stack;
3588 store.owner = js.bindingContext.owner;
3589 store.line = binding->location.start.line;
3590 store.column = binding->location.start.column;
3591 store.isAlias = prop->isAlias;
3593 if (valueTypeProperty) {
3594 store.isRoot = (compileState->root == valueTypeProperty->parent);
3596 store.isRoot = (compileState->root == obj);
3598 store.isFallback = false;
3600 Q_ASSERT(js.bindingContext.owner == 0 ||
3601 (js.bindingContext.owner != 0 && valueTypeProperty));
3602 if (js.bindingContext.owner) {
3603 store.property = genValueTypeData(prop, valueTypeProperty);
3605 store.property = prop->core;
3608 output->addInstruction(store);
3610 Q_ASSERT(!"Unhandled BindingReference::DataType type");
3614 int QQmlCompiler::genContextCache()
3616 if (compileState->ids.count() == 0)
3619 QQmlIntegerCache *cache = new QQmlIntegerCache();
3620 cache->reserve(compileState->ids.count());
3621 for (Object *o = compileState->ids.first(); o; o = compileState->ids.next(o))
3622 cache->add(o->id, o->idIndex);
3624 output->contextCaches.append(cache);
3625 return output->contextCaches.count() - 1;
3629 QQmlCompiler::genValueTypeData(QQmlScript::Property *valueTypeProp,
3630 QQmlScript::Property *prop)
3632 QQmlValueType *vt = QQmlValueTypeFactory::valueType(prop->type);
3634 return QQmlPropertyPrivate::saveValueType(prop->core, vt->metaObject(), valueTypeProp->index, engine);
3637 bool QQmlCompiler::completeComponentBuild()
3640 componentStats->componentStat.ids = compileState->ids.count();
3642 for (Object *aliasObject = compileState->aliasingObjects.first(); aliasObject;
3643 aliasObject = compileState->aliasingObjects.next(aliasObject))
3644 COMPILE_CHECK(buildDynamicMetaAliases(aliasObject));
3646 QV4Compiler::Expression expr(unit->imports());
3647 expr.component = compileState->root;
3648 expr.ids = &compileState->ids;
3649 expr.importCache = output->importCache;
3651 QV4Compiler bindingCompiler;
3653 QList<JSBindingReference*> sharedBindings;
3655 for (JSBindingReference *b = compileState->bindings.first(); b; b = b->nextReference) {
3657 JSBindingReference &binding = *b;
3660 expr.context = binding.bindingContext.object;
3661 expr.property = binding.property;
3662 expr.expression = binding.expression;
3664 bool needsFallback = false;
3665 int index = bindingCompiler.compile(expr, enginePrivate, &needsFallback);
3667 binding.dataType = BindingReference::V4;
3668 binding.compiledIndex = index;
3669 binding.sharedIndex = -1;
3671 componentStats->componentStat.optimizedBindings.append(b->value->location);
3676 // Drop through. We need to create a V8 binding in case the V4 binding is invalidated
3679 // Pre-rewrite the expression
3680 QString expression = binding.expression.asScript();
3682 QQmlRewrite::RewriteBinding rewriteBinding;
3683 rewriteBinding.setName(QLatin1Char('$')+binding.property->name().toString());
3684 bool isSharable = false;
3685 binding.rewrittenExpression = rewriteBinding(binding.expression.asAST(), expression, &isSharable);
3687 if (isSharable && binding.property->type != qMetaTypeId<QQmlBinding*>()) {
3688 sharedBindings.append(b);
3690 if (!needsFallback) {
3691 binding.dataType = BindingReference::V8;
3692 binding.compiledIndex = -1;
3695 componentStats->componentStat.sharedBindings.append(b->value->location);
3698 Q_ASSERT(!needsFallback);
3699 binding.dataType = BindingReference::QtScript;
3702 componentStats->componentStat.scriptBindings.append(b->value->location);
3706 if (!sharedBindings.isEmpty()) {
3708 static bool lt(const JSBindingReference *lhs, const JSBindingReference *rhs)
3710 return lhs->value->location.start.line < rhs->value->location.start.line;
3714 qSort(sharedBindings.begin(), sharedBindings.end(), Sort::lt);
3716 int startLineNumber = sharedBindings.at(0)->value->location.start.line;
3717 int lineNumber = startLineNumber;
3719 QByteArray functionArray("[", 1);
3720 for (int ii = 0; ii < sharedBindings.count(); ++ii) {
3722 JSBindingReference *reference = sharedBindings.at(ii);
3723 QQmlScript::Value *value = reference->value;
3724 const QString &expression = reference->rewrittenExpression;
3726 if (ii != 0) functionArray.append(",", 1);
3728 while (lineNumber < value->location.start.line) {
3730 functionArray.append("\n", 1);
3733 functionArray += expression.toUtf8();
3734 lineNumber += expression.count(QLatin1Char('\n'));
3736 reference->sharedIndex = ii;
3738 functionArray.append("]", 1);
3740 compileState->v8BindingProgram = functionArray;
3741 compileState->v8BindingProgramLine = startLineNumber;
3744 if (bindingCompiler.isValid())
3745 compileState->compiledBindingData = bindingCompiler.program();
3747 // Check pop()'s matched push()'s
3748 Q_ASSERT(compileState->objectDepth.depth() == 0);
3749 Q_ASSERT(compileState->listDepth.depth() == 0);
3751 saveComponentState();
3756 void QQmlCompiler::dumpStats()
3758 Q_ASSERT(componentStats);
3759 qWarning().nospace() << "QML Document: " << output->url.toString();
3760 for (int ii = 0; ii < componentStats->savedComponentStats.count(); ++ii) {
3761 const ComponentStat &stat = componentStats->savedComponentStats.at(ii);
3762 qWarning().nospace() << " Component Line " << stat.lineNumber;
3763 qWarning().nospace() << " Total Objects: " << stat.objects;
3764 qWarning().nospace() << " IDs Used: " << stat.ids;
3765 qWarning().nospace() << " Optimized Bindings: " << stat.optimizedBindings.count();
3769 for (int ii = 0; ii < stat.optimizedBindings.count(); ++ii) {
3770 if (0 == (ii % 10)) {
3771 if (ii) output.append("\n");
3776 output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.line));
3778 output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.column));
3779 output.append(") ");
3781 if (!output.isEmpty())
3782 qWarning().nospace() << output.constData();
3785 qWarning().nospace() << " Shared Bindings: " << stat.sharedBindings.count();
3788 for (int ii = 0; ii < stat.sharedBindings.count(); ++ii) {
3789 if (0 == (ii % 10)) {
3790 if (ii) output.append('\n');
3795 output.append(QByteArray::number(stat.sharedBindings.at(ii).start.line));
3797 output.append(QByteArray::number(stat.sharedBindings.at(ii).start.column));
3798 output.append(") ");
3800 if (!output.isEmpty())
3801 qWarning().nospace() << output.constData();
3804 qWarning().nospace() << " QScript Bindings: " << stat.scriptBindings.count();
3807 for (int ii = 0; ii < stat.scriptBindings.count(); ++ii) {
3808 if (0 == (ii % 10)) {
3809 if (ii) output.append('\n');
3814 output.append(QByteArray::number(stat.scriptBindings.at(ii).start.line));
3816 output.append(QByteArray::number(stat.scriptBindings.at(ii).start.column));
3817 output.append(") ");
3819 if (!output.isEmpty())
3820 qWarning().nospace() << output.constData();
3826 Returns true if from can be assigned to a (QObject) property of type
3829 bool QQmlCompiler::canCoerce(int to, QQmlScript::Object *from)
3831 QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to);
3832 QQmlPropertyCache *fromMo = from->metatype;
3837 fromMo = fromMo->parent();
3843 Returns the element name, as written in the QML file, for o.
3845 QString QQmlCompiler::elementName(QQmlScript::Object *o)
3848 if (o->type != -1) {
3849 return unit->parser().referencedTypes().at(o->type)->name;
3855 QQmlType *QQmlCompiler::toQmlType(QQmlScript::Object *from)
3857 if (from->type != -1 && output->types.at(from->type).type)
3858 return output->types.at(from->type).type;
3860 const QMetaObject *mo = from->metatype->firstCppMetaObject();
3862 while (!type && mo) {
3863 type = QQmlMetaType::qmlType(mo);
3864 mo = mo->superClass();
3869 QStringList QQmlCompiler::deferredProperties(QQmlScript::Object *obj)
3871 const QMetaObject *mo = obj->metatype->firstCppMetaObject();
3873 int idx = mo->indexOfClassInfo("DeferredPropertyNames");
3875 return QStringList();
3877 QMetaClassInfo classInfo = mo->classInfo(idx);
3878 QStringList rv = QString::fromUtf8(classInfo.value()).split(QLatin1Char(','));
3883 QQmlCompiler::propertyCacheForObject(QQmlScript::Object *object)
3885 if (object->synthCache)
3886 return object->synthCache;
3887 else if (object->type != -1)
3888 return output->types[object->type].createPropertyCache(engine);
3890 return object->metatype;
3894 QQmlCompiler::property(QQmlScript::Object *object, int index)
3896 QQmlPropertyCache *cache = propertyCacheForObject(object);
3898 return cache->property(index);
3902 QQmlCompiler::property(QQmlScript::Object *object, const QHashedStringRef &name, bool *notInRevision)
3904 if (notInRevision) *notInRevision = false;
3906 QQmlPropertyCache *cache = propertyCacheForObject(object);
3908 QQmlPropertyData *d = cache->property(name);
3910 // Find the first property
3911 while (d && d->isFunction())
3912 d = cache->overrideData(d);
3914 if (d && !cache->isAllowedInRevision(d)) {
3915 if (notInRevision) *notInRevision = true;
3922 // This code must match the semantics of QQmlPropertyPrivate::findSignalByName
3924 QQmlCompiler::signal(QQmlScript::Object *object, const QHashedStringRef &name, bool *notInRevision)
3926 if (notInRevision) *notInRevision = false;
3928 QQmlPropertyCache *cache = propertyCacheForObject(object);
3931 QQmlPropertyData *d = cache->property(name);
3932 if (notInRevision) *notInRevision = false;
3934 while (d && !(d->isFunction()))
3935 d = cache->overrideData(d);
3937 if (d && !cache->isAllowedInRevision(d)) {
3938 if (notInRevision) *notInRevision = true;
3940 } else if (d && d->isSignal()) {
3944 if (name.endsWith(Changed_string)) {
3945 QHashedStringRef propName = name.mid(0, name.length() - Changed_string.length());
3947 d = property(object, propName, notInRevision);
3949 return cache->signal(d->notifyIndex);
3955 // This code must match the semantics of QQmlPropertyPrivate::findSignalByName
3956 int QQmlCompiler::indexOfSignal(QQmlScript::Object *object, const QString &name,
3957 bool *notInRevision)
3959 QQmlPropertyData *d = signal(object, QStringRef(&name), notInRevision);
3960 return d?d->coreIndex:-1;
3963 int QQmlCompiler::indexOfProperty(QQmlScript::Object *object, const QString &name,
3964 bool *notInRevision)
3966 return indexOfProperty(object, QStringRef(&name), notInRevision);
3969 int QQmlCompiler::indexOfProperty(QQmlScript::Object *object, const QHashedStringRef &name,
3970 bool *notInRevision)
3972 QQmlPropertyData *d = property(object, name, notInRevision);
3973 return d?d->coreIndex:-1;