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 = pSigOffset; i < pSigCount; ++i) {
2946 QQmlPropertyData *currPSig = parentCache->signal(i);
2947 // XXX TODO: find a better way to get signal name from the property data :-/
2948 for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin();
2949 iter != parentCache->stringCache.end(); ++iter) {
2950 if (currPSig == iter.value()) {
2951 seenSignals.insert(iter.key());
2959 // First set up notify signals for properties - first normal, then var, then alias
2960 enum { NSS_Normal = 0, NSS_Var = 1, NSS_Alias = 2 };
2961 for (int ii = NSS_Normal; ii <= NSS_Alias; ++ii) { // 0 == normal, 1 == var, 2 == alias
2963 if (ii == NSS_Var && varPropCount == 0) continue;
2964 else if (ii == NSS_Alias && aliasCount == 0) continue;
2966 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
2967 p = obj->dynamicProperties.next(p)) {
2969 if ((ii == NSS_Normal && (p->type == Object::DynamicProperty::Alias ||
2970 p->type == Object::DynamicProperty::Var)) ||
2971 ((ii == NSS_Var) && (p->type != Object::DynamicProperty::Var)) ||
2972 ((ii == NSS_Alias) && (p->type != Object::DynamicProperty::Alias)))
2975 quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction |
2976 QQmlPropertyData::IsVMESignal;
2978 QString changedSigName = p->name.toString() + QLatin1String("Changed");
2979 seenSignals.insert(changedSigName);
2981 if (p->nameIndex != -1) {
2982 QHashedCStringRef changedSignalName(cStringData + p->nameIndex,
2983 p->name.length() + 7 /* strlen("Changed") */);
2984 cache->appendSignal(changedSignalName, flags, effectiveMethodIndex++);
2986 cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
2992 for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) {
2993 int paramCount = s->parameterNames.count();
2995 QList<QByteArray> names;
2996 QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0);
2999 paramTypes[0] = paramCount;
3001 for (int i = 0; i < paramCount; ++i) {
3002 if (s->parameterTypes.at(i) < builtinTypeCount) {
3004 paramTypes[i + 1] = builtinTypes[s->parameterTypes.at(i)].metaType;
3005 names.append(s->parameterNames.at(i).toString().toUtf8());
3007 // lazily resolved type
3008 Q_ASSERT(s->parameterTypes.at(i) == Object::DynamicProperty::Custom);
3009 QQmlType *qmltype = 0;
3011 if (!unit->imports().resolveType(s->parameterTypeNames.at(i).toString(), &qmltype, &url, 0, 0, 0))
3012 COMPILE_EXCEPTION(s, tr("Invalid signal parameter type: %1").arg(s->parameterTypeNames.at(i).toString()));
3015 QQmlTypeData *tdata = enginePrivate->typeLoader.getType(QUrl(url));
3017 Q_ASSERT(tdata->isComplete());
3019 QQmlCompiledData *data = tdata->compiledData();
3021 paramTypes[i + 1] = data->metaTypeId;
3025 paramTypes[i + 1] = qmltype->typeId();
3027 names.append(s->parameterNames.at(i).toString().toUtf8());
3032 ((QQmlVMEMetaData *)dynamicData.data())->signalCount++;
3034 quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction |
3035 QQmlPropertyData::IsVMESignal;
3037 flags |= QQmlPropertyData::HasArguments;
3039 QString signalName = s->name.toString();
3040 if (seenSignals.contains(signalName)) {
3041 const QQmlScript::Object::DynamicSignal &currSig = *s;
3042 COMPILE_EXCEPTION(&currSig, tr("Duplicate signal name: invalid override of property change signal or superclass signal"));
3044 seenSignals.insert(signalName);
3046 if (s->nameIndex != -1) {
3047 QHashedCStringRef name(cStringData + s->nameIndex, s->name.length(), s->name.hash());
3048 cache->appendSignal(name, flags, effectiveMethodIndex++,
3049 paramCount?paramTypes.constData():0, names);
3051 cache->appendSignal(signalName, flags, effectiveMethodIndex++,
3052 paramCount?paramTypes.constData():0, names);
3058 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
3059 int paramCount = s->parameterNames.count();
3061 quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction;
3064 flags |= QQmlPropertyData::HasArguments;
3066 QString slotName = s->name.toString();
3067 if (seenSignals.contains(slotName)) {
3068 const QQmlScript::Object::DynamicSlot &currSlot = *s;
3069 COMPILE_EXCEPTION(&currSlot, tr("Duplicate method name: invalid override of property change signal or superclass signal"));
3071 // Note: we don't append slotName to the seenSignals list, since we don't
3072 // protect against overriding change signals or methods with properties.
3074 if (s->nameIndex != -1) {
3075 QHashedCStringRef name(cStringData + s->nameIndex, s->name.length(), s->name.hash());
3076 cache->appendMethod(name, flags, effectiveMethodIndex++, s->parameterNames);
3078 cache->appendMethod(slotName, flags, effectiveMethodIndex++, s->parameterNames);
3083 // Dynamic properties (except var and aliases)
3084 int effectiveSignalIndex = cache->signalHandlerIndexCacheStart;
3085 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
3086 p = obj->dynamicProperties.next(p)) {
3088 if (p->type == Object::DynamicProperty::Alias ||
3089 p->type == Object::DynamicProperty::Var)
3092 int propertyType = 0;
3093 int vmePropertyType = 0;
3094 quint32 propertyFlags = 0;
3096 if (p->type < builtinTypeCount) {
3097 propertyType = builtinTypes[p->type].metaType;
3098 vmePropertyType = propertyType;
3100 if (p->type == Object::DynamicProperty::Variant)
3101 propertyFlags |= QQmlPropertyData::IsQVariant;
3103 Q_ASSERT(p->type == Object::DynamicProperty::CustomList ||
3104 p->type == Object::DynamicProperty::Custom);
3106 QQmlType *qmltype = 0;
3108 if (!unit->imports().resolveType(p->customType.toString(), &qmltype, &url, 0, 0, 0))
3109 COMPILE_EXCEPTION(p, tr("Invalid property type"));
3112 QQmlTypeData *tdata = enginePrivate->typeLoader.getType(QUrl(url));
3114 Q_ASSERT(tdata->isComplete());
3116 QQmlCompiledData *data = tdata->compiledData();
3118 if (p->type == Object::DynamicProperty::Custom) {
3119 propertyType = data->metaTypeId;
3120 vmePropertyType = QMetaType::QObjectStar;
3122 propertyType = data->listMetaTypeId;
3123 vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >();
3128 if (p->type == Object::DynamicProperty::Custom) {
3129 propertyType = qmltype->typeId();
3130 vmePropertyType = QMetaType::QObjectStar;
3132 propertyType = qmltype->qListTypeId();
3133 vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >();
3137 if (p->type == Object::DynamicProperty::Custom)
3138 propertyFlags |= QQmlPropertyData::IsQObjectDerived;
3140 propertyFlags |= QQmlPropertyData::IsQList;
3143 if (!p->isReadOnly && p->type != Object::DynamicProperty::CustomList)
3144 propertyFlags |= QQmlPropertyData::IsWritable;
3146 if (p->nameIndex != -1) {
3147 QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(),
3149 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16();
3150 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3151 propertyType, effectiveSignalIndex);
3153 QString propertyName = p->name.toString();
3154 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName;
3155 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3156 propertyType, effectiveSignalIndex);
3159 effectiveSignalIndex++;
3161 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3162 (vmd->propertyData() + vmd->propertyCount)->propertyType = vmePropertyType;
3163 vmd->propertyCount++;
3166 // Now do var properties
3167 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p && varPropCount;
3168 p = obj->dynamicProperties.next(p)) {
3170 if (p->type != Object::DynamicProperty::Var)
3173 quint32 propertyFlags = QQmlPropertyData::IsVarProperty;
3175 propertyFlags |= QQmlPropertyData::IsWritable;
3177 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3178 (vmd->propertyData() + vmd->propertyCount)->propertyType = QMetaType::QVariant;
3179 vmd->propertyCount++;
3180 ((QQmlVMEMetaData *)dynamicData.data())->varPropertyCount++;
3182 if (p->nameIndex != -1) {
3183 QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(),
3185 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16();
3186 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3187 QMetaType::QVariant, effectiveSignalIndex);
3189 QString propertyName = p->name.toString();
3190 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName;
3191 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3192 QMetaType::QVariant, effectiveSignalIndex);
3195 effectiveSignalIndex++;
3198 // Alias property count. Actual data is setup in buildDynamicMetaAliases
3199 ((QQmlVMEMetaData *)dynamicData.data())->aliasCount = aliasCount;
3201 // Dynamic slot data - comes after the property data
3202 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
3203 int paramCount = s->parameterNames.count();
3207 if (paramCount) namesSize += s->parameterNamesLength() + (paramCount - 1 /* commas */);
3208 funcScript.reserve(strlen("(function ") + s->name.length() + 1 /* lparen */ +
3209 namesSize + 1 /* rparen */ + s->body.length() + 1 /* rparen */);
3210 funcScript = QLatin1String("(function ") + s->name.toString() + QLatin1Char('(');
3211 for (int jj = 0; jj < paramCount; ++jj) {
3212 if (jj) funcScript.append(QLatin1Char(','));
3213 funcScript.append(QLatin1String(s->parameterNames.at(jj)));
3215 funcScript += QLatin1Char(')') + s->body + QLatin1Char(')');
3217 QByteArray utf8 = funcScript.toUtf8();
3218 VMD::MethodData methodData = { s->parameterNames.count(),
3221 s->location.start.line };
3223 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3224 VMD::MethodData &md = *(vmd->methodData() + vmd->methodCount);
3228 dynamicData.append((const char *)utf8.constData(), utf8.length());
3232 compileState->aliasingObjects.append(obj);
3234 obj->synthdata = dynamicData;
3235 obj->synthCache = cache;
3236 obj->metatype = cache;
3241 bool QQmlCompiler::buildDynamicMetaAliases(QQmlScript::Object *obj)
3243 Q_ASSERT(obj->synthCache);
3245 QByteArray &dynamicData = obj->synthdata;
3247 QQmlPropertyCache *cache = obj->synthCache;
3248 char *cStringData = cache->_dynamicStringData.data();
3250 int effectiveSignalIndex = cache->signalHandlerIndexCacheStart + cache->propertyIndexCache.count();
3251 int effectivePropertyIndex = cache->propertyIndexCacheStart + cache->propertyIndexCache.count();
3252 int effectiveAliasIndex = 0;
3254 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
3255 p = obj->dynamicProperties.next(p)) {
3257 if (p->type != Object::DynamicProperty::Alias)
3260 if (!p->defaultValue)
3261 COMPILE_EXCEPTION(obj, tr("No property alias location"));
3263 if (!p->defaultValue->values.isOne() ||
3264 p->defaultValue->values.first()->object ||
3265 !p->defaultValue->values.first()->value.isScript())
3266 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3268 QQmlJS::AST::Node *node = p->defaultValue->values.first()->value.asAST();
3271 QStringList alias = astNodeToStringList(node);
3272 if (alias.count() < 1 || alias.count() > 3)
3273 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
3275 QQmlScript::Object *idObject = compileState->ids.value(alias.at(0));
3277 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias reference. Unable to find id \"%1\"").arg(alias.at(0)));
3281 int notifySignal = -1;
3284 bool writable = false;
3285 bool resettable = false;
3287 quint32 propertyFlags = QQmlPropertyData::IsAlias;
3289 if (alias.count() == 2 || alias.count() == 3) {
3290 QQmlPropertyData *property = this->property(idObject, alias.at(1));
3292 if (!property || property->coreIndex > 0x0000FFFF)
3293 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3295 propIdx = property->coreIndex;
3296 type = property->propType;
3298 writable = property->isWritable();
3299 resettable = property->isResettable();
3300 notifySignal = property->notifyIndex;
3302 if (alias.count() == 3) {
3303 QQmlValueType *valueType = QQmlValueTypeFactory::valueType(type);
3305 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3309 int valueTypeIndex =
3310 valueType->metaObject()->indexOfProperty(alias.at(2).toUtf8().constData());
3311 if (valueTypeIndex == -1)
3312 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3313 Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
3315 propIdx |= (valueTypeIndex << 16);
3316 if (valueType->metaObject()->property(valueTypeIndex).isEnumType())
3317 type = QVariant::Int;
3319 type = valueType->metaObject()->property(valueTypeIndex).userType();
3322 if (property->isEnum()) {
3323 type = QVariant::Int;
3326 propertyFlags |= property->getFlags() & QQmlPropertyData::PropTypeFlagMask;
3328 if (property->isVarProperty())
3329 propertyFlags |= QQmlPropertyData::IsQVariant;
3331 if (property->isQObject())
3332 flags |= QML_ALIAS_FLAG_PTR;
3336 Q_ASSERT(idObject->type != -1); // How else did it get an id?
3338 const QQmlCompiledData::TypeReference &ref = output->types.at(idObject->type);
3340 type = ref.type->typeId();
3342 type = ref.component->metaTypeId;
3344 flags |= QML_ALIAS_FLAG_PTR;
3345 propertyFlags |= QQmlPropertyData::IsQObjectDerived;
3348 QQmlVMEMetaData::AliasData aliasData = { idObject->idIndex, propIdx, propType, flags, notifySignal };
3350 typedef QQmlVMEMetaData VMD;
3351 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3352 *(vmd->aliasData() + effectiveAliasIndex++) = aliasData;
3354 if (!p->isReadOnly && writable)
3355 propertyFlags |= QQmlPropertyData::IsWritable;
3357 propertyFlags &= ~QQmlPropertyData::IsWritable;
3360 propertyFlags |= QQmlPropertyData::IsResettable;
3362 propertyFlags &= ~QQmlPropertyData::IsResettable;
3364 if (p->nameIndex != -1) {
3365 QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(),
3367 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16();
3368 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3369 type, effectiveSignalIndex++);
3371 QString propertyName = p->name.toString();
3372 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName;
3373 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3374 type, effectiveSignalIndex++);
3381 bool QQmlCompiler::checkValidId(QQmlScript::Value *v, const QString &val)
3384 COMPILE_EXCEPTION(v, tr( "Invalid empty ID"));
3386 QChar ch = val.at(0);
3387 if (ch.isLetter() && !ch.isLower())
3388 COMPILE_EXCEPTION(v, tr( "IDs cannot start with an uppercase letter"));
3390 QChar u(QLatin1Char('_'));
3391 if (!ch.isLetter() && ch != u)
3392 COMPILE_EXCEPTION(v, tr( "IDs must start with a letter or underscore"));
3394 for (int ii = 1; ii < val.count(); ++ii) {
3396 if (!ch.isLetterOrNumber() && ch != u)
3397 COMPILE_EXCEPTION(v, tr( "IDs must contain only letters, numbers, and underscores"));
3400 if (enginePrivate->v8engine()->illegalNames().contains(val))
3401 COMPILE_EXCEPTION(v, tr( "ID illegally masks global JavaScript property"));
3406 bool QQmlCompiler::buildBinding(QQmlScript::Value *value,
3407 QQmlScript::Property *prop,
3408 const BindingContext &ctxt)
3410 Q_ASSERT(prop->index != -1);
3411 Q_ASSERT(prop->parent);
3412 Q_ASSERT(prop->parent->metatype);
3414 if (!prop->core.isWritable() && !prop->core.isQList() && !prop->isReadOnlyDeclaration)
3415 COMPILE_EXCEPTION(prop, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
3417 JSBindingReference *reference = pool->New<JSBindingReference>();
3418 reference->expression = value->value;
3419 reference->property = prop;
3420 reference->value = value;
3421 reference->bindingContext = ctxt;
3422 addBindingReference(reference);
3427 bool QQmlCompiler::buildLiteralBinding(QQmlScript::Value *v,
3428 QQmlScript::Property *prop,
3429 const QQmlCompilerTypes::BindingContext &)
3431 Q_ASSERT(v->value.isScript());
3433 if (!prop->core.isWritable())
3436 AST::Node *binding = v->value.asAST();
3438 if (prop->type == QVariant::String) {
3439 if (AST::CallExpression *e = AST::cast<AST::CallExpression *>(binding)) {
3440 if (AST::IdentifierExpression *i = AST::cast<AST::IdentifierExpression *>(e->base)) {
3441 if (i->name == qsTrId_string) {
3442 AST::ArgumentList *arg1 = e->arguments?e->arguments:0;
3443 AST::ArgumentList *arg2 = arg1?arg1->next:0;
3445 if (arg1 && arg1->expression->kind == AST::Node::Kind_StringLiteral &&
3446 (!arg2 || arg2->expression->kind == AST::Node::Kind_NumericLiteral) &&
3447 (!arg2 || !arg2->next)) {
3452 text = AST::cast<AST::StringLiteral *>(arg1->expression)->value;
3453 if (arg2) n = (int)AST::cast<AST::NumericLiteral *>(arg2->expression)->value;
3455 TrBindingReference *reference = pool->New<TrBindingReference>();
3456 reference->dataType = BindingReference::TrId;
3457 reference->text = text;
3459 v->bindingReference = reference;
3463 } else if (i->name == qsTr_string) {
3465 AST::ArgumentList *arg1 = e->arguments?e->arguments:0;
3466 AST::ArgumentList *arg2 = arg1?arg1->next:0;
3467 AST::ArgumentList *arg3 = arg2?arg2->next:0;
3469 if (arg1 && arg1->expression->kind == AST::Node::Kind_StringLiteral &&
3470 (!arg2 || arg2->expression->kind == AST::Node::Kind_StringLiteral) &&
3471 (!arg3 || arg3->expression->kind == AST::Node::Kind_NumericLiteral) &&
3472 (!arg3 || !arg3->next)) {
3478 text = AST::cast<AST::StringLiteral *>(arg1->expression)->value;
3479 if (arg2) comment = AST::cast<AST::StringLiteral *>(arg2->expression)->value;
3480 if (arg3) n = (int)AST::cast<AST::NumericLiteral *>(arg3->expression)->value;
3482 TrBindingReference *reference = pool->New<TrBindingReference>();
3483 reference->dataType = BindingReference::Tr;
3484 reference->text = text;
3485 reference->comment = comment;
3487 v->bindingReference = reference;
3500 void QQmlCompiler::genBindingAssignment(QQmlScript::Value *binding,
3501 QQmlScript::Property *prop,
3502 QQmlScript::Object *obj,
3503 QQmlScript::Property *valueTypeProperty)
3506 Q_ASSERT(binding->bindingReference);
3508 const BindingReference &ref = *binding->bindingReference;
3509 if (ref.dataType == BindingReference::TrId) {
3510 const TrBindingReference &tr = static_cast<const TrBindingReference &>(ref);
3512 Instruction::StoreTrIdString store;
3513 store.propertyIndex = prop->core.coreIndex;
3514 store.text = output->indexForByteArray(tr.text.toUtf8());
3516 output->addInstruction(store);
3517 } else if (ref.dataType == BindingReference::Tr) {
3518 const TrBindingReference &tr = static_cast<const TrBindingReference &>(ref);
3520 Instruction::StoreTrString store;
3521 store.propertyIndex = prop->core.coreIndex;
3522 store.context = translationContextIndex();
3523 store.text = output->indexForByteArray(tr.text.toUtf8());
3524 store.comment = output->indexForByteArray(tr.comment.toUtf8());
3526 output->addInstruction(store);
3527 } else if (ref.dataType == BindingReference::V4) {
3528 const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);
3530 Instruction::StoreV4Binding store;
3531 store.value = js.compiledIndex;
3532 store.fallbackValue = js.sharedIndex;
3533 store.context = js.bindingContext.stack;
3534 store.owner = js.bindingContext.owner;
3535 store.isAlias = prop->isAlias;
3536 if (valueTypeProperty) {
3537 store.property = ((prop->index << 16) | valueTypeProperty->index);
3538 store.propType = valueTypeProperty->type;
3539 store.isRoot = (compileState->root == valueTypeProperty->parent);
3541 store.property = prop->index;
3543 store.isRoot = (compileState->root == obj);
3545 store.line = binding->location.start.line;
3546 store.column = binding->location.start.column;
3547 output->addInstruction(store);
3549 if (store.fallbackValue > -1) {
3550 //also create v8 instruction (needed to properly configure the fallback v8 binding)
3551 JSBindingReference &js = static_cast<JSBindingReference &>(*binding->bindingReference);
3552 js.dataType = BindingReference::V8;
3553 genBindingAssignment(binding, prop, obj, valueTypeProperty);
3555 } else if (ref.dataType == BindingReference::V8) {
3556 const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);
3558 Instruction::StoreV8Binding store;
3559 store.value = js.sharedIndex;
3560 store.context = js.bindingContext.stack;
3561 store.owner = js.bindingContext.owner;
3562 store.isAlias = prop->isAlias;
3563 if (valueTypeProperty) {
3564 store.isRoot = (compileState->root == valueTypeProperty->parent);
3566 store.isRoot = (compileState->root == obj);
3568 store.isFallback = js.compiledIndex > -1;
3569 store.line = binding->location.start.line;
3570 store.column = binding->location.start.column;
3572 Q_ASSERT(js.bindingContext.owner == 0 ||
3573 (js.bindingContext.owner != 0 && valueTypeProperty));
3574 if (js.bindingContext.owner) {
3575 store.property = genValueTypeData(prop, valueTypeProperty);
3577 store.property = prop->core;
3580 output->addInstruction(store);
3581 } else if (ref.dataType == BindingReference::QtScript) {
3582 const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);
3584 Instruction::StoreBinding store;
3585 store.value = output->indexForString(js.rewrittenExpression);
3586 store.context = js.bindingContext.stack;
3587 store.owner = js.bindingContext.owner;
3588 store.line = binding->location.start.line;
3589 store.column = binding->location.start.column;
3590 store.isAlias = prop->isAlias;
3592 if (valueTypeProperty) {
3593 store.isRoot = (compileState->root == valueTypeProperty->parent);
3595 store.isRoot = (compileState->root == obj);
3597 store.isFallback = false;
3599 Q_ASSERT(js.bindingContext.owner == 0 ||
3600 (js.bindingContext.owner != 0 && valueTypeProperty));
3601 if (js.bindingContext.owner) {
3602 store.property = genValueTypeData(prop, valueTypeProperty);
3604 store.property = prop->core;
3607 output->addInstruction(store);
3609 Q_ASSERT(!"Unhandled BindingReference::DataType type");
3613 int QQmlCompiler::genContextCache()
3615 if (compileState->ids.count() == 0)
3618 QQmlIntegerCache *cache = new QQmlIntegerCache();
3619 cache->reserve(compileState->ids.count());
3620 for (Object *o = compileState->ids.first(); o; o = compileState->ids.next(o))
3621 cache->add(o->id, o->idIndex);
3623 output->contextCaches.append(cache);
3624 return output->contextCaches.count() - 1;
3628 QQmlCompiler::genValueTypeData(QQmlScript::Property *valueTypeProp,
3629 QQmlScript::Property *prop)
3631 QQmlValueType *vt = QQmlValueTypeFactory::valueType(prop->type);
3633 return QQmlPropertyPrivate::saveValueType(prop->core, vt->metaObject(), valueTypeProp->index, engine);
3636 bool QQmlCompiler::completeComponentBuild()
3639 componentStats->componentStat.ids = compileState->ids.count();
3641 for (Object *aliasObject = compileState->aliasingObjects.first(); aliasObject;
3642 aliasObject = compileState->aliasingObjects.next(aliasObject))
3643 COMPILE_CHECK(buildDynamicMetaAliases(aliasObject));
3645 QV4Compiler::Expression expr(unit->imports());
3646 expr.component = compileState->root;
3647 expr.ids = &compileState->ids;
3648 expr.importCache = output->importCache;
3650 QV4Compiler bindingCompiler;
3652 QList<JSBindingReference*> sharedBindings;
3654 for (JSBindingReference *b = compileState->bindings.first(); b; b = b->nextReference) {
3656 JSBindingReference &binding = *b;
3659 expr.context = binding.bindingContext.object;
3660 expr.property = binding.property;
3661 expr.expression = binding.expression;
3663 bool needsFallback = false;
3664 int index = bindingCompiler.compile(expr, enginePrivate, &needsFallback);
3666 binding.dataType = BindingReference::V4;
3667 binding.compiledIndex = index;
3668 binding.sharedIndex = -1;
3670 componentStats->componentStat.optimizedBindings.append(b->value->location);
3675 // Drop through. We need to create a V8 binding in case the V4 binding is invalidated
3678 // Pre-rewrite the expression
3679 QString expression = binding.expression.asScript();
3681 QQmlRewrite::RewriteBinding rewriteBinding;
3682 rewriteBinding.setName(QLatin1Char('$')+binding.property->name().toString());
3683 bool isSharable = false;
3684 binding.rewrittenExpression = rewriteBinding(binding.expression.asAST(), expression, &isSharable);
3686 if (isSharable && binding.property->type != qMetaTypeId<QQmlBinding*>()) {
3687 sharedBindings.append(b);
3689 if (!needsFallback) {
3690 binding.dataType = BindingReference::V8;
3691 binding.compiledIndex = -1;
3694 componentStats->componentStat.sharedBindings.append(b->value->location);
3697 Q_ASSERT(!needsFallback);
3698 binding.dataType = BindingReference::QtScript;
3701 componentStats->componentStat.scriptBindings.append(b->value->location);
3705 if (!sharedBindings.isEmpty()) {
3707 static bool lt(const JSBindingReference *lhs, const JSBindingReference *rhs)
3709 return lhs->value->location.start.line < rhs->value->location.start.line;
3713 qSort(sharedBindings.begin(), sharedBindings.end(), Sort::lt);
3715 int startLineNumber = sharedBindings.at(0)->value->location.start.line;
3716 int lineNumber = startLineNumber;
3718 QByteArray functionArray("[", 1);
3719 for (int ii = 0; ii < sharedBindings.count(); ++ii) {
3721 JSBindingReference *reference = sharedBindings.at(ii);
3722 QQmlScript::Value *value = reference->value;
3723 const QString &expression = reference->rewrittenExpression;
3725 if (ii != 0) functionArray.append(",", 1);
3727 while (lineNumber < value->location.start.line) {
3729 functionArray.append("\n", 1);
3732 functionArray += expression.toUtf8();
3733 lineNumber += expression.count(QLatin1Char('\n'));
3735 reference->sharedIndex = ii;
3737 functionArray.append("]", 1);
3739 compileState->v8BindingProgram = functionArray;
3740 compileState->v8BindingProgramLine = startLineNumber;
3743 if (bindingCompiler.isValid())
3744 compileState->compiledBindingData = bindingCompiler.program();
3746 // Check pop()'s matched push()'s
3747 Q_ASSERT(compileState->objectDepth.depth() == 0);
3748 Q_ASSERT(compileState->listDepth.depth() == 0);
3750 saveComponentState();
3755 void QQmlCompiler::dumpStats()
3757 Q_ASSERT(componentStats);
3758 qWarning().nospace() << "QML Document: " << output->url.toString();
3759 for (int ii = 0; ii < componentStats->savedComponentStats.count(); ++ii) {
3760 const ComponentStat &stat = componentStats->savedComponentStats.at(ii);
3761 qWarning().nospace() << " Component Line " << stat.lineNumber;
3762 qWarning().nospace() << " Total Objects: " << stat.objects;
3763 qWarning().nospace() << " IDs Used: " << stat.ids;
3764 qWarning().nospace() << " Optimized Bindings: " << stat.optimizedBindings.count();
3768 for (int ii = 0; ii < stat.optimizedBindings.count(); ++ii) {
3769 if (0 == (ii % 10)) {
3770 if (ii) output.append("\n");
3775 output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.line));
3777 output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.column));
3778 output.append(") ");
3780 if (!output.isEmpty())
3781 qWarning().nospace() << output.constData();
3784 qWarning().nospace() << " Shared Bindings: " << stat.sharedBindings.count();
3787 for (int ii = 0; ii < stat.sharedBindings.count(); ++ii) {
3788 if (0 == (ii % 10)) {
3789 if (ii) output.append('\n');
3794 output.append(QByteArray::number(stat.sharedBindings.at(ii).start.line));
3796 output.append(QByteArray::number(stat.sharedBindings.at(ii).start.column));
3797 output.append(") ");
3799 if (!output.isEmpty())
3800 qWarning().nospace() << output.constData();
3803 qWarning().nospace() << " QScript Bindings: " << stat.scriptBindings.count();
3806 for (int ii = 0; ii < stat.scriptBindings.count(); ++ii) {
3807 if (0 == (ii % 10)) {
3808 if (ii) output.append('\n');
3813 output.append(QByteArray::number(stat.scriptBindings.at(ii).start.line));
3815 output.append(QByteArray::number(stat.scriptBindings.at(ii).start.column));
3816 output.append(") ");
3818 if (!output.isEmpty())
3819 qWarning().nospace() << output.constData();
3825 Returns true if from can be assigned to a (QObject) property of type
3828 bool QQmlCompiler::canCoerce(int to, QQmlScript::Object *from)
3830 QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to);
3831 QQmlPropertyCache *fromMo = from->metatype;
3836 fromMo = fromMo->parent();
3842 Returns the element name, as written in the QML file, for o.
3844 QString QQmlCompiler::elementName(QQmlScript::Object *o)
3847 if (o->type != -1) {
3848 return unit->parser().referencedTypes().at(o->type)->name;
3854 QQmlType *QQmlCompiler::toQmlType(QQmlScript::Object *from)
3856 if (from->type != -1 && output->types.at(from->type).type)
3857 return output->types.at(from->type).type;
3859 const QMetaObject *mo = from->metatype->firstCppMetaObject();
3861 while (!type && mo) {
3862 type = QQmlMetaType::qmlType(mo);
3863 mo = mo->superClass();
3868 QStringList QQmlCompiler::deferredProperties(QQmlScript::Object *obj)
3870 const QMetaObject *mo = obj->metatype->firstCppMetaObject();
3872 int idx = mo->indexOfClassInfo("DeferredPropertyNames");
3874 return QStringList();
3876 QMetaClassInfo classInfo = mo->classInfo(idx);
3877 QStringList rv = QString::fromUtf8(classInfo.value()).split(QLatin1Char(','));
3882 QQmlCompiler::propertyCacheForObject(QQmlScript::Object *object)
3884 if (object->synthCache)
3885 return object->synthCache;
3886 else if (object->type != -1)
3887 return output->types[object->type].createPropertyCache(engine);
3889 return object->metatype;
3893 QQmlCompiler::property(QQmlScript::Object *object, int index)
3895 QQmlPropertyCache *cache = propertyCacheForObject(object);
3897 return cache->property(index);
3901 QQmlCompiler::property(QQmlScript::Object *object, const QHashedStringRef &name, bool *notInRevision)
3903 if (notInRevision) *notInRevision = false;
3905 QQmlPropertyCache *cache = propertyCacheForObject(object);
3907 QQmlPropertyData *d = cache->property(name);
3909 // Find the first property
3910 while (d && d->isFunction())
3911 d = cache->overrideData(d);
3913 if (d && !cache->isAllowedInRevision(d)) {
3914 if (notInRevision) *notInRevision = true;
3921 // This code must match the semantics of QQmlPropertyPrivate::findSignalByName
3923 QQmlCompiler::signal(QQmlScript::Object *object, const QHashedStringRef &name, bool *notInRevision)
3925 if (notInRevision) *notInRevision = false;
3927 QQmlPropertyCache *cache = propertyCacheForObject(object);
3930 QQmlPropertyData *d = cache->property(name);
3931 if (notInRevision) *notInRevision = false;
3933 while (d && !(d->isFunction()))
3934 d = cache->overrideData(d);
3936 if (d && !cache->isAllowedInRevision(d)) {
3937 if (notInRevision) *notInRevision = true;
3939 } else if (d && d->isSignal()) {
3943 if (name.endsWith(Changed_string)) {
3944 QHashedStringRef propName = name.mid(0, name.length() - Changed_string.length());
3946 d = property(object, propName, notInRevision);
3948 return cache->signal(d->notifyIndex);
3954 // This code must match the semantics of QQmlPropertyPrivate::findSignalByName
3955 int QQmlCompiler::indexOfSignal(QQmlScript::Object *object, const QString &name,
3956 bool *notInRevision)
3958 QQmlPropertyData *d = signal(object, QStringRef(&name), notInRevision);
3959 return d?d->coreIndex:-1;
3962 int QQmlCompiler::indexOfProperty(QQmlScript::Object *object, const QString &name,
3963 bool *notInRevision)
3965 return indexOfProperty(object, QStringRef(&name), notInRevision);
3968 int QQmlCompiler::indexOfProperty(QQmlScript::Object *object, const QHashedStringRef &name,
3969 bool *notInRevision)
3971 QQmlPropertyData *d = property(object, name, notInRevision);
3972 return d?d->coreIndex:-1;