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 output->addInstruction(ss);
1271 bool seenDefer = false;
1272 for (Property *prop = obj->valueProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1273 if (prop->isDeferred) {
1278 genValueProperty(prop, obj);
1281 Instruction::Defer defer;
1282 defer.deferCount = 0;
1283 int deferIdx = output->addInstruction(defer);
1284 int nextInstructionIndex = output->nextInstructionIndex();
1286 Instruction::DeferInit dinit;
1287 // XXX - these are now massive over allocations
1288 dinit.bindingsSize = compileState->totalBindingsCount;
1289 dinit.parserStatusSize = compileState->parserStatusCount;
1290 dinit.objectStackSize = compileState->objectDepth.maxDepth();
1291 dinit.listStackSize = compileState->listDepth.maxDepth();
1292 output->addInstruction(dinit);
1294 for (Property *prop = obj->valueProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1295 if (!prop->isDeferred)
1297 genValueProperty(prop, obj);
1300 Instruction::Done done;
1301 output->addInstruction(done);
1303 output->instruction(deferIdx)->defer.deferCount = output->nextInstructionIndex() - nextInstructionIndex;
1306 for (Property *prop = obj->signalProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1308 QQmlScript::Value *v = prop->values.first();
1310 if (v->type == Value::SignalObject) {
1312 genObject(v->object);
1314 Instruction::AssignSignalObject assign;
1315 assign.line = v->location.start.line;
1316 assign.signal = output->indexForString(prop->name().toString());
1317 output->addInstruction(assign);
1319 } else if (v->type == Value::SignalExpression) {
1321 Instruction::StoreSignal store;
1322 store.signalIndex = prop->index;
1323 const QString &rewrite = rewriteSignalHandler(v->value, prop->name().toString());
1324 store.value = output->indexForByteArray(rewrite.toUtf8());
1325 store.context = v->signalExpressionContextStack;
1326 store.line = v->location.start.line;
1327 store.column = v->location.start.column;
1328 output->addInstruction(store);
1334 for (Property *prop = obj->attachedProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1335 Instruction::FetchAttached fetch;
1336 fetch.id = prop->index;
1337 fetch.line = prop->location.start.line;
1338 output->addInstruction(fetch);
1340 genObjectBody(prop->value);
1342 Instruction::PopFetchedObject pop;
1343 output->addInstruction(pop);
1346 for (Property *prop = obj->groupedProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1347 Instruction::FetchObject fetch;
1348 fetch.property = prop->index;
1349 fetch.line = prop->location.start.line;
1350 output->addInstruction(fetch);
1352 if (!prop->value->synthdata.isEmpty()) {
1353 Instruction::StoreMetaObject meta;
1354 meta.aliasData = output->indexForByteArray(prop->value->synthdata);
1355 meta.propertyCache = output->propertyCaches.count();
1356 QQmlPropertyCache *propertyCache = prop->value->synthCache;
1357 Q_ASSERT(propertyCache);
1358 propertyCache->addref();
1359 output->propertyCaches << propertyCache;
1360 output->addInstruction(meta);
1363 genObjectBody(prop->value);
1365 Instruction::PopFetchedObject pop;
1366 output->addInstruction(pop);
1369 for (Property *prop = obj->valueTypeProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1371 genValueTypeProperty(obj, prop);
1374 for (Property *prop = obj->valueProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1375 if (prop->isDeferred)
1378 genValueProperty(prop, obj);
1381 for (Property *prop = obj->valueTypeProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1383 genValueTypeProperty(obj, prop);
1387 void QQmlCompiler::genValueTypeProperty(QQmlScript::Object *obj,QQmlScript::Property *prop)
1389 Instruction::FetchValueType fetch;
1390 fetch.property = prop->index;
1391 fetch.type = prop->type;
1392 fetch.bindingSkipList = 0;
1394 if (obj->type == -1 || output->types.at(obj->type).component) {
1395 // We only have to do this if this is a composite type. If it is a builtin
1396 // type it can't possibly already have bindings that need to be cleared.
1397 for (Property *vprop = prop->value->valueProperties.first(); vprop; vprop = Object::PropertyList::next(vprop)) {
1398 if (!vprop->values.isEmpty()) {
1399 Q_ASSERT(vprop->index >= 0 && vprop->index < 32);
1400 fetch.bindingSkipList |= (1 << vprop->index);
1405 output->addInstruction(fetch);
1407 for (Property *vprop = prop->value->valueProperties.first(); vprop; vprop = Object::PropertyList::next(vprop)) {
1408 genPropertyAssignment(vprop, prop->value, prop);
1411 Instruction::PopValueType pop;
1412 pop.property = prop->index;
1413 pop.type = prop->type;
1414 pop.bindingSkipList = 0;
1415 output->addInstruction(pop);
1417 genPropertyAssignment(prop, obj);
1420 void QQmlCompiler::genComponent(QQmlScript::Object *obj)
1422 QQmlScript::Object *root = obj->defaultProperty->values.first()->object;
1425 Instruction::CreateComponent create;
1426 create.line = root->location.start.line;
1427 create.column = root->location.start.column;
1428 create.endLine = root->location.end.line;
1429 create.isRoot = (compileState->root == obj);
1430 int createInstruction = output->addInstruction(create);
1431 int nextInstructionIndex = output->nextInstructionIndex();
1433 ComponentCompileState *oldCompileState = compileState;
1434 compileState = componentState(root);
1436 Instruction::Init init;
1437 init.bindingsSize = compileState->totalBindingsCount;
1438 init.parserStatusSize = compileState->parserStatusCount;
1439 init.contextCache = genContextCache();
1440 init.objectStackSize = compileState->objectDepth.maxDepth();
1441 init.listStackSize = compileState->listDepth.maxDepth();
1442 if (compileState->compiledBindingData.isEmpty())
1443 init.compiledBinding = -1;
1445 init.compiledBinding = output->indexForByteArray(compileState->compiledBindingData);
1446 output->addInstruction(init);
1448 if (!compileState->v8BindingProgram.isEmpty()) {
1449 Instruction::InitV8Bindings bindings;
1450 int index = output->programs.count();
1452 typedef QQmlCompiledData::V8Program V8Program;
1453 output->programs.append(V8Program(compileState->v8BindingProgram, output));
1455 bindings.programIndex = index;
1456 bindings.line = compileState->v8BindingProgramLine;
1457 output->addInstruction(bindings);
1462 Instruction::SetDefault def;
1463 output->addInstruction(def);
1465 Instruction::Done done;
1466 output->addInstruction(done);
1468 output->instruction(createInstruction)->createComponent.count =
1469 output->nextInstructionIndex() - nextInstructionIndex;
1471 compileState = oldCompileState;
1473 if (!obj->id.isEmpty()) {
1474 Instruction::SetId id;
1475 id.value = output->indexForString(obj->id);
1476 id.index = obj->idIndex;
1477 output->addInstruction(id);
1480 if (obj == unitRoot) {
1481 output->rootPropertyCache = output->types[obj->type].createPropertyCache(engine);
1482 output->rootPropertyCache->addref();
1486 bool QQmlCompiler::buildComponent(QQmlScript::Object *obj,
1487 const BindingContext &ctxt)
1489 // The special "Component" element can only have the id property and a
1490 // default property, that actually defines the component's tree
1492 compileState->objectDepth.push();
1494 // Find, check and set the "id" property (if any)
1495 Property *idProp = 0;
1496 if (obj->properties.isMany() ||
1497 (obj->properties.isOne() && obj->properties.first()->name() != id_string))
1498 COMPILE_EXCEPTION(obj->properties.first(), tr("Component elements may not contain properties other than id"));
1500 if (!obj->properties.isEmpty())
1501 idProp = obj->properties.first();
1504 if (idProp->value || idProp->values.isMany() || idProp->values.first()->object)
1505 COMPILE_EXCEPTION(idProp, tr("Invalid component id specification"));
1506 COMPILE_CHECK(checkValidId(idProp->values.first(), idProp->values.first()->primitive()))
1508 QString idVal = idProp->values.first()->primitive();
1510 if (compileState->ids.value(idVal))
1511 COMPILE_EXCEPTION(idProp, tr("id is not unique"));
1517 // Check the Component tree is well formed
1518 if (obj->defaultProperty &&
1519 (obj->defaultProperty->value || obj->defaultProperty->values.isMany() ||
1520 (obj->defaultProperty->values.isOne() && !obj->defaultProperty->values.first()->object)))
1521 COMPILE_EXCEPTION(obj, tr("Invalid component body specification"));
1523 if (!obj->dynamicProperties.isEmpty())
1524 COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties."));
1525 if (!obj->dynamicSignals.isEmpty())
1526 COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals."));
1527 if (!obj->dynamicSlots.isEmpty())
1528 COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions."));
1530 QQmlScript::Object *root = 0;
1531 if (obj->defaultProperty && !obj->defaultProperty->values.isEmpty())
1532 root = obj->defaultProperty->values.first()->object;
1535 COMPILE_EXCEPTION(obj, tr("Cannot create empty component specification"));
1537 // Build the component tree
1538 COMPILE_CHECK(buildComponentFromRoot(root, ctxt));
1540 compileState->objectDepth.pop();
1545 bool QQmlCompiler::buildComponentFromRoot(QQmlScript::Object *obj,
1546 const BindingContext &ctxt)
1548 ComponentCompileState *oldComponentCompileState = compileState;
1549 compileState = pool->New<ComponentCompileState>();
1550 compileState->root = obj;
1551 compileState->nested = true;
1553 if (componentStats) {
1554 ComponentStat oldComponentStat = componentStats->componentStat;
1556 componentStats->componentStat = ComponentStat();
1557 componentStats->componentStat.lineNumber = obj->location.start.line;
1560 COMPILE_CHECK(buildObject(obj, ctxt));
1562 COMPILE_CHECK(completeComponentBuild());
1564 componentStats->componentStat = oldComponentStat;
1567 COMPILE_CHECK(buildObject(obj, ctxt));
1569 COMPILE_CHECK(completeComponentBuild());
1572 compileState = oldComponentCompileState;
1578 // Build a sub-object. A sub-object is one that was not created directly by
1579 // QML - such as a grouped property object, or an attached object. Sub-object's
1580 // can't have an id, involve a custom parser, have attached properties etc.
1581 bool QQmlCompiler::buildSubObject(QQmlScript::Object *obj, const BindingContext &ctxt)
1583 Q_ASSERT(obj->metatype);
1584 Q_ASSERT(!obj->defaultProperty);
1585 Q_ASSERT(ctxt.isSubContext()); // sub-objects must always be in a binding
1588 for (Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) {
1589 if (isSignalPropertyName(prop->name())) {
1590 COMPILE_CHECK(buildSignal(prop, obj, ctxt));
1592 COMPILE_CHECK(buildProperty(prop, obj, ctxt));
1599 int QQmlCompiler::componentTypeRef()
1601 if (cachedComponentTypeRef == -1) {
1602 QQmlType *t = QQmlMetaType::qmlType(Component_string, Component_module_string, 1, 0);
1603 for (int ii = output->types.count() - 1; ii >= 0; --ii) {
1604 if (output->types.at(ii).type == t) {
1605 cachedComponentTypeRef = ii;
1609 QQmlCompiledData::TypeReference ref;
1611 output->types << ref;
1612 cachedComponentTypeRef = output->types.count() - 1;
1614 return cachedComponentTypeRef;
1617 int QQmlCompiler::translationContextIndex()
1619 if (cachedTranslationContextIndex == -1) {
1620 // This code must match that in the qsTr() implementation
1621 const QString &path = output->name;
1622 int lastSlash = path.lastIndexOf(QLatin1Char('/'));
1623 QString context = (lastSlash > -1) ? path.mid(lastSlash + 1, path.length()-lastSlash-5) :
1625 QByteArray contextUtf8 = context.toUtf8();
1626 cachedTranslationContextIndex = output->indexForByteArray(contextUtf8);
1628 return cachedTranslationContextIndex;
1631 bool QQmlCompiler::buildSignal(QQmlScript::Property *prop, QQmlScript::Object *obj,
1632 const BindingContext &ctxt)
1634 Q_ASSERT(obj->metatype);
1636 const QHashedStringRef &propName = prop->name();
1638 Q_ASSERT(propName.startsWith(on_string));
1639 QString name = propName.mid(2, -1).toString();
1641 // Note that the property name could start with any alpha or '_' or '$' character,
1642 // so we need to do the lower-casing of the first alpha character.
1643 for (int firstAlphaIndex = 0; firstAlphaIndex < name.size(); ++firstAlphaIndex) {
1644 if (name.at(firstAlphaIndex).isUpper()) {
1645 name[firstAlphaIndex] = name.at(firstAlphaIndex).toLower();
1650 bool notInRevision = false;
1652 QQmlPropertyData *sig = signal(obj, QStringRef(&name), ¬InRevision);
1656 if (notInRevision && 0 == property(obj, propName, 0)) {
1657 Q_ASSERT(obj->type != -1);
1658 const QList<QQmlTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes();
1659 const QQmlTypeData::TypeReference &type = resolvedTypes.at(obj->type);
1661 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));
1663 COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available due to component versioning.").arg(elementName(obj)).arg(prop->name().toString()));
1667 // If the "on<Signal>" name doesn't resolve into a signal, try it as a
1669 COMPILE_CHECK(buildProperty(prop, obj, ctxt));
1673 if (prop->value || !prop->values.isOne())
1674 COMPILE_EXCEPTION(prop, tr("Incorrectly specified signal assignment"));
1676 prop->index = propertyCacheForObject(obj)->methodIndexToSignalIndex(sig->coreIndex);
1679 obj->addSignalProperty(prop);
1681 if (prop->values.first()->object) {
1682 COMPILE_CHECK(buildObject(prop->values.first()->object, ctxt));
1683 prop->values.first()->type = Value::SignalObject;
1685 prop->values.first()->type = Value::SignalExpression;
1687 if (!prop->values.first()->value.isScript())
1688 COMPILE_EXCEPTION(prop, tr("Cannot assign a value to a signal (expecting a script to be run)"));
1690 QString script = prop->values.first()->value.asScript().trimmed();
1691 if (script.isEmpty())
1692 COMPILE_EXCEPTION(prop, tr("Empty signal assignment"));
1694 prop->values.first()->signalExpressionContextStack = ctxt.stack;
1703 Returns true if (value) property \a prop exists on obj, false otherwise.
1705 bool QQmlCompiler::doesPropertyExist(QQmlScript::Property *prop,
1706 QQmlScript::Object *obj)
1708 if (prop->name().isEmpty())
1710 if(isAttachedPropertyName(prop->name()) || prop->name() == id_string)
1713 return property(obj, prop->name()) != 0;
1716 bool QQmlCompiler::buildProperty(QQmlScript::Property *prop,
1717 QQmlScript::Object *obj,
1718 const BindingContext &ctxt)
1720 if (prop->isEmpty())
1721 COMPILE_EXCEPTION(prop, tr("Empty property assignment"));
1723 if (isAttachedPropertyName(prop->name())) {
1724 // Setup attached property data
1726 if (ctxt.isSubContext()) {
1727 // Attached properties cannot be used on sub-objects. Sub-objects
1728 // always exist in a binding sub-context, which is what we test
1730 COMPILE_EXCEPTION(prop, tr("Attached properties cannot be used here"));
1734 QQmlImportNamespace *typeNamespace = 0;
1735 unit->imports().resolveType(prop->name(), &type, 0, 0, 0, &typeNamespace);
1737 if (typeNamespace) {
1738 COMPILE_CHECK(buildPropertyInNamespace(typeNamespace, prop, obj,
1741 } else if (!type || !type->attachedPropertiesType()) {
1742 COMPILE_EXCEPTION(prop, tr("Non-existent attached object"));
1746 COMPILE_EXCEPTION(prop, tr("Invalid attached object assignment"));
1748 Q_ASSERT(type->attachedPropertiesFunction());
1749 prop->index = type->attachedPropertiesId();
1750 prop->value->metatype = enginePrivate->cache(type->attachedPropertiesType());
1752 // Setup regular property data
1753 bool notInRevision = false;
1754 QQmlPropertyData *d =
1755 prop->name().isEmpty()?0:property(obj, prop->name(), ¬InRevision);
1757 if (d == 0 && notInRevision) {
1758 const QList<QQmlTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes();
1759 const QQmlTypeData::TypeReference &type = resolvedTypes.at(obj->type);
1761 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));
1763 COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available due to component versioning.").arg(elementName(obj)).arg(prop->name().toString()));
1766 prop->index = d->coreIndex;
1768 } else if (prop->isDefault) {
1769 QString defaultPropertyName = obj->metatype->defaultPropertyName();
1771 if (!defaultPropertyName.isEmpty()) {
1772 prop->setName(defaultPropertyName);
1773 prop->core = *obj->metatype->defaultProperty();
1774 prop->index = prop->core.coreIndex;
1778 // We can't error here as the "id" property does not require a
1779 // successful index resolution
1780 if (prop->index != -1)
1781 prop->type = prop->core.propType;
1783 // Check if this is an alias
1784 if (prop->index != -1 &&
1786 prop->parent->type != -1 &&
1787 output->types.at(prop->parent->type).component) {
1789 QQmlPropertyCache *cache = output->types.at(prop->parent->type).component->rootPropertyCache;
1790 if (cache && cache->property(prop->index) && cache->property(prop->index)->isAlias())
1791 prop->isAlias = true;
1794 if (prop->index != -1 && !prop->values.isEmpty())
1795 prop->parent->setBindingBit(prop->index);
1798 if (!prop->isDefault && prop->name() == id_string && !ctxt.isSubContext()) {
1800 // The magic "id" behavior doesn't apply when "id" is resolved as a
1801 // default property or to sub-objects (which are always in binding
1803 COMPILE_CHECK(buildIdProperty(prop, obj));
1804 if (prop->type == QVariant::String &&
1805 prop->values.first()->value.isString())
1806 COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt));
1808 } else if (isAttachedPropertyName(prop->name())) {
1810 COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt));
1812 } else if (prop->index == -1) {
1814 if (prop->isDefault) {
1815 COMPILE_EXCEPTION(prop->values.first(), tr("Cannot assign to non-existent default property"));
1817 COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(prop->name().toString()));
1820 } else if (prop->value) {
1822 COMPILE_CHECK(buildGroupedProperty(prop, obj, ctxt));
1824 } else if (prop->core.isQList()) {
1826 COMPILE_CHECK(buildListProperty(prop, obj, ctxt));
1828 } else if (prop->type == qMetaTypeId<QQmlScriptString>()) {
1830 COMPILE_CHECK(buildScriptStringProperty(prop, obj, ctxt));
1834 COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt));
1841 bool QQmlCompiler::buildPropertyInNamespace(QQmlImportNamespace *ns,
1842 QQmlScript::Property *nsProp,
1843 QQmlScript::Object *obj,
1844 const BindingContext &ctxt)
1847 COMPILE_EXCEPTION(nsProp, tr("Invalid use of namespace"));
1849 for (Property *prop = nsProp->value->properties.first(); prop; prop = nsProp->value->properties.next(prop)) {
1851 if (!isAttachedPropertyName(prop->name()))
1852 COMPILE_EXCEPTION(prop, tr("Not an attached property name"));
1854 // Setup attached property data
1857 unit->imports().resolveType(ns, prop->name(), &type, 0, 0, 0);
1859 if (!type || !type->attachedPropertiesType())
1860 COMPILE_EXCEPTION(prop, tr("Non-existent attached object"));
1863 COMPILE_EXCEPTION(prop, tr("Invalid attached object assignment"));
1865 Q_ASSERT(type->attachedPropertiesFunction());
1866 prop->index = type->index();
1867 prop->value->metatype = enginePrivate->cache(type->attachedPropertiesType());
1869 COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt));
1875 void QQmlCompiler::genValueProperty(QQmlScript::Property *prop,
1876 QQmlScript::Object *obj)
1878 if (prop->core.isQList()) {
1879 genListProperty(prop, obj);
1881 genPropertyAssignment(prop, obj);
1885 void QQmlCompiler::genListProperty(QQmlScript::Property *prop,
1886 QQmlScript::Object *obj)
1888 int listType = enginePrivate->listType(prop->type);
1890 Instruction::FetchQList fetch;
1891 fetch.property = prop->index;
1892 bool listTypeIsInterface = QQmlMetaType::isInterface(listType);
1893 fetch.type = listType;
1894 output->addInstruction(fetch);
1896 for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
1898 if (v->type == Value::CreatedObject) {
1900 genObject(v->object);
1901 if (listTypeIsInterface) {
1902 Instruction::AssignObjectList assign;
1903 assign.line = prop->location.start.line;
1904 output->addInstruction(assign);
1906 Instruction::StoreObjectQList store;
1907 output->addInstruction(store);
1910 } else if (v->type == Value::PropertyBinding) {
1912 genBindingAssignment(v, prop, obj);
1918 Instruction::PopQList pop;
1919 output->addInstruction(pop);
1922 void QQmlCompiler::genPropertyAssignment(QQmlScript::Property *prop,
1923 QQmlScript::Object *obj,
1924 QQmlScript::Property *valueTypeProperty)
1926 for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
1928 Q_ASSERT(v->type == Value::CreatedObject ||
1929 v->type == Value::PropertyBinding ||
1930 v->type == Value::Literal);
1932 if (v->type == Value::CreatedObject) {
1934 genObject(v->object);
1936 if (QQmlMetaType::isInterface(prop->type)) {
1938 Instruction::StoreInterface store;
1939 store.line = v->object->location.start.line;
1940 store.propertyIndex = prop->index;
1941 output->addInstruction(store);
1943 } else if (prop->type == QMetaType::QVariant) {
1945 if (prop->core.isVarProperty()) {
1946 Instruction::StoreVarObject store;
1947 store.line = v->object->location.start.line;
1948 store.propertyIndex = prop->index;
1949 output->addInstruction(store);
1951 Instruction::StoreVariantObject store;
1952 store.line = v->object->location.start.line;
1953 store.propertyIndex = prop->index;
1954 output->addInstruction(store);
1960 Instruction::StoreObject store;
1961 store.line = v->object->location.start.line;
1962 store.propertyIndex = prop->index;
1963 output->addInstruction(store);
1966 } else if (v->type == Value::PropertyBinding) {
1968 genBindingAssignment(v, prop, obj, valueTypeProperty);
1970 } else if (v->type == Value::Literal) {
1972 genLiteralAssignment(prop, v);
1978 for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) {
1980 Q_ASSERT(v->type == Value::ValueSource ||
1981 v->type == Value::ValueInterceptor);
1983 if (v->type == Value::ValueSource) {
1984 genObject(v->object, valueTypeProperty?true:false);
1986 Instruction::StoreValueSource store;
1987 if (valueTypeProperty)
1988 store.property = genValueTypeData(prop, valueTypeProperty);
1990 store.property = prop->core;
1991 QQmlType *valueType = toQmlType(v->object);
1992 store.castValue = valueType->propertyValueSourceCast();
1993 output->addInstruction(store);
1995 } else if (v->type == Value::ValueInterceptor) {
1996 genObject(v->object, valueTypeProperty?true:false);
1998 Instruction::StoreValueInterceptor store;
1999 if (valueTypeProperty)
2000 store.property = genValueTypeData(prop, valueTypeProperty);
2002 store.property = prop->core;
2003 QQmlType *valueType = toQmlType(v->object);
2004 store.castValue = valueType->propertyValueInterceptorCast();
2005 output->addInstruction(store);
2011 bool QQmlCompiler::buildIdProperty(QQmlScript::Property *prop,
2012 QQmlScript::Object *obj)
2015 prop->values.isMany() ||
2016 prop->values.first()->object)
2017 COMPILE_EXCEPTION(prop, tr("Invalid use of id property"));
2019 QQmlScript::Value *idValue = prop->values.first();
2020 QString val = idValue->primitive();
2022 COMPILE_CHECK(checkValidId(idValue, val));
2024 if (compileState->ids.value(val))
2025 COMPILE_EXCEPTION(prop, tr("id is not unique"));
2027 prop->values.first()->type = Value::Id;
2035 void QQmlCompiler::addId(const QString &id, QQmlScript::Object *obj)
2038 Q_ASSERT(!compileState->ids.value(id));
2039 Q_ASSERT(obj->id == id);
2040 obj->idIndex = compileState->ids.count();
2041 compileState->ids.append(obj);
2044 void QQmlCompiler::addBindingReference(JSBindingReference *ref)
2046 Q_ASSERT(ref->value && !ref->value->bindingReference);
2047 ref->value->bindingReference = ref;
2048 compileState->totalBindingsCount++;
2049 compileState->bindings.prepend(ref);
2052 void QQmlCompiler::saveComponentState()
2054 Q_ASSERT(compileState->root);
2055 Q_ASSERT(compileState->root->componentCompileState == 0);
2057 compileState->root->componentCompileState = compileState;
2060 componentStats->savedComponentStats.append(componentStats->componentStat);
2063 QQmlCompilerTypes::ComponentCompileState *
2064 QQmlCompiler::componentState(QQmlScript::Object *obj)
2066 Q_ASSERT(obj->componentCompileState);
2067 return obj->componentCompileState;
2070 // Build attached property object. In this example,
2074 // GridView is an attached property object.
2075 bool QQmlCompiler::buildAttachedProperty(QQmlScript::Property *prop,
2076 QQmlScript::Object *obj,
2077 const BindingContext &ctxt)
2079 Q_ASSERT(prop->value);
2080 Q_ASSERT(prop->index != -1); // This is set in buildProperty()
2082 compileState->objectDepth.push();
2084 obj->addAttachedProperty(prop);
2086 COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr()));
2088 compileState->objectDepth.pop();
2094 // Build "grouped" properties. In this example:
2096 // font.pointSize: 12
2097 // font.family: "Helvetica"
2099 // font is a nested property. pointSize and family are not.
2100 bool QQmlCompiler::buildGroupedProperty(QQmlScript::Property *prop,
2101 QQmlScript::Object *obj,
2102 const BindingContext &ctxt)
2104 Q_ASSERT(prop->type != 0);
2105 Q_ASSERT(prop->index != -1);
2107 if (QQmlValueTypeFactory::isValueType(prop->type)) {
2108 if (prop->type >= 0 && enginePrivate->valueTypes[prop->type]) {
2110 if (!prop->values.isEmpty()) {
2111 // Only error if we are assigning values, and not e.g. a property interceptor
2112 for (Property *dotProp = prop->value->properties.first(); dotProp; dotProp = prop->value->properties.next(dotProp)) {
2113 if (!dotProp->values.isEmpty()) {
2114 if (prop->values.first()->location < prop->value->location) {
2115 COMPILE_EXCEPTION(prop->value, tr( "Property has already been assigned a value"));
2117 COMPILE_EXCEPTION(prop->values.first(), tr( "Property has already been assigned a value"));
2123 if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration) {
2124 COMPILE_EXCEPTION(prop, tr( "Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
2127 if (prop->isAlias) {
2128 for (Property *vtProp = prop->value->properties.first(); vtProp; vtProp = prop->value->properties.next(vtProp)) {
2129 vtProp->isAlias = true;
2133 COMPILE_CHECK(buildValueTypeProperty(enginePrivate->valueTypes[prop->type],
2134 prop->value, obj, ctxt.incr()));
2136 // When building a value type where sub components are declared, this
2137 // code path is followed from buildProperty, even if there is a previous
2138 // assignment to the value type as a whole. Therefore we need to look
2139 // for (and build) assignments to the entire value type before looking
2140 // for any onValue assignments.
2141 for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
2143 COMPILE_EXCEPTION(v->object, tr("Objects cannot be assigned to value types"));
2145 COMPILE_CHECK(buildPropertyLiteralAssignment(prop, obj, v, ctxt));
2148 for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) {
2149 Q_ASSERT(v->object);
2150 COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, obj, v, ctxt));
2153 obj->addValueTypeProperty(prop);
2156 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 // First set up notify signals for properties - first normal, then var, then alias
2937 enum { NSS_Normal = 0, NSS_Var = 1, NSS_Alias = 2 };
2938 for (int ii = NSS_Normal; ii <= NSS_Alias; ++ii) { // 0 == normal, 1 == var, 2 == alias
2940 if (ii == NSS_Var && varPropCount == 0) continue;
2941 else if (ii == NSS_Alias && aliasCount == 0) continue;
2943 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
2944 p = obj->dynamicProperties.next(p)) {
2946 if ((ii == NSS_Normal && (p->type == Object::DynamicProperty::Alias ||
2947 p->type == Object::DynamicProperty::Var)) ||
2948 ((ii == NSS_Var) && (p->type != Object::DynamicProperty::Var)) ||
2949 ((ii == NSS_Alias) && (p->type != Object::DynamicProperty::Alias)))
2952 quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction |
2953 QQmlPropertyData::IsVMESignal;
2955 if (p->nameIndex != -1) {
2956 QHashedCStringRef changedSignalName(cStringData + p->nameIndex,
2957 p->name.length() + 7 /* strlen("Changed") */);
2958 cache->appendSignal(changedSignalName, flags, effectiveMethodIndex++);
2960 QString changedSignalName = p->name.toString() + QLatin1String("Changed");
2962 cache->appendSignal(changedSignalName, flags, effectiveMethodIndex++);
2968 for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) {
2969 int paramCount = s->parameterNames.count();
2971 QList<QByteArray> names;
2972 QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0);
2975 paramTypes[0] = paramCount;
2977 for (int i = 0; i < paramCount; ++i) {
2978 Q_ASSERT(s->parameterTypes.at(i) < builtinTypeCount);
2979 paramTypes[i + 1] = builtinTypes[s->parameterTypes.at(i)].metaType;
2980 names.append(s->parameterNames.at(i).toString().toUtf8());
2984 ((QQmlVMEMetaData *)dynamicData.data())->signalCount++;
2986 quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction |
2987 QQmlPropertyData::IsVMESignal;
2989 flags |= QQmlPropertyData::HasArguments;
2991 if (s->nameIndex != -1) {
2992 QHashedCStringRef name(cStringData + s->nameIndex, s->name.length(), s->name.hash());
2993 cache->appendSignal(name, flags, effectiveMethodIndex++,
2994 paramCount?paramTypes.constData():0, names);
2996 QString name = s->name.toString();
2997 cache->appendSignal(name, flags, effectiveMethodIndex++,
2998 paramCount?paramTypes.constData():0, names);
3004 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
3005 int paramCount = s->parameterNames.count();
3007 quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction;
3010 flags |= QQmlPropertyData::HasArguments;
3012 if (s->nameIndex != -1) {
3013 QHashedCStringRef name(cStringData + s->nameIndex, s->name.length(), s->name.hash());
3014 cache->appendMethod(name, flags, effectiveMethodIndex++, s->parameterNames);
3016 QString name = s->name.toString();
3017 cache->appendMethod(name, flags, effectiveMethodIndex++, s->parameterNames);
3022 // Dynamic properties (except var and aliases)
3023 int effectiveSignalIndex = cache->signalHandlerIndexCacheStart;
3024 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
3025 p = obj->dynamicProperties.next(p)) {
3027 if (p->type == Object::DynamicProperty::Alias ||
3028 p->type == Object::DynamicProperty::Var)
3031 int propertyType = 0;
3032 int vmePropertyType = 0;
3033 quint32 propertyFlags = 0;
3035 if (p->type < builtinTypeCount) {
3036 propertyType = builtinTypes[p->type].metaType;
3037 vmePropertyType = propertyType;
3039 if (p->type == Object::DynamicProperty::Variant)
3040 propertyFlags |= QQmlPropertyData::IsQVariant;
3042 Q_ASSERT(p->type == Object::DynamicProperty::CustomList ||
3043 p->type == Object::DynamicProperty::Custom);
3045 QQmlType *qmltype = 0;
3047 if (!unit->imports().resolveType(p->customType.toString(), &qmltype, &url, 0, 0, 0))
3048 COMPILE_EXCEPTION(p, tr("Invalid property type"));
3051 QQmlTypeData *tdata = enginePrivate->typeLoader.getType(QUrl(url));
3053 Q_ASSERT(tdata->isComplete());
3055 QQmlCompiledData *data = tdata->compiledData();
3057 if (p->type == Object::DynamicProperty::Custom) {
3058 propertyType = data->metaTypeId;
3059 vmePropertyType = QMetaType::QObjectStar;
3061 propertyType = data->listMetaTypeId;
3062 vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >();
3067 if (p->type == Object::DynamicProperty::Custom) {
3068 propertyType = qmltype->typeId();
3069 vmePropertyType = QMetaType::QObjectStar;
3071 propertyType = qmltype->qListTypeId();
3072 vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >();
3076 if (p->type == Object::DynamicProperty::Custom)
3077 propertyFlags |= QQmlPropertyData::IsQObjectDerived;
3079 propertyFlags |= QQmlPropertyData::IsQList;
3082 if (!p->isReadOnly && p->type != Object::DynamicProperty::CustomList)
3083 propertyFlags |= QQmlPropertyData::IsWritable;
3085 if (p->nameIndex != -1) {
3086 QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(),
3088 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16();
3089 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3090 propertyType, effectiveSignalIndex);
3092 QString propertyName = p->name.toString();
3093 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName;
3094 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3095 propertyType, effectiveSignalIndex);
3098 effectiveSignalIndex++;
3100 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3101 (vmd->propertyData() + vmd->propertyCount)->propertyType = vmePropertyType;
3102 vmd->propertyCount++;
3105 // Now do var properties
3106 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p && varPropCount;
3107 p = obj->dynamicProperties.next(p)) {
3109 if (p->type != Object::DynamicProperty::Var)
3112 quint32 propertyFlags = QQmlPropertyData::IsVarProperty;
3114 propertyFlags |= QQmlPropertyData::IsWritable;
3116 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3117 (vmd->propertyData() + vmd->propertyCount)->propertyType = QMetaType::QVariant;
3118 vmd->propertyCount++;
3119 ((QQmlVMEMetaData *)dynamicData.data())->varPropertyCount++;
3121 if (p->nameIndex != -1) {
3122 QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(),
3124 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16();
3125 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3126 QMetaType::QVariant, effectiveSignalIndex);
3128 QString propertyName = p->name.toString();
3129 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName;
3130 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3131 QMetaType::QVariant, effectiveSignalIndex);
3134 effectiveSignalIndex++;
3137 // Alias property count. Actual data is setup in buildDynamicMetaAliases
3138 ((QQmlVMEMetaData *)dynamicData.data())->aliasCount = aliasCount;
3140 // Dynamic slot data - comes after the property data
3141 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
3142 int paramCount = s->parameterNames.count();
3146 if (paramCount) namesSize += s->parameterNamesLength() + (paramCount - 1 /* commas */);
3147 funcScript.reserve(strlen("(function ") + s->name.length() + 1 /* lparen */ +
3148 namesSize + 1 /* rparen */ + s->body.length() + 1 /* rparen */);
3149 funcScript = QLatin1String("(function ") + s->name.toString() + QLatin1Char('(');
3150 for (int jj = 0; jj < paramCount; ++jj) {
3151 if (jj) funcScript.append(QLatin1Char(','));
3152 funcScript.append(QLatin1String(s->parameterNames.at(jj)));
3154 funcScript += QLatin1Char(')') + s->body + QLatin1Char(')');
3156 QByteArray utf8 = funcScript.toUtf8();
3157 VMD::MethodData methodData = { s->parameterNames.count(),
3160 s->location.start.line };
3162 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3163 VMD::MethodData &md = *(vmd->methodData() + vmd->methodCount);
3167 dynamicData.append((const char *)utf8.constData(), utf8.length());
3171 compileState->aliasingObjects.append(obj);
3173 obj->synthdata = dynamicData;
3174 obj->synthCache = cache;
3175 obj->metatype = cache;
3180 bool QQmlCompiler::buildDynamicMetaAliases(QQmlScript::Object *obj)
3182 Q_ASSERT(obj->synthCache);
3184 QByteArray &dynamicData = obj->synthdata;
3186 QQmlPropertyCache *cache = obj->synthCache;
3187 char *cStringData = cache->_dynamicStringData.data();
3189 int effectiveSignalIndex = cache->signalHandlerIndexCacheStart + cache->propertyIndexCache.count();
3190 int effectivePropertyIndex = cache->propertyIndexCacheStart + cache->propertyIndexCache.count();
3191 int effectiveAliasIndex = 0;
3193 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
3194 p = obj->dynamicProperties.next(p)) {
3196 if (p->type != Object::DynamicProperty::Alias)
3199 if (!p->defaultValue)
3200 COMPILE_EXCEPTION(obj, tr("No property alias location"));
3202 if (!p->defaultValue->values.isOne() ||
3203 p->defaultValue->values.first()->object ||
3204 !p->defaultValue->values.first()->value.isScript())
3205 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3207 QQmlJS::AST::Node *node = p->defaultValue->values.first()->value.asAST();
3210 QStringList alias = astNodeToStringList(node);
3211 if (alias.count() < 1 || alias.count() > 3)
3212 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
3214 QQmlScript::Object *idObject = compileState->ids.value(alias.at(0));
3216 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias reference. Unable to find id \"%1\"").arg(alias.at(0)));
3219 int notifySignal = -1;
3222 bool writable = false;
3223 bool resettable = false;
3225 quint32 propertyFlags = QQmlPropertyData::IsAlias;
3227 if (alias.count() == 2 || alias.count() == 3) {
3228 QQmlPropertyData *property = this->property(idObject, alias.at(1));
3230 if (!property || property->coreIndex > 0xFFFF)
3231 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3233 propIdx = property->coreIndex;
3234 type = property->propType;
3236 writable = property->isWritable();
3237 resettable = property->isResettable();
3238 notifySignal = property->notifyIndex;
3240 if (alias.count() == 3) {
3241 QQmlValueType *valueType = enginePrivate->valueTypes[type]; // XXX threadsafe?
3243 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3245 propIdx |= ((unsigned int)type) << 24;
3246 int valueTypeIndex =
3247 valueType->metaObject()->indexOfProperty(alias.at(2).toUtf8().constData());
3248 if (valueTypeIndex == -1)
3249 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3250 Q_ASSERT(valueTypeIndex <= 0xFF);
3252 propIdx |= (valueTypeIndex << 16);
3253 if (valueType->metaObject()->property(valueTypeIndex).isEnumType())
3254 type = QVariant::Int;
3256 type = valueType->metaObject()->property(valueTypeIndex).userType();
3259 if (property->isEnum()) {
3260 type = QVariant::Int;
3263 propertyFlags |= property->getFlags() & QQmlPropertyData::PropTypeFlagMask;
3265 if (property->isVarProperty())
3266 propertyFlags |= QQmlPropertyData::IsQVariant;
3268 if (property->isQObject())
3269 flags |= QML_ALIAS_FLAG_PTR;
3273 Q_ASSERT(idObject->type != -1); // How else did it get an id?
3275 const QQmlCompiledData::TypeReference &ref = output->types.at(idObject->type);
3277 type = ref.type->typeId();
3279 type = ref.component->metaTypeId;
3281 flags |= QML_ALIAS_FLAG_PTR;
3282 propertyFlags |= QQmlPropertyData::IsQObjectDerived;
3285 QQmlVMEMetaData::AliasData aliasData = { idObject->idIndex, propIdx, flags, notifySignal };
3287 typedef QQmlVMEMetaData VMD;
3288 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3289 *(vmd->aliasData() + effectiveAliasIndex++) = aliasData;
3291 if (!p->isReadOnly && writable)
3292 propertyFlags |= QQmlPropertyData::IsWritable;
3294 propertyFlags &= ~QQmlPropertyData::IsWritable;
3297 propertyFlags |= QQmlPropertyData::IsResettable;
3299 propertyFlags &= ~QQmlPropertyData::IsResettable;
3301 if (p->nameIndex != -1) {
3302 QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(),
3304 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16();
3305 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3306 type, effectiveSignalIndex++);
3308 QString propertyName = p->name.toString();
3309 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName;
3310 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3311 type, effectiveSignalIndex++);
3318 bool QQmlCompiler::checkValidId(QQmlScript::Value *v, const QString &val)
3321 COMPILE_EXCEPTION(v, tr( "Invalid empty ID"));
3323 QChar ch = val.at(0);
3324 if (ch.isLetter() && !ch.isLower())
3325 COMPILE_EXCEPTION(v, tr( "IDs cannot start with an uppercase letter"));
3327 QChar u(QLatin1Char('_'));
3328 if (!ch.isLetter() && ch != u)
3329 COMPILE_EXCEPTION(v, tr( "IDs must start with a letter or underscore"));
3331 for (int ii = 1; ii < val.count(); ++ii) {
3333 if (!ch.isLetterOrNumber() && ch != u)
3334 COMPILE_EXCEPTION(v, tr( "IDs must contain only letters, numbers, and underscores"));
3337 if (enginePrivate->v8engine()->illegalNames().contains(val))
3338 COMPILE_EXCEPTION(v, tr( "ID illegally masks global JavaScript property"));
3343 bool QQmlCompiler::buildBinding(QQmlScript::Value *value,
3344 QQmlScript::Property *prop,
3345 const BindingContext &ctxt)
3347 Q_ASSERT(prop->index != -1);
3348 Q_ASSERT(prop->parent);
3349 Q_ASSERT(prop->parent->metatype);
3351 if (!prop->core.isWritable() && !prop->core.isQList() && !prop->isReadOnlyDeclaration)
3352 COMPILE_EXCEPTION(prop, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
3354 JSBindingReference *reference = pool->New<JSBindingReference>();
3355 reference->expression = value->value;
3356 reference->property = prop;
3357 reference->value = value;
3358 reference->bindingContext = ctxt;
3359 addBindingReference(reference);
3364 bool QQmlCompiler::buildLiteralBinding(QQmlScript::Value *v,
3365 QQmlScript::Property *prop,
3366 const QQmlCompilerTypes::BindingContext &)
3368 Q_ASSERT(v->value.isScript());
3370 if (!prop->core.isWritable())
3373 AST::Node *binding = v->value.asAST();
3375 if (prop->type == QVariant::String) {
3376 if (AST::CallExpression *e = AST::cast<AST::CallExpression *>(binding)) {
3377 if (AST::IdentifierExpression *i = AST::cast<AST::IdentifierExpression *>(e->base)) {
3378 if (i->name == qsTrId_string) {
3379 AST::ArgumentList *arg1 = e->arguments?e->arguments:0;
3380 AST::ArgumentList *arg2 = arg1?arg1->next:0;
3382 if (arg1 && arg1->expression->kind == AST::Node::Kind_StringLiteral &&
3383 (!arg2 || arg2->expression->kind == AST::Node::Kind_NumericLiteral) &&
3384 (!arg2 || !arg2->next)) {
3389 text = AST::cast<AST::StringLiteral *>(arg1->expression)->value;
3390 if (arg2) n = (int)AST::cast<AST::NumericLiteral *>(arg2->expression)->value;
3392 TrBindingReference *reference = pool->New<TrBindingReference>();
3393 reference->dataType = BindingReference::TrId;
3394 reference->text = text;
3396 v->bindingReference = reference;
3400 } else if (i->name == qsTr_string) {
3402 AST::ArgumentList *arg1 = e->arguments?e->arguments:0;
3403 AST::ArgumentList *arg2 = arg1?arg1->next:0;
3404 AST::ArgumentList *arg3 = arg2?arg2->next:0;
3406 if (arg1 && arg1->expression->kind == AST::Node::Kind_StringLiteral &&
3407 (!arg2 || arg2->expression->kind == AST::Node::Kind_StringLiteral) &&
3408 (!arg3 || arg3->expression->kind == AST::Node::Kind_NumericLiteral) &&
3409 (!arg3 || !arg3->next)) {
3415 text = AST::cast<AST::StringLiteral *>(arg1->expression)->value;
3416 if (arg2) comment = AST::cast<AST::StringLiteral *>(arg2->expression)->value;
3417 if (arg3) n = (int)AST::cast<AST::NumericLiteral *>(arg3->expression)->value;
3419 TrBindingReference *reference = pool->New<TrBindingReference>();
3420 reference->dataType = BindingReference::Tr;
3421 reference->text = text;
3422 reference->comment = comment;
3424 v->bindingReference = reference;
3437 void QQmlCompiler::genBindingAssignment(QQmlScript::Value *binding,
3438 QQmlScript::Property *prop,
3439 QQmlScript::Object *obj,
3440 QQmlScript::Property *valueTypeProperty)
3443 Q_ASSERT(binding->bindingReference);
3445 const BindingReference &ref = *binding->bindingReference;
3446 if (ref.dataType == BindingReference::TrId) {
3447 const TrBindingReference &tr = static_cast<const TrBindingReference &>(ref);
3449 Instruction::StoreTrIdString store;
3450 store.propertyIndex = prop->core.coreIndex;
3451 store.text = output->indexForByteArray(tr.text.toUtf8());
3453 output->addInstruction(store);
3454 } else if (ref.dataType == BindingReference::Tr) {
3455 const TrBindingReference &tr = static_cast<const TrBindingReference &>(ref);
3457 Instruction::StoreTrString store;
3458 store.propertyIndex = prop->core.coreIndex;
3459 store.context = translationContextIndex();
3460 store.text = output->indexForByteArray(tr.text.toUtf8());
3461 store.comment = output->indexForByteArray(tr.comment.toUtf8());
3463 output->addInstruction(store);
3464 } else if (ref.dataType == BindingReference::V4) {
3465 const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);
3467 Instruction::StoreV4Binding store;
3468 store.value = js.compiledIndex;
3469 store.fallbackValue = js.sharedIndex;
3470 store.context = js.bindingContext.stack;
3471 store.owner = js.bindingContext.owner;
3472 store.isAlias = prop->isAlias;
3473 if (valueTypeProperty) {
3474 store.property = (valueTypeProperty->index & 0xFFFF) |
3475 ((valueTypeProperty->type & 0xFF)) << 16 |
3476 ((prop->index & 0xFF) << 24);
3477 store.isRoot = (compileState->root == valueTypeProperty->parent);
3479 store.property = prop->index;
3480 store.isRoot = (compileState->root == obj);
3482 store.line = binding->location.start.line;
3483 store.column = binding->location.start.column;
3484 output->addInstruction(store);
3486 if (store.fallbackValue > -1) {
3487 //also create v8 instruction (needed to properly configure the fallback v8 binding)
3488 JSBindingReference &js = static_cast<JSBindingReference &>(*binding->bindingReference);
3489 js.dataType = BindingReference::V8;
3490 genBindingAssignment(binding, prop, obj, valueTypeProperty);
3492 } else if (ref.dataType == BindingReference::V8) {
3493 const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);
3495 Instruction::StoreV8Binding store;
3496 store.value = js.sharedIndex;
3497 store.context = js.bindingContext.stack;
3498 store.owner = js.bindingContext.owner;
3499 store.isAlias = prop->isAlias;
3500 if (valueTypeProperty) {
3501 store.isRoot = (compileState->root == valueTypeProperty->parent);
3503 store.isRoot = (compileState->root == obj);
3505 store.isFallback = js.compiledIndex > -1;
3506 store.line = binding->location.start.line;
3507 store.column = binding->location.start.column;
3509 Q_ASSERT(js.bindingContext.owner == 0 ||
3510 (js.bindingContext.owner != 0 && valueTypeProperty));
3511 if (js.bindingContext.owner) {
3512 store.property = genValueTypeData(prop, valueTypeProperty);
3514 store.property = prop->core;
3517 output->addInstruction(store);
3518 } else if (ref.dataType == BindingReference::QtScript) {
3519 const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);
3521 Instruction::StoreBinding store;
3522 store.value = output->indexForString(js.rewrittenExpression);
3523 store.context = js.bindingContext.stack;
3524 store.owner = js.bindingContext.owner;
3525 store.line = binding->location.start.line;
3526 store.column = binding->location.start.column;
3527 store.isAlias = prop->isAlias;
3529 if (valueTypeProperty) {
3530 store.isRoot = (compileState->root == valueTypeProperty->parent);
3532 store.isRoot = (compileState->root == obj);
3534 store.isFallback = false;
3536 Q_ASSERT(js.bindingContext.owner == 0 ||
3537 (js.bindingContext.owner != 0 && valueTypeProperty));
3538 if (js.bindingContext.owner) {
3539 store.property = genValueTypeData(prop, valueTypeProperty);
3541 store.property = prop->core;
3544 output->addInstruction(store);
3546 Q_ASSERT(!"Unhandled BindingReference::DataType type");
3550 int QQmlCompiler::genContextCache()
3552 if (compileState->ids.count() == 0)
3555 QQmlIntegerCache *cache = new QQmlIntegerCache();
3556 cache->reserve(compileState->ids.count());
3557 for (Object *o = compileState->ids.first(); o; o = compileState->ids.next(o))
3558 cache->add(o->id, o->idIndex);
3560 output->contextCaches.append(cache);
3561 return output->contextCaches.count() - 1;
3565 QQmlCompiler::genValueTypeData(QQmlScript::Property *valueTypeProp,
3566 QQmlScript::Property *prop)
3568 typedef QQmlPropertyPrivate QDPP;
3569 return QDPP::saveValueType(prop->core, enginePrivate->valueTypes[prop->type]->metaObject(),
3570 valueTypeProp->index, engine);
3573 bool QQmlCompiler::completeComponentBuild()
3576 componentStats->componentStat.ids = compileState->ids.count();
3578 for (Object *aliasObject = compileState->aliasingObjects.first(); aliasObject;
3579 aliasObject = compileState->aliasingObjects.next(aliasObject))
3580 COMPILE_CHECK(buildDynamicMetaAliases(aliasObject));
3582 QV4Compiler::Expression expr(unit->imports());
3583 expr.component = compileState->root;
3584 expr.ids = &compileState->ids;
3585 expr.importCache = output->importCache;
3587 QV4Compiler bindingCompiler;
3589 QList<JSBindingReference*> sharedBindings;
3591 for (JSBindingReference *b = compileState->bindings.first(); b; b = b->nextReference) {
3593 JSBindingReference &binding = *b;
3596 expr.context = binding.bindingContext.object;
3597 expr.property = binding.property;
3598 expr.expression = binding.expression;
3600 bool needsFallback = false;
3601 int index = bindingCompiler.compile(expr, enginePrivate, &needsFallback);
3603 // Ensure the index value fits within the available space
3604 Q_ASSERT(index < (1 << 15));
3606 binding.dataType = BindingReference::V4;
3607 binding.compiledIndex = index;
3608 binding.sharedIndex = -1;
3610 componentStats->componentStat.optimizedBindings.append(b->value->location);
3615 // Drop through. We need to create a V8 binding in case the V4 binding is invalidated
3618 // Pre-rewrite the expression
3619 QString expression = binding.expression.asScript();
3621 QQmlRewrite::RewriteBinding rewriteBinding;
3622 rewriteBinding.setName(QLatin1Char('$')+binding.property->name().toString());
3623 bool isSharable = false;
3624 binding.rewrittenExpression = rewriteBinding(binding.expression.asAST(), expression, &isSharable);
3626 if (isSharable && binding.property->type != qMetaTypeId<QQmlBinding*>()) {
3627 sharedBindings.append(b);
3629 if (!needsFallback) {
3630 binding.dataType = BindingReference::V8;
3631 binding.compiledIndex = -1;
3634 componentStats->componentStat.sharedBindings.append(b->value->location);
3637 Q_ASSERT(!needsFallback);
3638 binding.dataType = BindingReference::QtScript;
3641 componentStats->componentStat.scriptBindings.append(b->value->location);
3645 if (!sharedBindings.isEmpty()) {
3647 static bool lt(const JSBindingReference *lhs, const JSBindingReference *rhs)
3649 return lhs->value->location.start.line < rhs->value->location.start.line;
3653 qSort(sharedBindings.begin(), sharedBindings.end(), Sort::lt);
3655 int startLineNumber = sharedBindings.at(0)->value->location.start.line;
3656 int lineNumber = startLineNumber;
3658 QByteArray functionArray("[", 1);
3659 for (int ii = 0; ii < sharedBindings.count(); ++ii) {
3661 JSBindingReference *reference = sharedBindings.at(ii);
3662 QQmlScript::Value *value = reference->value;
3663 const QString &expression = reference->rewrittenExpression;
3665 if (ii != 0) functionArray.append(",", 1);
3667 while (lineNumber < value->location.start.line) {
3669 functionArray.append("\n", 1);
3672 functionArray += expression.toUtf8();
3673 lineNumber += expression.count(QLatin1Char('\n'));
3675 // Ensure the index value fits within the available space
3676 Q_ASSERT(ii < (1 << 15));
3677 reference->sharedIndex = ii;
3679 functionArray.append("]", 1);
3681 compileState->v8BindingProgram = functionArray;
3682 compileState->v8BindingProgramLine = startLineNumber;
3685 if (bindingCompiler.isValid())
3686 compileState->compiledBindingData = bindingCompiler.program();
3688 // Check pop()'s matched push()'s
3689 Q_ASSERT(compileState->objectDepth.depth() == 0);
3690 Q_ASSERT(compileState->listDepth.depth() == 0);
3692 saveComponentState();
3697 void QQmlCompiler::dumpStats()
3699 Q_ASSERT(componentStats);
3700 qWarning().nospace() << "QML Document: " << output->url.toString();
3701 for (int ii = 0; ii < componentStats->savedComponentStats.count(); ++ii) {
3702 const ComponentStat &stat = componentStats->savedComponentStats.at(ii);
3703 qWarning().nospace() << " Component Line " << stat.lineNumber;
3704 qWarning().nospace() << " Total Objects: " << stat.objects;
3705 qWarning().nospace() << " IDs Used: " << stat.ids;
3706 qWarning().nospace() << " Optimized Bindings: " << stat.optimizedBindings.count();
3710 for (int ii = 0; ii < stat.optimizedBindings.count(); ++ii) {
3711 if (0 == (ii % 10)) {
3712 if (ii) output.append("\n");
3717 output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.line));
3719 output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.column));
3720 output.append(") ");
3722 if (!output.isEmpty())
3723 qWarning().nospace() << output.constData();
3726 qWarning().nospace() << " Shared Bindings: " << stat.sharedBindings.count();
3729 for (int ii = 0; ii < stat.sharedBindings.count(); ++ii) {
3730 if (0 == (ii % 10)) {
3731 if (ii) output.append('\n');
3736 output.append(QByteArray::number(stat.sharedBindings.at(ii).start.line));
3738 output.append(QByteArray::number(stat.sharedBindings.at(ii).start.column));
3739 output.append(") ");
3741 if (!output.isEmpty())
3742 qWarning().nospace() << output.constData();
3745 qWarning().nospace() << " QScript Bindings: " << stat.scriptBindings.count();
3748 for (int ii = 0; ii < stat.scriptBindings.count(); ++ii) {
3749 if (0 == (ii % 10)) {
3750 if (ii) output.append('\n');
3755 output.append(QByteArray::number(stat.scriptBindings.at(ii).start.line));
3757 output.append(QByteArray::number(stat.scriptBindings.at(ii).start.column));
3758 output.append(") ");
3760 if (!output.isEmpty())
3761 qWarning().nospace() << output.constData();
3767 Returns true if from can be assigned to a (QObject) property of type
3770 bool QQmlCompiler::canCoerce(int to, QQmlScript::Object *from)
3772 QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to);
3773 QQmlPropertyCache *fromMo = from->metatype;
3778 fromMo = fromMo->parent();
3784 Returns the element name, as written in the QML file, for o.
3786 QString QQmlCompiler::elementName(QQmlScript::Object *o)
3789 if (o->type != -1) {
3790 return unit->parser().referencedTypes().at(o->type)->name;
3796 QQmlType *QQmlCompiler::toQmlType(QQmlScript::Object *from)
3798 if (from->type != -1 && output->types.at(from->type).type)
3799 return output->types.at(from->type).type;
3801 const QMetaObject *mo = from->metatype->firstCppMetaObject();
3803 while (!type && mo) {
3804 type = QQmlMetaType::qmlType(mo);
3805 mo = mo->superClass();
3810 QStringList QQmlCompiler::deferredProperties(QQmlScript::Object *obj)
3812 const QMetaObject *mo = obj->metatype->firstCppMetaObject();
3814 int idx = mo->indexOfClassInfo("DeferredPropertyNames");
3816 return QStringList();
3818 QMetaClassInfo classInfo = mo->classInfo(idx);
3819 QStringList rv = QString::fromUtf8(classInfo.value()).split(QLatin1Char(','));
3824 QQmlCompiler::propertyCacheForObject(QQmlScript::Object *object)
3826 if (object->synthCache)
3827 return object->synthCache;
3828 else if (object->type != -1)
3829 return output->types[object->type].createPropertyCache(engine);
3831 return object->metatype;
3835 QQmlCompiler::property(QQmlScript::Object *object, int index)
3837 QQmlPropertyCache *cache = propertyCacheForObject(object);
3839 return cache->property(index);
3843 QQmlCompiler::property(QQmlScript::Object *object, const QHashedStringRef &name, bool *notInRevision)
3845 if (notInRevision) *notInRevision = false;
3847 QQmlPropertyCache *cache = propertyCacheForObject(object);
3849 QQmlPropertyData *d = cache->property(name);
3851 // Find the first property
3852 while (d && d->isFunction())
3853 d = cache->overrideData(d);
3855 if (d && !cache->isAllowedInRevision(d)) {
3856 if (notInRevision) *notInRevision = true;
3863 // This code must match the semantics of QQmlPropertyPrivate::findSignalByName
3865 QQmlCompiler::signal(QQmlScript::Object *object, const QHashedStringRef &name, bool *notInRevision)
3867 if (notInRevision) *notInRevision = false;
3869 QQmlPropertyCache *cache = propertyCacheForObject(object);
3872 QQmlPropertyData *d = cache->property(name);
3873 if (notInRevision) *notInRevision = false;
3875 while (d && !(d->isFunction()))
3876 d = cache->overrideData(d);
3878 if (d && !cache->isAllowedInRevision(d)) {
3879 if (notInRevision) *notInRevision = true;
3881 } else if (d && d->isSignal()) {
3885 if (name.endsWith(Changed_string)) {
3886 QHashedStringRef propName = name.mid(0, name.length() - Changed_string.length());
3888 d = property(object, propName, notInRevision);
3890 return cache->signal(d->notifyIndex);
3896 // This code must match the semantics of QQmlPropertyPrivate::findSignalByName
3897 int QQmlCompiler::indexOfSignal(QQmlScript::Object *object, const QString &name,
3898 bool *notInRevision)
3900 QQmlPropertyData *d = signal(object, QStringRef(&name), notInRevision);
3901 return d?d->coreIndex:-1;
3904 int QQmlCompiler::indexOfProperty(QQmlScript::Object *object, const QString &name,
3905 bool *notInRevision)
3907 return indexOfProperty(object, QStringRef(&name), notInRevision);
3910 int QQmlCompiler::indexOfProperty(QQmlScript::Object *object, const QHashedStringRef &name,
3911 bool *notInRevision)
3913 QQmlPropertyData *d = property(object, name, notInRevision);
3914 return d?d->coreIndex:-1;