1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include "qqmlcompiler_p.h"
44 #include "qqmlpropertyvaluesource.h"
45 #include "qqmlcomponent.h"
46 #include <private/qmetaobjectbuilder_p.h>
47 #include "qqmlstringconverters_p.h"
48 #include "qqmlengine_p.h"
49 #include "qqmlengine.h"
50 #include "qqmlcontext.h"
51 #include "qqmlmetatype_p.h"
52 #include "qqmlcustomparser_p_p.h"
53 #include "qqmlcontext_p.h"
54 #include "qqmlcomponent_p.h"
55 #include <private/qqmljsast_p.h>
56 #include "qqmlvmemetaobject_p.h"
57 #include "qqmlexpression_p.h"
58 #include "qqmlproperty_p.h"
59 #include "qqmlrewrite_p.h"
60 #include "qqmlscriptstring.h"
61 #include "qqmlglobal_p.h"
62 #include "qqmlbinding_p.h"
63 #include <private/qv4compiler_p.h>
70 #include <QtCore/qdebug.h>
71 #include <QtCore/qdatetime.h>
72 #include <QtCore/qvarlengtharray.h>
74 Q_DECLARE_METATYPE(QList<int>)
75 Q_DECLARE_METATYPE(QList<qreal>)
76 Q_DECLARE_METATYPE(QList<bool>)
77 Q_DECLARE_METATYPE(QList<QString>)
78 Q_DECLARE_METATYPE(QList<QUrl>)
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);
901 if (!buildObject(tree, BindingContext()) || !completeComponentBuild())
904 Instruction::Init init;
905 init.bindingsSize = compileState->totalBindingsCount;
906 init.parserStatusSize = compileState->parserStatusCount;
907 init.contextCache = genContextCache();
908 init.objectStackSize = compileState->objectDepth.maxDepth();
909 init.listStackSize = compileState->listDepth.maxDepth();
910 if (compileState->compiledBindingData.isEmpty())
911 init.compiledBinding = -1;
913 init.compiledBinding = output->indexForByteArray(compileState->compiledBindingData);
914 output->addInstruction(init);
916 foreach (const QQmlTypeData::ScriptReference &script, unit->resolvedScripts()) {
917 Instruction::StoreImportedScript import;
918 import.value = output->scripts.count();
920 QQmlScriptData *scriptData = script.script->scriptData();
921 scriptData->addref();
922 output->scripts << scriptData;
923 output->addInstruction(import);
926 if (!compileState->v8BindingProgram.isEmpty()) {
927 Instruction::InitV8Bindings bindings;
928 int index = output->programs.count();
930 typedef QQmlCompiledData::V8Program V8Program;
931 output->programs.append(V8Program(compileState->v8BindingProgram, output));
933 bindings.programIndex = index;
934 bindings.line = compileState->v8BindingProgramLine;
935 output->addInstruction(bindings);
940 Instruction::SetDefault def;
941 output->addInstruction(def);
943 Instruction::Done done;
944 output->addInstruction(done);
946 Q_ASSERT(tree->metatype);
947 if (!tree->synthdata.isEmpty()) {
948 enginePrivate->registerCompositeType(output);
949 } else if (output->types.at(tree->type).component) {
950 output->metaTypeId = output->types.at(tree->type).component->metaTypeId;
951 output->listMetaTypeId = output->types.at(tree->type).component->listMetaTypeId;
953 Q_ASSERT(output->types.at(tree->type).type);
954 output->metaTypeId = output->types.at(tree->type).type->typeId();
955 output->listMetaTypeId = output->types.at(tree->type).type->qListTypeId();
957 if (!tree->synthdata.isEmpty())
958 enginePrivate->registerCompositeType(output);
961 static bool QStringList_contains(const QStringList &list, const QHashedStringRef &string)
963 for (int ii = 0; ii < list.count(); ++ii)
964 if (string == list.at(ii))
970 bool QQmlCompiler::buildObject(QQmlScript::Object *obj, const BindingContext &ctxt)
973 componentStats->componentStat.objects++;
975 Q_ASSERT (obj->type != -1);
976 QQmlCompiledData::TypeReference &tr = output->types[obj->type];
977 obj->metatype = tr.createPropertyCache(engine);
979 // This object is a "Component" element.
980 if (tr.type && obj->metatype->metaObject() == &QQmlComponent::staticMetaObject) {
981 COMPILE_CHECK(buildComponent(obj, ctxt));
986 typedef QQmlInstruction I;
987 const I *init = ((const I *)tr.component->bytecode.constData());
988 Q_ASSERT(init && tr.component->instructionType(init) == QQmlInstruction::Init);
990 // Adjust stack depths to include nested components
991 compileState->objectDepth.pushPop(init->init.objectStackSize);
992 compileState->listDepth.pushPop(init->init.listStackSize);
993 compileState->parserStatusCount += init->init.parserStatusSize;
994 compileState->totalBindingsCount += init->init.bindingsSize;
997 compileState->objectDepth.push();
999 // Object instantiations reset the binding context
1000 BindingContext objCtxt(obj);
1002 // Create the synthesized meta object, ignoring aliases
1003 COMPILE_CHECK(checkDynamicMeta(obj));
1004 COMPILE_CHECK(mergeDynamicMetaProperties(obj));
1005 COMPILE_CHECK(buildDynamicMeta(obj, Normal));
1007 // Find the native type and check for the QQmlParserStatus interface
1008 QQmlType *type = toQmlType(obj);
1010 obj->parserStatusCast = type->parserStatusCast();
1011 if (obj->parserStatusCast != -1)
1012 compileState->parserStatusCount++;
1014 // Check if this is a custom parser type. Custom parser types allow
1015 // assignments to non-existent properties. These assignments are then
1016 // compiled by the type.
1017 bool isCustomParser = output->types.at(obj->type).type &&
1018 output->types.at(obj->type).type->customParser() != 0;
1019 QList<QQmlCustomParserProperty> customProps;
1021 // Fetch the list of deferred properties
1022 QStringList deferredList = deferredProperties(obj);
1024 // Must do id property first. This is to ensure that the id given to any
1025 // id reference created matches the order in which the objects are
1027 for (Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) {
1028 if (prop->name() == id_string) {
1029 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
1035 Property *defaultProperty = 0;
1036 Property *skipProperty = 0;
1037 if (obj->defaultProperty) {
1038 defaultProperty = obj->defaultProperty;
1040 Property *explicitProperty = 0;
1042 QString defaultPropertyName = obj->metatype->defaultPropertyName();
1043 if (!defaultPropertyName.isEmpty()) {
1044 QString *s = pool->NewString(defaultPropertyName);
1045 QHashedStringRef r(*s);
1047 if (obj->propertiesHashField.test(r.hash())) {
1048 for (Property *ep = obj->properties.first(); ep; ep = obj->properties.next(ep)) {
1049 if (ep->name() == r) {
1050 explicitProperty = ep;
1056 if (!explicitProperty)
1057 defaultProperty->setName(r);
1060 if (explicitProperty && !explicitProperty->value && !explicitProperty->values.isEmpty()) {
1062 skipProperty = explicitProperty; // We merge the values into defaultProperty
1064 // Find the correct insertion point
1065 Value *insertPos = 0;
1067 for (Value *v = defaultProperty->values.first(); v; v = Property::ValueList::next(v)) {
1068 if (!(v->location.start < explicitProperty->values.first()->location.start))
1073 defaultProperty->values.insertAfter(insertPos, explicitProperty->values);
1077 QQmlCustomParser *cp = 0;
1079 cp = output->types.at(obj->type).type->customParser();
1081 // Build all explicit properties specified
1082 for (Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) {
1084 if (prop == skipProperty)
1086 if (prop->name() == id_string)
1089 bool canDefer = false;
1090 if (isCustomParser) {
1091 if (doesPropertyExist(prop, obj) &&
1092 (!(cp->flags() & QQmlCustomParser::AcceptsAttachedProperties) ||
1093 !isAttachedPropertyName(prop->name()))) {
1094 int ids = compileState->ids.count();
1095 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
1096 canDefer = ids == compileState->ids.count();
1097 } else if (isSignalPropertyName(prop->name()) &&
1098 (cp->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
1099 COMPILE_CHECK(buildSignal(prop,obj,objCtxt));
1101 customProps << QQmlCustomParserNodePrivate::fromProperty(prop);
1104 if (isSignalPropertyName(prop->name())) {
1105 COMPILE_CHECK(buildSignal(prop,obj,objCtxt));
1107 int ids = compileState->ids.count();
1108 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
1109 canDefer = ids == compileState->ids.count();
1113 if (canDefer && !deferredList.isEmpty() && QStringList_contains(deferredList, prop->name()))
1114 prop->isDeferred = true;
1118 // Build the default property
1119 if (defaultProperty) {
1120 Property *prop = defaultProperty;
1122 bool canDefer = false;
1123 if (isCustomParser) {
1124 if (doesPropertyExist(prop, obj)) {
1125 int ids = compileState->ids.count();
1126 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
1127 canDefer = ids == compileState->ids.count();
1129 customProps << QQmlCustomParserNodePrivate::fromProperty(prop);
1132 int ids = compileState->ids.count();
1133 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
1134 canDefer = ids == compileState->ids.count();
1137 if (canDefer && !deferredList.isEmpty() && QStringList_contains(deferredList, prop->name()))
1138 prop->isDeferred = true;
1141 // Compile custom parser parts
1142 if (isCustomParser && !customProps.isEmpty()) {
1144 cp->compiler = this;
1146 obj->custom = cp->compile(customProps);
1149 foreach (QQmlError err, cp->errors()) {
1150 err.setUrl(output->url);
1155 compileState->objectDepth.pop();
1160 void QQmlCompiler::genObject(QQmlScript::Object *obj, bool parentToSuper)
1162 QQmlCompiledData::TypeReference &tr = output->types[obj->type];
1163 if (tr.type && obj->metatype->metaObject() == &QQmlComponent::staticMetaObject) {
1168 // Create the object
1169 if (obj->custom.isEmpty() && output->types.at(obj->type).type &&
1170 !output->types.at(obj->type).type->isExtendedType() && obj != compileState->root) {
1172 Instruction::CreateSimpleObject create;
1173 create.create = output->types.at(obj->type).type->createFunction();
1174 create.typeSize = output->types.at(obj->type).type->createSize();
1175 create.type = obj->type;
1176 create.line = obj->location.start.line;
1177 create.column = obj->location.start.column;
1178 create.parentToSuper = parentToSuper;
1179 output->addInstruction(create);
1183 if (output->types.at(obj->type).type) {
1184 Instruction::CreateCppObject create;
1185 create.line = obj->location.start.line;
1186 create.column = obj->location.start.column;
1188 if (!obj->custom.isEmpty())
1189 create.data = output->indexForByteArray(obj->custom);
1190 create.type = obj->type;
1191 create.isRoot = (compileState->root == obj);
1192 create.parentToSuper = parentToSuper;
1193 output->addInstruction(create);
1195 Instruction::CreateQMLObject create;
1196 create.type = obj->type;
1197 create.isRoot = (compileState->root == obj);
1199 if (!obj->bindingBitmask.isEmpty()) {
1200 Q_ASSERT(obj->bindingBitmask.size() % 4 == 0);
1201 create.bindingBits = output->indexForByteArray(obj->bindingBitmask);
1203 create.bindingBits = -1;
1205 output->addInstruction(create);
1207 Instruction::CompleteQMLObject complete;
1208 complete.line = obj->location.start.line;
1209 complete.column = obj->location.start.column;
1210 complete.isRoot = (compileState->root == obj);
1211 output->addInstruction(complete);
1215 // Setup the synthesized meta object if necessary
1216 if (!obj->synthdata.isEmpty()) {
1217 Instruction::StoreMetaObject meta;
1218 meta.aliasData = output->indexForByteArray(obj->synthdata);
1219 meta.propertyCache = output->propertyCaches.count();
1221 QQmlPropertyCache *propertyCache = obj->synthCache;
1222 Q_ASSERT(propertyCache);
1223 propertyCache->addref();
1225 if (obj == unitRoot) {
1226 propertyCache->addref();
1227 output->rootPropertyCache = propertyCache;
1230 output->propertyCaches << propertyCache;
1231 output->addInstruction(meta);
1232 } else if (obj == unitRoot) {
1233 output->rootPropertyCache = tr.createPropertyCache(engine);
1234 output->rootPropertyCache->addref();
1237 // Set the object id
1238 if (!obj->id.isEmpty()) {
1239 Instruction::SetId id;
1240 id.value = output->indexForString(obj->id);
1241 id.index = obj->idIndex;
1242 output->addInstruction(id);
1246 if (tr.type && obj->parserStatusCast != -1) {
1247 Instruction::BeginObject begin;
1248 begin.castValue = obj->parserStatusCast;
1249 output->addInstruction(begin);
1255 void QQmlCompiler::genObjectBody(QQmlScript::Object *obj)
1257 for (Property *prop = obj->scriptStringProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1258 Q_ASSERT(prop->scriptStringScope != -1);
1259 const QString &script = prop->values.first()->value.asScript();
1260 Instruction::StoreScriptString ss;
1261 ss.propertyIndex = prop->index;
1262 ss.value = output->indexForString(script);
1263 ss.scope = prop->scriptStringScope;
1264 // ss.bindingId = rewriteBinding(script, prop->name());
1265 ss.bindingId = rewriteBinding(prop->values.first()->value, QString()); // XXX
1266 ss.line = prop->location.start.line;
1267 ss.column = prop->location.start.column;
1268 ss.isStringLiteral = prop->values.first()->value.isString();
1269 ss.isNumberLiteral = prop->values.first()->value.isNumber();
1270 ss.numberValue = prop->values.first()->value.asNumber();
1271 output->addInstruction(ss);
1274 bool seenDefer = false;
1275 for (Property *prop = obj->valueProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1276 if (prop->isDeferred) {
1281 genValueProperty(prop, obj);
1284 Instruction::Defer defer;
1285 defer.deferCount = 0;
1286 int deferIdx = output->addInstruction(defer);
1287 int nextInstructionIndex = output->nextInstructionIndex();
1289 Instruction::DeferInit dinit;
1290 // XXX - these are now massive over allocations
1291 dinit.bindingsSize = compileState->totalBindingsCount;
1292 dinit.parserStatusSize = compileState->parserStatusCount;
1293 dinit.objectStackSize = compileState->objectDepth.maxDepth();
1294 dinit.listStackSize = compileState->listDepth.maxDepth();
1295 output->addInstruction(dinit);
1297 for (Property *prop = obj->valueProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1298 if (!prop->isDeferred)
1300 genValueProperty(prop, obj);
1303 Instruction::Done done;
1304 output->addInstruction(done);
1306 output->instruction(deferIdx)->defer.deferCount = output->nextInstructionIndex() - nextInstructionIndex;
1309 for (Property *prop = obj->signalProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1311 QQmlScript::Value *v = prop->values.first();
1313 if (v->type == Value::SignalObject) {
1315 genObject(v->object);
1317 Instruction::AssignSignalObject assign;
1318 assign.line = v->location.start.line;
1319 assign.signal = output->indexForString(prop->name().toString());
1320 output->addInstruction(assign);
1322 } else if (v->type == Value::SignalExpression) {
1324 Instruction::StoreSignal store;
1325 store.signalIndex = prop->index;
1327 const QList<QByteArray> ¶meterNameList = obj->metatype->signalParameterNames(prop->index);
1328 QQmlRewrite::RewriteSignalHandler rewriter;
1330 const QString &rewrite = rewriter(v->value.asAST(), v->value.asScript(),
1331 prop->name().toString(),
1332 obj->metatype->signalParameterStringForJS(prop->index, &count),
1334 store.value = output->indexForByteArray(rewrite.toUtf8());
1335 store.parameterCount =
1336 (rewriter.parameterAccess() == QQmlRewrite::RewriteSignalHandler::ParametersUnaccessed) ? 0 : count;
1337 store.context = v->signalExpressionContextStack;
1338 store.line = v->location.start.line;
1339 store.column = v->location.start.column;
1340 output->addInstruction(store);
1346 for (Property *prop = obj->attachedProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1347 Instruction::FetchAttached fetch;
1348 fetch.id = prop->index;
1349 fetch.line = prop->location.start.line;
1350 output->addInstruction(fetch);
1352 genObjectBody(prop->value);
1354 Instruction::PopFetchedObject pop;
1355 output->addInstruction(pop);
1358 for (Property *prop = obj->groupedProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1359 Instruction::FetchObject fetch;
1360 fetch.property = prop->index;
1361 fetch.line = prop->location.start.line;
1362 output->addInstruction(fetch);
1364 if (!prop->value->synthdata.isEmpty()) {
1365 Instruction::StoreMetaObject meta;
1366 meta.aliasData = output->indexForByteArray(prop->value->synthdata);
1367 meta.propertyCache = output->propertyCaches.count();
1368 QQmlPropertyCache *propertyCache = prop->value->synthCache;
1369 Q_ASSERT(propertyCache);
1370 propertyCache->addref();
1371 output->propertyCaches << propertyCache;
1372 output->addInstruction(meta);
1375 genObjectBody(prop->value);
1377 Instruction::PopFetchedObject pop;
1378 output->addInstruction(pop);
1381 for (Property *prop = obj->valueTypeProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1383 genValueTypeProperty(obj, prop);
1386 for (Property *prop = obj->valueProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1387 if (prop->isDeferred)
1390 genValueProperty(prop, obj);
1393 for (Property *prop = obj->valueTypeProperties.first(); prop; prop = Object::PropertyList::next(prop)) {
1395 genValueTypeProperty(obj, prop);
1399 void QQmlCompiler::genValueTypeProperty(QQmlScript::Object *obj,QQmlScript::Property *prop)
1401 Instruction::FetchValueType fetch;
1402 fetch.property = prop->index;
1403 fetch.type = prop->type;
1404 fetch.bindingSkipList = 0;
1406 if (obj->type == -1 || output->types.at(obj->type).component) {
1407 // We only have to do this if this is a composite type. If it is a builtin
1408 // type it can't possibly already have bindings that need to be cleared.
1409 for (Property *vprop = prop->value->valueProperties.first(); vprop; vprop = Object::PropertyList::next(vprop)) {
1410 if (!vprop->values.isEmpty()) {
1411 Q_ASSERT(vprop->index >= 0 && vprop->index < 32);
1412 fetch.bindingSkipList |= (1 << vprop->index);
1417 output->addInstruction(fetch);
1419 for (Property *vprop = prop->value->valueProperties.first(); vprop; vprop = Object::PropertyList::next(vprop)) {
1420 genPropertyAssignment(vprop, prop->value, prop);
1423 Instruction::PopValueType pop;
1424 pop.property = prop->index;
1425 pop.type = prop->type;
1426 pop.bindingSkipList = 0;
1427 output->addInstruction(pop);
1429 genPropertyAssignment(prop, obj);
1432 void QQmlCompiler::genComponent(QQmlScript::Object *obj)
1434 QQmlScript::Object *root = obj->defaultProperty->values.first()->object;
1437 Instruction::CreateComponent create;
1438 create.line = root->location.start.line;
1439 create.column = root->location.start.column;
1440 create.endLine = root->location.end.line;
1441 create.isRoot = (compileState->root == obj);
1442 int createInstruction = output->addInstruction(create);
1443 int nextInstructionIndex = output->nextInstructionIndex();
1445 ComponentCompileState *oldCompileState = compileState;
1446 compileState = componentState(root);
1448 Instruction::Init init;
1449 init.bindingsSize = compileState->totalBindingsCount;
1450 init.parserStatusSize = compileState->parserStatusCount;
1451 init.contextCache = genContextCache();
1452 init.objectStackSize = compileState->objectDepth.maxDepth();
1453 init.listStackSize = compileState->listDepth.maxDepth();
1454 if (compileState->compiledBindingData.isEmpty())
1455 init.compiledBinding = -1;
1457 init.compiledBinding = output->indexForByteArray(compileState->compiledBindingData);
1458 output->addInstruction(init);
1460 if (!compileState->v8BindingProgram.isEmpty()) {
1461 Instruction::InitV8Bindings bindings;
1462 int index = output->programs.count();
1464 typedef QQmlCompiledData::V8Program V8Program;
1465 output->programs.append(V8Program(compileState->v8BindingProgram, output));
1467 bindings.programIndex = index;
1468 bindings.line = compileState->v8BindingProgramLine;
1469 output->addInstruction(bindings);
1474 Instruction::SetDefault def;
1475 output->addInstruction(def);
1477 Instruction::Done done;
1478 output->addInstruction(done);
1480 output->instruction(createInstruction)->createComponent.count =
1481 output->nextInstructionIndex() - nextInstructionIndex;
1483 compileState = oldCompileState;
1485 if (!obj->id.isEmpty()) {
1486 Instruction::SetId id;
1487 id.value = output->indexForString(obj->id);
1488 id.index = obj->idIndex;
1489 output->addInstruction(id);
1492 if (obj == unitRoot) {
1493 output->rootPropertyCache = output->types[obj->type].createPropertyCache(engine);
1494 output->rootPropertyCache->addref();
1498 bool QQmlCompiler::buildComponent(QQmlScript::Object *obj,
1499 const BindingContext &ctxt)
1501 // The special "Component" element can only have the id property and a
1502 // default property, that actually defines the component's tree
1504 compileState->objectDepth.push();
1506 // Find, check and set the "id" property (if any)
1507 Property *idProp = 0;
1508 if (obj->properties.isMany() ||
1509 (obj->properties.isOne() && obj->properties.first()->name() != id_string))
1510 COMPILE_EXCEPTION(obj->properties.first(), tr("Component elements may not contain properties other than id"));
1512 if (!obj->properties.isEmpty())
1513 idProp = obj->properties.first();
1516 if (idProp->value || idProp->values.isMany() || idProp->values.first()->object)
1517 COMPILE_EXCEPTION(idProp, tr("Invalid component id specification"));
1518 COMPILE_CHECK(checkValidId(idProp->values.first(), idProp->values.first()->primitive()))
1520 QString idVal = idProp->values.first()->primitive();
1522 if (compileState->ids.value(idVal))
1523 COMPILE_EXCEPTION(idProp, tr("id is not unique"));
1529 // Check the Component tree is well formed
1530 if (obj->defaultProperty &&
1531 (obj->defaultProperty->value || obj->defaultProperty->values.isMany() ||
1532 (obj->defaultProperty->values.isOne() && !obj->defaultProperty->values.first()->object)))
1533 COMPILE_EXCEPTION(obj, tr("Invalid component body specification"));
1535 if (!obj->dynamicProperties.isEmpty())
1536 COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties."));
1537 if (!obj->dynamicSignals.isEmpty())
1538 COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals."));
1539 if (!obj->dynamicSlots.isEmpty())
1540 COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions."));
1542 QQmlScript::Object *root = 0;
1543 if (obj->defaultProperty && !obj->defaultProperty->values.isEmpty())
1544 root = obj->defaultProperty->values.first()->object;
1547 COMPILE_EXCEPTION(obj, tr("Cannot create empty component specification"));
1549 // Build the component tree
1550 COMPILE_CHECK(buildComponentFromRoot(root, ctxt));
1552 compileState->objectDepth.pop();
1557 bool QQmlCompiler::buildComponentFromRoot(QQmlScript::Object *obj,
1558 const BindingContext &ctxt)
1560 ComponentCompileState *oldComponentCompileState = compileState;
1561 compileState = pool->New<ComponentCompileState>();
1562 compileState->root = obj;
1563 compileState->nested = true;
1565 if (componentStats) {
1566 ComponentStat oldComponentStat = componentStats->componentStat;
1568 componentStats->componentStat = ComponentStat();
1569 componentStats->componentStat.lineNumber = obj->location.start.line;
1572 COMPILE_CHECK(buildObject(obj, ctxt));
1574 COMPILE_CHECK(completeComponentBuild());
1576 componentStats->componentStat = oldComponentStat;
1579 COMPILE_CHECK(buildObject(obj, ctxt));
1581 COMPILE_CHECK(completeComponentBuild());
1584 compileState = oldComponentCompileState;
1590 // Build a sub-object. A sub-object is one that was not created directly by
1591 // QML - such as a grouped property object, or an attached object. Sub-object's
1592 // can't have an id, involve a custom parser, have attached properties etc.
1593 bool QQmlCompiler::buildSubObject(QQmlScript::Object *obj, const BindingContext &ctxt)
1595 Q_ASSERT(obj->metatype);
1596 Q_ASSERT(!obj->defaultProperty);
1597 Q_ASSERT(ctxt.isSubContext()); // sub-objects must always be in a binding
1600 for (Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) {
1601 if (isSignalPropertyName(prop->name())) {
1602 COMPILE_CHECK(buildSignal(prop, obj, ctxt));
1604 COMPILE_CHECK(buildProperty(prop, obj, ctxt));
1611 int QQmlCompiler::componentTypeRef()
1613 if (cachedComponentTypeRef == -1) {
1614 QQmlType *t = QQmlMetaType::qmlType(Component_string, Component_module_string, 1, 0);
1615 for (int ii = output->types.count() - 1; ii >= 0; --ii) {
1616 if (output->types.at(ii).type == t) {
1617 cachedComponentTypeRef = ii;
1621 QQmlCompiledData::TypeReference ref;
1623 output->types << ref;
1624 cachedComponentTypeRef = output->types.count() - 1;
1626 return cachedComponentTypeRef;
1629 int QQmlCompiler::translationContextIndex()
1631 if (cachedTranslationContextIndex == -1) {
1632 // This code must match that in the qsTr() implementation
1633 const QString &path = output->name;
1634 int lastSlash = path.lastIndexOf(QLatin1Char('/'));
1635 QString context = (lastSlash > -1) ? path.mid(lastSlash + 1, path.length()-lastSlash-5) :
1637 QByteArray contextUtf8 = context.toUtf8();
1638 cachedTranslationContextIndex = output->indexForByteArray(contextUtf8);
1640 return cachedTranslationContextIndex;
1643 bool QQmlCompiler::buildSignal(QQmlScript::Property *prop, QQmlScript::Object *obj,
1644 const BindingContext &ctxt)
1646 Q_ASSERT(obj->metatype);
1648 const QHashedStringRef &propName = prop->name();
1650 Q_ASSERT(propName.startsWith(on_string));
1651 QString name = propName.mid(2, -1).toString();
1653 // Note that the property name could start with any alpha or '_' or '$' character,
1654 // so we need to do the lower-casing of the first alpha character.
1655 for (int firstAlphaIndex = 0; firstAlphaIndex < name.size(); ++firstAlphaIndex) {
1656 if (name.at(firstAlphaIndex).isUpper()) {
1657 name[firstAlphaIndex] = name.at(firstAlphaIndex).toLower();
1662 bool notInRevision = false;
1664 QQmlPropertyData *sig = signal(obj, QStringRef(&name), ¬InRevision);
1668 if (notInRevision && 0 == property(obj, propName, 0)) {
1669 Q_ASSERT(obj->type != -1);
1670 const QList<QQmlTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes();
1671 const QQmlTypeData::TypeReference &type = resolvedTypes.at(obj->type);
1673 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));
1675 COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available due to component versioning.").arg(elementName(obj)).arg(prop->name().toString()));
1679 // If the "on<Signal>" name doesn't resolve into a signal, try it as a
1681 COMPILE_CHECK(buildProperty(prop, obj, ctxt));
1685 if (prop->value || !prop->values.isOne())
1686 COMPILE_EXCEPTION(prop, tr("Incorrectly specified signal assignment"));
1688 prop->index = propertyCacheForObject(obj)->methodIndexToSignalIndex(sig->coreIndex);
1691 obj->addSignalProperty(prop);
1693 if (prop->values.first()->object) {
1694 COMPILE_CHECK(buildObject(prop->values.first()->object, ctxt));
1695 prop->values.first()->type = Value::SignalObject;
1697 prop->values.first()->type = Value::SignalExpression;
1699 if (!prop->values.first()->value.isScript())
1700 COMPILE_EXCEPTION(prop, tr("Cannot assign a value to a signal (expecting a script to be run)"));
1702 QString script = prop->values.first()->value.asScript().trimmed();
1703 if (script.isEmpty())
1704 COMPILE_EXCEPTION(prop, tr("Empty signal assignment"));
1706 //all handlers should be on the original, rather than cloned signals in order
1707 //to ensure all parameters are available (see qqmlboundsignal constructor for more details)
1708 prop->index = obj->metatype->originalClone(prop->index);
1710 QString errorString;
1711 obj->metatype->signalParameterStringForJS(prop->index, 0, &errorString);
1712 if (!errorString.isEmpty())
1713 COMPILE_EXCEPTION(prop, errorString);
1715 prop->values.first()->signalExpressionContextStack = ctxt.stack;
1724 Returns true if (value) property \a prop exists on obj, false otherwise.
1726 bool QQmlCompiler::doesPropertyExist(QQmlScript::Property *prop,
1727 QQmlScript::Object *obj)
1729 if (prop->name().isEmpty())
1731 if(isAttachedPropertyName(prop->name()) || prop->name() == id_string)
1734 return property(obj, prop->name()) != 0;
1737 bool QQmlCompiler::buildProperty(QQmlScript::Property *prop,
1738 QQmlScript::Object *obj,
1739 const BindingContext &ctxt)
1741 if (prop->isEmpty())
1742 COMPILE_EXCEPTION(prop, tr("Empty property assignment"));
1744 if (isAttachedPropertyName(prop->name())) {
1745 // Setup attached property data
1747 if (ctxt.isSubContext()) {
1748 // Attached properties cannot be used on sub-objects. Sub-objects
1749 // always exist in a binding sub-context, which is what we test
1751 COMPILE_EXCEPTION(prop, tr("Attached properties cannot be used here"));
1755 QQmlImportNamespace *typeNamespace = 0;
1756 unit->imports().resolveType(prop->name(), &type, 0, 0, 0, &typeNamespace);
1758 if (typeNamespace) {
1759 COMPILE_CHECK(buildPropertyInNamespace(typeNamespace, prop, obj,
1762 } else if (!type || !type->attachedPropertiesType()) {
1763 COMPILE_EXCEPTION(prop, tr("Non-existent attached object"));
1767 COMPILE_EXCEPTION(prop, tr("Invalid attached object assignment"));
1769 Q_ASSERT(type->attachedPropertiesFunction());
1770 prop->index = type->attachedPropertiesId();
1771 prop->value->metatype = enginePrivate->cache(type->attachedPropertiesType());
1773 // Setup regular property data
1774 bool notInRevision = false;
1775 QQmlPropertyData *d =
1776 prop->name().isEmpty()?0:property(obj, prop->name(), ¬InRevision);
1778 if (d == 0 && notInRevision) {
1779 const QList<QQmlTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes();
1780 const QQmlTypeData::TypeReference &type = resolvedTypes.at(obj->type);
1782 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));
1784 COMPILE_EXCEPTION(prop, tr("\"%1.%2\" is not available due to component versioning.").arg(elementName(obj)).arg(prop->name().toString()));
1787 prop->index = d->coreIndex;
1789 } else if (prop->isDefault) {
1790 QString defaultPropertyName = obj->metatype->defaultPropertyName();
1792 if (!defaultPropertyName.isEmpty()) {
1793 prop->setName(defaultPropertyName);
1794 prop->core = *obj->metatype->defaultProperty();
1795 prop->index = prop->core.coreIndex;
1799 // We can't error here as the "id" property does not require a
1800 // successful index resolution
1801 if (prop->index != -1)
1802 prop->type = prop->core.propType;
1804 // Check if this is an alias
1805 if (prop->index != -1 &&
1807 prop->parent->type != -1 &&
1808 output->types.at(prop->parent->type).component) {
1810 QQmlPropertyCache *cache = output->types.at(prop->parent->type).component->rootPropertyCache;
1811 if (cache && cache->property(prop->index) && cache->property(prop->index)->isAlias())
1812 prop->isAlias = true;
1815 if (prop->index != -1 && !prop->values.isEmpty())
1816 prop->parent->setBindingBit(prop->index);
1819 if (!prop->isDefault && prop->name() == id_string && !ctxt.isSubContext()) {
1821 // The magic "id" behavior doesn't apply when "id" is resolved as a
1822 // default property or to sub-objects (which are always in binding
1824 COMPILE_CHECK(buildIdProperty(prop, obj));
1825 if (prop->type == QVariant::String &&
1826 prop->values.first()->value.isString())
1827 COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt));
1829 } else if (isAttachedPropertyName(prop->name())) {
1831 COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt));
1833 } else if (prop->index == -1) {
1835 if (prop->isDefault) {
1836 COMPILE_EXCEPTION(prop->values.first(), tr("Cannot assign to non-existent default property"));
1838 COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(prop->name().toString()));
1841 } else if (prop->value) {
1843 COMPILE_CHECK(buildGroupedProperty(prop, obj, ctxt));
1845 } else if (prop->core.isQList()) {
1847 COMPILE_CHECK(buildListProperty(prop, obj, ctxt));
1849 } else if (prop->type == qMetaTypeId<QQmlScriptString>()) {
1851 COMPILE_CHECK(buildScriptStringProperty(prop, obj, ctxt));
1855 COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt));
1862 bool QQmlCompiler::buildPropertyInNamespace(QQmlImportNamespace *ns,
1863 QQmlScript::Property *nsProp,
1864 QQmlScript::Object *obj,
1865 const BindingContext &ctxt)
1868 COMPILE_EXCEPTION(nsProp, tr("Invalid use of namespace"));
1870 for (Property *prop = nsProp->value->properties.first(); prop; prop = nsProp->value->properties.next(prop)) {
1872 if (!isAttachedPropertyName(prop->name()))
1873 COMPILE_EXCEPTION(prop, tr("Not an attached property name"));
1875 // Setup attached property data
1878 unit->imports().resolveType(ns, prop->name(), &type, 0, 0, 0);
1880 if (!type || !type->attachedPropertiesType())
1881 COMPILE_EXCEPTION(prop, tr("Non-existent attached object"));
1884 COMPILE_EXCEPTION(prop, tr("Invalid attached object assignment"));
1886 Q_ASSERT(type->attachedPropertiesFunction());
1887 prop->index = type->index();
1888 prop->value->metatype = enginePrivate->cache(type->attachedPropertiesType());
1890 COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt));
1896 void QQmlCompiler::genValueProperty(QQmlScript::Property *prop,
1897 QQmlScript::Object *obj)
1899 if (prop->core.isQList()) {
1900 genListProperty(prop, obj);
1902 genPropertyAssignment(prop, obj);
1906 void QQmlCompiler::genListProperty(QQmlScript::Property *prop,
1907 QQmlScript::Object *obj)
1909 int listType = enginePrivate->listType(prop->type);
1911 Instruction::FetchQList fetch;
1912 fetch.property = prop->index;
1913 bool listTypeIsInterface = QQmlMetaType::isInterface(listType);
1914 fetch.type = listType;
1915 output->addInstruction(fetch);
1917 for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
1919 if (v->type == Value::CreatedObject) {
1921 genObject(v->object);
1922 if (listTypeIsInterface) {
1923 Instruction::AssignObjectList assign;
1924 assign.line = prop->location.start.line;
1925 output->addInstruction(assign);
1927 Instruction::StoreObjectQList store;
1928 output->addInstruction(store);
1931 } else if (v->type == Value::PropertyBinding) {
1933 genBindingAssignment(v, prop, obj);
1939 Instruction::PopQList pop;
1940 output->addInstruction(pop);
1943 void QQmlCompiler::genPropertyAssignment(QQmlScript::Property *prop,
1944 QQmlScript::Object *obj,
1945 QQmlScript::Property *valueTypeProperty)
1947 for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
1949 Q_ASSERT(v->type == Value::CreatedObject ||
1950 v->type == Value::PropertyBinding ||
1951 v->type == Value::Literal);
1953 if (v->type == Value::CreatedObject) {
1955 genObject(v->object);
1957 if (QQmlMetaType::isInterface(prop->type)) {
1959 Instruction::StoreInterface store;
1960 store.line = v->object->location.start.line;
1961 store.propertyIndex = prop->index;
1962 output->addInstruction(store);
1964 } else if (prop->type == QMetaType::QVariant) {
1966 if (prop->core.isVarProperty()) {
1967 Instruction::StoreVarObject store;
1968 store.line = v->object->location.start.line;
1969 store.propertyIndex = prop->index;
1970 output->addInstruction(store);
1972 Instruction::StoreVariantObject store;
1973 store.line = v->object->location.start.line;
1974 store.propertyIndex = prop->index;
1975 output->addInstruction(store);
1981 Instruction::StoreObject store;
1982 store.line = v->object->location.start.line;
1983 store.propertyIndex = prop->index;
1984 output->addInstruction(store);
1987 } else if (v->type == Value::PropertyBinding) {
1989 genBindingAssignment(v, prop, obj, valueTypeProperty);
1991 } else if (v->type == Value::Literal) {
1993 genLiteralAssignment(prop, v);
1999 for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) {
2001 Q_ASSERT(v->type == Value::ValueSource ||
2002 v->type == Value::ValueInterceptor);
2004 if (v->type == Value::ValueSource) {
2005 genObject(v->object, valueTypeProperty?true:false);
2007 Instruction::StoreValueSource store;
2008 if (valueTypeProperty)
2009 store.property = genValueTypeData(prop, valueTypeProperty);
2011 store.property = prop->core;
2012 QQmlType *valueType = toQmlType(v->object);
2013 store.castValue = valueType->propertyValueSourceCast();
2014 output->addInstruction(store);
2016 } else if (v->type == Value::ValueInterceptor) {
2017 genObject(v->object, valueTypeProperty?true:false);
2019 Instruction::StoreValueInterceptor store;
2020 if (valueTypeProperty)
2021 store.property = genValueTypeData(prop, valueTypeProperty);
2023 store.property = prop->core;
2024 QQmlType *valueType = toQmlType(v->object);
2025 store.castValue = valueType->propertyValueInterceptorCast();
2026 output->addInstruction(store);
2032 bool QQmlCompiler::buildIdProperty(QQmlScript::Property *prop,
2033 QQmlScript::Object *obj)
2036 prop->values.isMany() ||
2037 prop->values.first()->object)
2038 COMPILE_EXCEPTION(prop, tr("Invalid use of id property"));
2040 QQmlScript::Value *idValue = prop->values.first();
2041 QString val = idValue->primitive();
2043 COMPILE_CHECK(checkValidId(idValue, val));
2045 if (compileState->ids.value(val))
2046 COMPILE_EXCEPTION(prop, tr("id is not unique"));
2048 prop->values.first()->type = Value::Id;
2056 void QQmlCompiler::addId(const QString &id, QQmlScript::Object *obj)
2059 Q_ASSERT(!compileState->ids.value(id));
2060 Q_ASSERT(obj->id == id);
2061 obj->idIndex = compileState->ids.count();
2062 compileState->ids.append(obj);
2065 void QQmlCompiler::addBindingReference(JSBindingReference *ref)
2067 Q_ASSERT(ref->value && !ref->value->bindingReference);
2068 ref->value->bindingReference = ref;
2069 compileState->totalBindingsCount++;
2070 compileState->bindings.prepend(ref);
2073 void QQmlCompiler::saveComponentState()
2075 Q_ASSERT(compileState->root);
2076 Q_ASSERT(compileState->root->componentCompileState == 0);
2078 compileState->root->componentCompileState = compileState;
2081 componentStats->savedComponentStats.append(componentStats->componentStat);
2084 QQmlCompilerTypes::ComponentCompileState *
2085 QQmlCompiler::componentState(QQmlScript::Object *obj)
2087 Q_ASSERT(obj->componentCompileState);
2088 return obj->componentCompileState;
2091 // Build attached property object. In this example,
2095 // GridView is an attached property object.
2096 bool QQmlCompiler::buildAttachedProperty(QQmlScript::Property *prop,
2097 QQmlScript::Object *obj,
2098 const BindingContext &ctxt)
2100 Q_ASSERT(prop->value);
2101 Q_ASSERT(prop->index != -1); // This is set in buildProperty()
2103 compileState->objectDepth.push();
2105 obj->addAttachedProperty(prop);
2107 COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr()));
2109 compileState->objectDepth.pop();
2115 // Build "grouped" properties. In this example:
2117 // font.pointSize: 12
2118 // font.family: "Helvetica"
2120 // font is a nested property. pointSize and family are not.
2121 bool QQmlCompiler::buildGroupedProperty(QQmlScript::Property *prop,
2122 QQmlScript::Object *obj,
2123 const BindingContext &ctxt)
2125 Q_ASSERT(prop->type != 0);
2126 Q_ASSERT(prop->index != -1);
2128 if (QQmlValueTypeFactory::isValueType(prop->type)) {
2129 QQmlValueType *valueType = QQmlValueTypeFactory::valueType(prop->type);
2130 if (prop->type >= 0 && valueType) {
2131 if (!prop->values.isEmpty()) {
2132 // Only error if we are assigning values, and not e.g. a property interceptor
2133 for (Property *dotProp = prop->value->properties.first(); dotProp; dotProp = prop->value->properties.next(dotProp)) {
2134 if (!dotProp->values.isEmpty()) {
2135 if (prop->values.first()->location < prop->value->location) {
2136 COMPILE_EXCEPTION(prop->value, tr( "Property has already been assigned a value"));
2138 COMPILE_EXCEPTION(prop->values.first(), tr( "Property has already been assigned a value"));
2144 if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration) {
2145 COMPILE_EXCEPTION(prop, tr( "Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
2148 if (prop->isAlias) {
2149 for (Property *vtProp = prop->value->properties.first(); vtProp; vtProp = prop->value->properties.next(vtProp)) {
2150 vtProp->isAlias = true;
2154 COMPILE_CHECK(buildValueTypeProperty(valueType, prop->value, obj, ctxt.incr()));
2156 // When building a value type where sub components are declared, this
2157 // code path is followed from buildProperty, even if there is a previous
2158 // assignment to the value type as a whole. Therefore we need to look
2159 // for (and build) assignments to the entire value type before looking
2160 // for any onValue assignments.
2161 for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
2163 COMPILE_EXCEPTION(v->object, tr("Objects cannot be assigned to value types"));
2165 COMPILE_CHECK(buildPropertyLiteralAssignment(prop, obj, v, ctxt));
2168 for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) {
2169 Q_ASSERT(v->object);
2170 COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, obj, v, ctxt));
2173 obj->addValueTypeProperty(prop);
2175 COMPILE_EXCEPTION(prop, tr("Invalid grouped property access"));
2178 // Load the nested property's meta type
2179 prop->value->metatype = enginePrivate->propertyCacheForType(prop->type);
2180 if (!prop->value->metatype)
2181 COMPILE_EXCEPTION(prop, tr("Invalid grouped property access"));
2183 if (!prop->values.isEmpty())
2184 COMPILE_EXCEPTION(prop->values.first(), tr( "Cannot assign a value directly to a grouped property"));
2186 obj->addGroupedProperty(prop);
2188 compileState->objectDepth.push();
2190 COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr()));
2192 compileState->objectDepth.pop();
2198 bool QQmlCompiler::buildValueTypeProperty(QObject *type,
2199 QQmlScript::Object *obj,
2200 QQmlScript::Object *baseObj,
2201 const BindingContext &ctxt)
2203 compileState->objectDepth.push();
2205 if (obj->defaultProperty)
2206 COMPILE_EXCEPTION(obj, tr("Invalid property use"));
2207 obj->metatype = enginePrivate->cache(type);
2209 for (Property *prop = obj->properties.first(); prop; prop = obj->properties.next(prop)) {
2211 QQmlPropertyData *d = property(obj, prop->name());
2213 COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(prop->name().toString()));
2215 prop->index = d->coreIndex;
2216 prop->type = d->propType;
2218 prop->isValueTypeSubProperty = true;
2221 COMPILE_EXCEPTION(prop, tr("Property assignment expected"));
2223 if (prop->values.isMany()) {
2224 COMPILE_EXCEPTION(prop, tr("Single property assignment expected"));
2225 } else if (!prop->values.isEmpty()) {
2226 QQmlScript::Value *value = prop->values.first();
2228 if (value->object) {
2229 COMPILE_EXCEPTION(prop, tr("Unexpected object assignment"));
2230 } else if (value->value.isScript()) {
2231 // ### Check for writability
2233 //optimization for <Type>.<EnumValue> enum assignments
2234 bool isEnumAssignment = false;
2236 if (prop->core.isEnum() || prop->core.propType == QMetaType::Int)
2237 COMPILE_CHECK(testQualifiedEnumAssignment(prop, obj, value, &isEnumAssignment));
2239 if (isEnumAssignment) {
2240 value->type = Value::Literal;
2242 JSBindingReference *reference = pool->New<JSBindingReference>();
2243 reference->expression = value->value;
2244 reference->property = prop;
2245 reference->value = value;
2246 reference->bindingContext = ctxt;
2247 reference->bindingContext.owner++;
2248 addBindingReference(reference);
2249 value->type = Value::PropertyBinding;
2252 COMPILE_CHECK(testLiteralAssignment(prop, value));
2253 value->type = Value::Literal;
2257 for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) {
2258 Q_ASSERT(v->object);
2260 COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, baseObj, v, ctxt));
2263 obj->addValueProperty(prop);
2266 compileState->objectDepth.pop();
2271 // Build assignments to QML lists. QML lists are properties of type
2272 // QQmlListProperty<T>. List properties can accept a list of
2273 // objects, or a single binding.
2274 bool QQmlCompiler::buildListProperty(QQmlScript::Property *prop,
2275 QQmlScript::Object *obj,
2276 const BindingContext &ctxt)
2278 Q_ASSERT(prop->core.isQList());
2280 compileState->listDepth.push();
2284 obj->addValueProperty(prop);
2286 int listType = enginePrivate->listType(t);
2287 bool listTypeIsInterface = QQmlMetaType::isInterface(listType);
2289 bool assignedBinding = false;
2290 for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
2292 v->type = Value::CreatedObject;
2293 COMPILE_CHECK(buildObject(v->object, ctxt));
2295 // We check object coercian here. We check interface assignment
2297 if (!listTypeIsInterface) {
2298 if (!canCoerce(listType, v->object)) {
2299 COMPILE_EXCEPTION(v, tr("Cannot assign object to list"));
2303 } else if (v->value.isScript()) {
2304 if (assignedBinding)
2305 COMPILE_EXCEPTION(v, tr("Can only assign one binding to lists"));
2307 assignedBinding = true;
2308 COMPILE_CHECK(buildBinding(v, prop, ctxt));
2309 v->type = Value::PropertyBinding;
2311 COMPILE_EXCEPTION(v, tr("Cannot assign primitives to lists"));
2315 compileState->listDepth.pop();
2320 // Compiles an assignment to a QQmlScriptString property
2321 bool QQmlCompiler::buildScriptStringProperty(QQmlScript::Property *prop,
2322 QQmlScript::Object *obj,
2323 const BindingContext &ctxt)
2325 if (prop->values.isMany())
2326 COMPILE_EXCEPTION(prop->values.first()->nextValue, tr( "Cannot assign multiple values to a script property"));
2328 if (prop->values.first()->object)
2329 COMPILE_EXCEPTION(prop->values.first(), tr( "Invalid property assignment: script expected"));
2331 prop->scriptStringScope = ctxt.stack;
2332 obj->addScriptStringProperty(prop);
2337 // Compile regular property assignments of the form "property: <value>"
2338 bool QQmlCompiler::buildPropertyAssignment(QQmlScript::Property *prop,
2339 QQmlScript::Object *obj,
2340 const BindingContext &ctxt)
2342 obj->addValueProperty(prop);
2344 if (prop->values.isMany())
2345 COMPILE_EXCEPTION(prop->values.first(), tr( "Cannot assign multiple values to a singular property") );
2347 for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
2350 COMPILE_CHECK(buildPropertyObjectAssignment(prop, obj, v, ctxt));
2354 COMPILE_CHECK(buildPropertyLiteralAssignment(prop, obj, v, ctxt));
2359 for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) {
2360 Q_ASSERT(v->object);
2361 COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, obj, v, ctxt));
2367 // Compile assigning a single object instance to a regular property
2368 bool QQmlCompiler::buildPropertyObjectAssignment(QQmlScript::Property *prop,
2369 QQmlScript::Object *obj,
2370 QQmlScript::Value *v,
2371 const BindingContext &ctxt)
2373 Q_ASSERT(prop->index != -1);
2374 Q_ASSERT(v->object->type != -1);
2376 if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration)
2377 COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
2379 if (QQmlMetaType::isInterface(prop->type)) {
2381 // Assigning an object to an interface ptr property
2382 COMPILE_CHECK(buildObject(v->object, ctxt));
2384 v->type = Value::CreatedObject;
2386 } else if (prop->type == QMetaType::QVariant) {
2388 // Assigning an object to a QVariant
2389 COMPILE_CHECK(buildObject(v->object, ctxt));
2391 v->type = Value::CreatedObject;
2393 // Normally buildObject() will set this up, but we need the static
2394 // meta object earlier to test for assignability. It doesn't matter
2395 // that there may still be outstanding synthesized meta object changes
2396 // on this type, as they are not relevant for assignability testing
2397 v->object->metatype = output->types[v->object->type].createPropertyCache(engine);
2398 Q_ASSERT(v->object->metatype);
2400 // We want to raw metaObject here as the raw metaobject is the
2401 // actual property type before we applied any extensions that might
2402 // effect the properties on the type, but don't effect assignability
2403 QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(prop->type);
2405 // Will be true if the assgned type inherits propertyMetaObject
2406 bool isAssignable = false;
2407 // Determine isAssignable value
2408 if (propertyMetaObject) {
2409 QQmlPropertyCache *c = v->object->metatype;
2410 while (c && !isAssignable) {
2411 isAssignable |= c == propertyMetaObject;
2417 // Simple assignment
2418 COMPILE_CHECK(buildObject(v->object, ctxt));
2420 v->type = Value::CreatedObject;
2421 } else if (propertyMetaObject && propertyMetaObject->metaObject() == &QQmlComponent::staticMetaObject) {
2422 // Automatic "Component" insertion
2423 QQmlScript::Object *root = v->object;
2424 QQmlScript::Object *component = pool->New<Object>();
2425 component->type = componentTypeRef();
2426 component->metatype = enginePrivate->cache(&QQmlComponent::staticMetaObject);
2427 component->location = root->location;
2428 QQmlScript::Value *componentValue = pool->New<Value>();
2429 componentValue->object = root;
2430 component->getDefaultProperty()->addValue(componentValue);
2431 v->object = component;
2432 COMPILE_CHECK(buildPropertyObjectAssignment(prop, obj, v, ctxt));
2434 COMPILE_EXCEPTION(v->object, tr("Cannot assign object to property"));
2441 // Compile assigning a single object instance to a regular property using the "on" syntax.
2445 // NumberAnimation on x { }
2447 bool QQmlCompiler::buildPropertyOnAssignment(QQmlScript::Property *prop,
2448 QQmlScript::Object *obj,
2449 QQmlScript::Object *baseObj,
2450 QQmlScript::Value *v,
2451 const BindingContext &ctxt)
2453 Q_ASSERT(prop->index != -1);
2454 Q_ASSERT(v->object->type != -1);
2458 if (!prop->core.isWritable())
2459 COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
2462 // Normally buildObject() will set this up, but we need the static
2463 // meta object earlier to test for assignability. It doesn't matter
2464 // that there may still be outstanding synthesized meta object changes
2465 // on this type, as they are not relevant for assignability testing
2466 v->object->metatype = output->types[v->object->type].createPropertyCache(engine);
2467 Q_ASSERT(v->object->metatype);
2469 // Will be true if the assigned type inherits QQmlPropertyValueSource
2470 bool isPropertyValue = false;
2471 // Will be true if the assigned type inherits QQmlPropertyValueInterceptor
2472 bool isPropertyInterceptor = false;
2473 if (QQmlType *valueType = toQmlType(v->object)) {
2474 isPropertyValue = valueType->propertyValueSourceCast() != -1;
2475 isPropertyInterceptor = valueType->propertyValueInterceptorCast() != -1;
2478 if (isPropertyValue || isPropertyInterceptor) {
2479 // Assign as a property value source
2480 COMPILE_CHECK(buildObject(v->object, ctxt));
2482 if (isPropertyInterceptor && baseObj->synthdata.isEmpty())
2483 buildDynamicMeta(baseObj, ForceCreation);
2484 v->type = isPropertyValue ? Value::ValueSource : Value::ValueInterceptor;
2486 COMPILE_EXCEPTION(v, tr("\"%1\" cannot operate on \"%2\"").arg(elementName(v->object)).arg(prop->name().toString()));
2492 // Compile assigning a literal or binding to a regular property
2493 bool QQmlCompiler::buildPropertyLiteralAssignment(QQmlScript::Property *prop,
2494 QQmlScript::Object *obj,
2495 QQmlScript::Value *v,
2496 const BindingContext &ctxt)
2498 Q_ASSERT(prop->index != -1);
2500 if (v->value.isScript()) {
2502 //optimization for <Type>.<EnumValue> enum assignments
2503 if (prop->core.isEnum() || prop->core.propType == QMetaType::Int) {
2504 bool isEnumAssignment = false;
2505 COMPILE_CHECK(testQualifiedEnumAssignment(prop, obj, v, &isEnumAssignment));
2506 if (isEnumAssignment) {
2507 v->type = Value::Literal;
2512 // Test for other binding optimizations
2513 if (!buildLiteralBinding(v, prop, ctxt))
2514 COMPILE_CHECK(buildBinding(v, prop, ctxt));
2516 v->type = Value::PropertyBinding;
2520 COMPILE_CHECK(testLiteralAssignment(prop, v));
2522 v->type = Value::Literal;
2528 struct StaticQtMetaObject : public QObject
2530 static const QMetaObject *get()
2531 { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; }
2534 bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop,
2535 QQmlScript::Object *obj,
2536 QQmlScript::Value *v,
2539 bool isIntProp = (prop->core.propType == QMetaType::Int) && !prop->core.isEnum();
2540 *isAssignment = false;
2541 if (!prop->core.isEnum() && !isIntProp)
2544 QMetaProperty mprop = obj->metatype->firstCppMetaObject()->property(prop->index);
2546 if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration)
2547 COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
2549 QString string = v->value.asString();
2550 if (!string.at(0).isUpper())
2553 int dot = string.indexOf(QLatin1Char('.'));
2554 if (dot == -1 || dot == string.length()-1)
2557 if (string.indexOf(QLatin1Char('.'), dot+1) != -1)
2560 QHashedStringRef typeName(string.constData(), dot);
2561 QString enumValue = string.mid(dot+1);
2564 // Allow enum assignment to ints.
2566 int enumval = evaluateEnum(typeName, enumValue.toUtf8(), &ok);
2568 v->type = Value::Literal;
2569 v->value = QQmlScript::Variant((double)enumval);
2570 *isAssignment = true;
2576 unit->imports().resolveType(typeName, &type, 0, 0, 0, 0);
2578 if (!type && typeName != QLatin1String("Qt"))
2584 if (type && toQmlType(obj) == type) {
2585 // When these two match, we can short cut the search
2586 if (mprop.isFlagType()) {
2587 value = mprop.enumerator().keysToValue(enumValue.toUtf8().constData(), &ok);
2589 value = mprop.enumerator().keyToValue(enumValue.toUtf8().constData(), &ok);
2592 // Otherwise we have to search the whole type
2594 value = type->enumValue(QHashedStringRef(enumValue), &ok);
2596 QByteArray enumName = enumValue.toUtf8();
2597 const QMetaObject *metaObject = StaticQtMetaObject::get();
2598 for (int ii = metaObject->enumeratorCount() - 1; !ok && ii >= 0; --ii) {
2599 QMetaEnum e = metaObject->enumerator(ii);
2600 value = e.keyToValue(enumName.constData(), &ok);
2608 v->type = Value::Literal;
2609 v->value = QQmlScript::Variant((double)value);
2610 *isAssignment = true;
2615 // Similar logic to above, but not knowing target property.
2616 int QQmlCompiler::evaluateEnum(const QHashedStringRef &scope, const QByteArray& enumValue, bool *ok) const
2618 Q_ASSERT_X(ok, "QQmlCompiler::evaluateEnum", "ok must not be a null pointer");
2621 if (scope != QLatin1String("Qt")) {
2623 unit->imports().resolveType(scope, &type, 0, 0, 0, 0);
2624 return type ? type->enumValue(QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1;
2627 const QMetaObject *mo = StaticQtMetaObject::get();
2628 int i = mo->enumeratorCount();
2630 int v = mo->enumerator(i).keyToValue(enumValue.constData(), ok);
2637 const QMetaObject *QQmlCompiler::resolveType(const QString& name) const
2639 QQmlType *qmltype = 0;
2640 if (!unit->imports().resolveType(name, &qmltype, 0, 0, 0, 0))
2644 return qmltype->metaObject();
2647 // similar to logic of completeComponentBuild, but also sticks data
2648 // into primitives at the end
2649 int QQmlCompiler::rewriteBinding(const QQmlScript::Variant& value, const QString& name)
2651 QQmlRewrite::RewriteBinding rewriteBinding;
2652 rewriteBinding.setName(QLatin1Char('$') + name.mid(name.lastIndexOf(QLatin1Char('.')) + 1));
2654 QString rewrite = rewriteBinding(value.asAST(), value.asScript(), 0);
2656 return output->indexForString(rewrite);
2659 QString QQmlCompiler::rewriteSignalHandler(const QQmlScript::Variant& value, const QString &name)
2661 QQmlRewrite::RewriteSignalHandler rewriteSignalHandler;
2662 return rewriteSignalHandler(value.asAST(), value.asScript(), name);
2665 // Ensures that the dynamic meta specification on obj is valid
2666 bool QQmlCompiler::checkDynamicMeta(QQmlScript::Object *obj)
2668 bool seenDefaultProperty = false;
2670 // We use a coarse grain, 31 bit hash to check if there are duplicates.
2671 // Calculating the hash for the names is not a waste as we have to test
2672 // them against the illegalNames set anyway.
2673 QHashField propNames;
2674 QHashField methodNames;
2677 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) {
2678 const QQmlScript::Object::DynamicProperty &prop = *p;
2680 if (prop.isDefaultProperty) {
2681 if (seenDefaultProperty)
2682 COMPILE_EXCEPTION(&prop, tr("Duplicate default property"));
2683 seenDefaultProperty = true;
2686 if (propNames.testAndSet(prop.name.hash())) {
2687 for (Object::DynamicProperty *p2 = obj->dynamicProperties.first(); p2 != p;
2688 p2 = obj->dynamicProperties.next(p2)) {
2689 if (p2->name == prop.name) {
2690 COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line,
2691 prop.nameLocation.column,
2692 tr("Duplicate property name"));
2697 if (prop.name.at(0).isUpper()) {
2698 COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line,
2699 prop.nameLocation.column,
2700 tr("Property names cannot begin with an upper case letter"));
2703 if (enginePrivate->v8engine()->illegalNames().contains(prop.name)) {
2704 COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line,
2705 prop.nameLocation.column,
2706 tr("Illegal property name"));
2710 for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) {
2711 const QQmlScript::Object::DynamicSignal &currSig = *s;
2713 if (methodNames.testAndSet(currSig.name.hash())) {
2714 for (Object::DynamicSignal *s2 = obj->dynamicSignals.first(); s2 != s;
2715 s2 = obj->dynamicSignals.next(s2)) {
2716 if (s2->name == currSig.name)
2717 COMPILE_EXCEPTION(&currSig, tr("Duplicate signal name"));
2721 if (currSig.name.at(0).isUpper())
2722 COMPILE_EXCEPTION(&currSig, tr("Signal names cannot begin with an upper case letter"));
2723 if (enginePrivate->v8engine()->illegalNames().contains(currSig.name))
2724 COMPILE_EXCEPTION(&currSig, tr("Illegal signal name"));
2727 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
2728 const QQmlScript::Object::DynamicSlot &currSlot = *s;
2730 if (methodNames.testAndSet(currSlot.name.hash())) {
2731 for (Object::DynamicSignal *s2 = obj->dynamicSignals.first(); s2;
2732 s2 = obj->dynamicSignals.next(s2)) {
2733 if (s2->name == currSlot.name)
2734 COMPILE_EXCEPTION(&currSlot, tr("Duplicate method name"));
2736 for (Object::DynamicSlot *s2 = obj->dynamicSlots.first(); s2 != s;
2737 s2 = obj->dynamicSlots.next(s2)) {
2738 if (s2->name == currSlot.name)
2739 COMPILE_EXCEPTION(&currSlot, tr("Duplicate method name"));
2743 if (currSlot.name.at(0).isUpper())
2744 COMPILE_EXCEPTION(&currSlot, tr("Method names cannot begin with an upper case letter"));
2745 if (enginePrivate->v8engine()->illegalNames().contains(currSlot.name))
2746 COMPILE_EXCEPTION(&currSlot, tr("Illegal method name"));
2752 bool QQmlCompiler::mergeDynamicMetaProperties(QQmlScript::Object *obj)
2754 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
2755 p = obj->dynamicProperties.next(p)) {
2757 if (!p->defaultValue || p->type == Object::DynamicProperty::Alias)
2760 Property *property = 0;
2761 if (p->isDefaultProperty) {
2762 property = obj->getDefaultProperty();
2764 property = obj->getProperty(p->name);
2765 if (!property->values.isEmpty())
2766 COMPILE_EXCEPTION(property, tr("Property value set multiple times"));
2770 property->isReadOnlyDeclaration = true;
2772 if (property->value)
2773 COMPILE_EXCEPTION(property, tr("Invalid property nesting"));
2775 property->values.append(p->defaultValue->values);
2780 #include <private/qqmljsparser_p.h>
2782 static QStringList astNodeToStringList(QQmlJS::AST::Node *node)
2784 if (node->kind == QQmlJS::AST::Node::Kind_IdentifierExpression) {
2786 static_cast<QQmlJS::AST::IdentifierExpression *>(node)->name.toString();
2787 return QStringList() << name;
2788 } else if (node->kind == QQmlJS::AST::Node::Kind_FieldMemberExpression) {
2789 QQmlJS::AST::FieldMemberExpression *expr = static_cast<QQmlJS::AST::FieldMemberExpression *>(node);
2791 QStringList rv = astNodeToStringList(expr->base);
2794 rv.append(expr->name.toString());
2797 return QStringList();
2800 static QAtomicInt classIndexCounter(0);
2802 bool QQmlCompiler::buildDynamicMeta(QQmlScript::Object *obj, DynamicMetaMode mode)
2805 Q_ASSERT(obj->metatype);
2807 if (mode != ForceCreation &&
2808 obj->dynamicProperties.isEmpty() &&
2809 obj->dynamicSignals.isEmpty() &&
2810 obj->dynamicSlots.isEmpty())
2813 Q_ASSERT(obj->synthCache == 0);
2816 Object::DynamicProperty::Type dtype;
2818 } builtinTypes[] = {
2819 { Object::DynamicProperty::Var, QMetaType::QVariant },
2820 { Object::DynamicProperty::Variant, QMetaType::QVariant },
2821 { Object::DynamicProperty::Int, QMetaType::Int },
2822 { Object::DynamicProperty::Bool, QMetaType::Bool },
2823 { Object::DynamicProperty::Real, QMetaType::Double },
2824 { Object::DynamicProperty::String, QMetaType::QString },
2825 { Object::DynamicProperty::Url, QMetaType::QUrl },
2826 { Object::DynamicProperty::Color, QMetaType::QColor },
2827 { Object::DynamicProperty::Font, QMetaType::QFont },
2828 { Object::DynamicProperty::Time, QMetaType::QTime },
2829 { Object::DynamicProperty::Date, QMetaType::QDate },
2830 { Object::DynamicProperty::DateTime, QMetaType::QDateTime },
2831 { Object::DynamicProperty::Rect, QMetaType::QRectF },
2832 { Object::DynamicProperty::Point, QMetaType::QPointF },
2833 { Object::DynamicProperty::Size, QMetaType::QSizeF },
2834 { Object::DynamicProperty::Vector2D, QMetaType::QVector2D },
2835 { Object::DynamicProperty::Vector3D, QMetaType::QVector3D },
2836 { Object::DynamicProperty::Vector4D, QMetaType::QVector4D },
2837 { Object::DynamicProperty::Matrix4x4, QMetaType::QMatrix4x4 },
2838 { Object::DynamicProperty::Quaternion, QMetaType::QQuaternion }
2840 static const int builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData);
2842 QByteArray newClassName;
2844 if (compileState->root == obj && !compileState->nested) {
2845 QString path = output->url.path();
2846 int lastSlash = path.lastIndexOf(QLatin1Char('/'));
2847 if (lastSlash > -1) {
2848 QString nameBase = path.mid(lastSlash + 1, path.length()-lastSlash-5);
2849 if (!nameBase.isEmpty() && nameBase.at(0).isUpper())
2850 newClassName = nameBase.toUtf8() + "_QMLTYPE_" +
2851 QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
2854 if (newClassName.isEmpty()) {
2855 newClassName = QQmlMetaObject(obj->metatype).className();
2856 newClassName.append("_QML_");
2857 newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)));
2859 QQmlPropertyCache *cache = obj->metatype->copyAndReserve(engine, obj->dynamicProperties.count(),
2860 obj->dynamicProperties.count() +
2861 obj->dynamicSignals.count() +
2862 obj->dynamicSlots.count(),
2863 obj->dynamicProperties.count() +
2864 obj->dynamicSignals.count());
2866 cache->_dynamicClassName = newClassName;
2868 int cStringNameCount = 0;
2871 int varPropCount = 0;
2873 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
2874 p = obj->dynamicProperties.next(p)) {
2876 if (p->type == Object::DynamicProperty::Alias)
2878 else if (p->type == Object::DynamicProperty::Var)
2881 if (p->name.isLatin1()) {
2882 p->nameIndex = cStringNameCount;
2883 cStringNameCount += p->name.length() + 7 /* strlen("Changed") */;
2886 // No point doing this for both the alias and non alias cases
2887 QQmlPropertyData *d = property(obj, p->name);
2888 if (d && d->isFinal())
2889 COMPILE_EXCEPTION(p, tr("Cannot override FINAL property"));
2892 for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) {
2893 if (s->name.isLatin1()) {
2894 s->nameIndex = cStringNameCount;
2895 cStringNameCount += s->name.length();
2899 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
2900 if (s->name.isLatin1()) {
2901 s->nameIndex = cStringNameCount;
2902 cStringNameCount += s->name.length();
2906 char *cStringData = 0;
2907 if (cStringNameCount) {
2908 cache->_dynamicStringData.resize(cStringNameCount);
2909 cStringData = cache->_dynamicStringData.data();
2911 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
2912 p = obj->dynamicProperties.next(p)) {
2914 if (p->nameIndex == -1) continue;
2916 char *myData = cStringData + p->nameIndex;
2917 for (int ii = 0; ii < p->name.length(); ++ii)
2918 *myData++ = p->name.at(ii).unicode();
2919 *myData++ = 'C'; *myData++ = 'h'; *myData++ = 'a'; *myData++ = 'n';
2920 *myData++ = 'g'; *myData++ = 'e'; *myData++ = 'd';
2923 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
2925 if (s->nameIndex == -1) continue;
2927 char *myData = cStringData + s->nameIndex;
2928 for (int ii = 0; ii < s->name.length(); ++ii)
2929 *myData++ = s->name.at(ii).unicode();
2932 for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s;
2933 s = obj->dynamicSignals.next(s)) {
2935 if (s->nameIndex == -1) continue;
2937 char *myData = cStringData + s->nameIndex;
2938 for (int ii = 0; ii < s->name.length(); ++ii)
2939 *myData++ = s->name.at(ii).unicode();
2943 QByteArray dynamicData;
2944 typedef QQmlVMEMetaData VMD;
2946 dynamicData = QByteArray(sizeof(QQmlVMEMetaData) +
2947 obj->dynamicProperties.count() * sizeof(VMD::PropertyData) +
2948 obj->dynamicSlots.count() * sizeof(VMD::MethodData) +
2949 aliasCount * sizeof(VMD::AliasData), 0);
2951 int effectivePropertyIndex = cache->propertyIndexCacheStart;
2952 int effectiveMethodIndex = cache->methodIndexCacheStart;
2954 // For property change signal override detection.
2955 // We prepopulate a set of signal names which already exist in the object,
2956 // and throw an error if there is a signal/method defined as an override.
2957 QSet<QString> seenSignals;
2958 seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged");
2959 QQmlPropertyCache *parentCache = cache;
2960 while ((parentCache = parentCache->parent())) {
2961 if (int pSigCount = parentCache->signalCount()) {
2962 int pSigOffset = parentCache->signalOffset();
2963 for (int i = pSigOffset; i < pSigCount; ++i) {
2964 QQmlPropertyData *currPSig = parentCache->signal(i);
2965 // XXX TODO: find a better way to get signal name from the property data :-/
2966 for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin();
2967 iter != parentCache->stringCache.end(); ++iter) {
2968 if (currPSig == (*iter).second) {
2969 seenSignals.insert(iter.key());
2977 // First set up notify signals for properties - first normal, then var, then alias
2978 enum { NSS_Normal = 0, NSS_Var = 1, NSS_Alias = 2 };
2979 for (int ii = NSS_Normal; ii <= NSS_Alias; ++ii) { // 0 == normal, 1 == var, 2 == alias
2981 if (ii == NSS_Var && varPropCount == 0) continue;
2982 else if (ii == NSS_Alias && aliasCount == 0) continue;
2984 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
2985 p = obj->dynamicProperties.next(p)) {
2987 if ((ii == NSS_Normal && (p->type == Object::DynamicProperty::Alias ||
2988 p->type == Object::DynamicProperty::Var)) ||
2989 ((ii == NSS_Var) && (p->type != Object::DynamicProperty::Var)) ||
2990 ((ii == NSS_Alias) && (p->type != Object::DynamicProperty::Alias)))
2993 quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction |
2994 QQmlPropertyData::IsVMESignal;
2996 QString changedSigName = p->name.toString() + QLatin1String("Changed");
2997 seenSignals.insert(changedSigName);
2999 if (p->nameIndex != -1) {
3000 QHashedCStringRef changedSignalName(cStringData + p->nameIndex,
3001 p->name.length() + 7 /* strlen("Changed") */);
3002 cache->appendSignal(changedSignalName, flags, effectiveMethodIndex++);
3004 cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
3010 for (Object::DynamicSignal *s = obj->dynamicSignals.first(); s; s = obj->dynamicSignals.next(s)) {
3011 int paramCount = s->parameterNames.count();
3013 QList<QByteArray> names;
3014 QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0);
3017 paramTypes[0] = paramCount;
3019 for (int i = 0; i < paramCount; ++i) {
3020 if (s->parameterTypes.at(i) < builtinTypeCount) {
3022 paramTypes[i + 1] = builtinTypes[s->parameterTypes.at(i)].metaType;
3023 names.append(s->parameterNames.at(i).toString().toUtf8());
3025 // lazily resolved type
3026 Q_ASSERT(s->parameterTypes.at(i) == Object::DynamicProperty::Custom);
3027 QQmlType *qmltype = 0;
3029 if (!unit->imports().resolveType(s->parameterTypeNames.at(i).toString(), &qmltype, &url, 0, 0, 0))
3030 COMPILE_EXCEPTION(s, tr("Invalid signal parameter type: %1").arg(s->parameterTypeNames.at(i).toString()));
3033 QQmlTypeData *tdata = enginePrivate->typeLoader.getType(QUrl(url));
3035 Q_ASSERT(tdata->isComplete());
3037 QQmlCompiledData *data = tdata->compiledData();
3039 paramTypes[i + 1] = data->metaTypeId;
3043 paramTypes[i + 1] = qmltype->typeId();
3045 names.append(s->parameterNames.at(i).toString().toUtf8());
3050 ((QQmlVMEMetaData *)dynamicData.data())->signalCount++;
3052 quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction |
3053 QQmlPropertyData::IsVMESignal;
3055 flags |= QQmlPropertyData::HasArguments;
3057 QString signalName = s->name.toString();
3058 if (seenSignals.contains(signalName)) {
3059 const QQmlScript::Object::DynamicSignal &currSig = *s;
3060 COMPILE_EXCEPTION(&currSig, tr("Duplicate signal name: invalid override of property change signal or superclass signal"));
3062 seenSignals.insert(signalName);
3064 if (s->nameIndex != -1) {
3065 QHashedCStringRef name(cStringData + s->nameIndex, s->name.length(), s->name.hash());
3066 cache->appendSignal(name, flags, effectiveMethodIndex++,
3067 paramCount?paramTypes.constData():0, names);
3069 cache->appendSignal(signalName, flags, effectiveMethodIndex++,
3070 paramCount?paramTypes.constData():0, names);
3076 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
3077 int paramCount = s->parameterNames.count();
3079 quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction;
3082 flags |= QQmlPropertyData::HasArguments;
3084 QString slotName = s->name.toString();
3085 if (seenSignals.contains(slotName)) {
3086 const QQmlScript::Object::DynamicSlot &currSlot = *s;
3087 COMPILE_EXCEPTION(&currSlot, tr("Duplicate method name: invalid override of property change signal or superclass signal"));
3089 // Note: we don't append slotName to the seenSignals list, since we don't
3090 // protect against overriding change signals or methods with properties.
3092 if (s->nameIndex != -1) {
3093 QHashedCStringRef name(cStringData + s->nameIndex, s->name.length(), s->name.hash());
3094 cache->appendMethod(name, flags, effectiveMethodIndex++, s->parameterNames);
3096 cache->appendMethod(slotName, flags, effectiveMethodIndex++, s->parameterNames);
3101 // Dynamic properties (except var and aliases)
3102 int effectiveSignalIndex = cache->signalHandlerIndexCacheStart;
3103 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
3104 p = obj->dynamicProperties.next(p)) {
3106 if (p->type == Object::DynamicProperty::Alias ||
3107 p->type == Object::DynamicProperty::Var)
3110 int propertyType = 0;
3111 int vmePropertyType = 0;
3112 quint32 propertyFlags = 0;
3114 if (p->type < builtinTypeCount) {
3115 propertyType = builtinTypes[p->type].metaType;
3116 vmePropertyType = propertyType;
3118 if (p->type == Object::DynamicProperty::Variant)
3119 propertyFlags |= QQmlPropertyData::IsQVariant;
3121 Q_ASSERT(p->type == Object::DynamicProperty::CustomList ||
3122 p->type == Object::DynamicProperty::Custom);
3124 QQmlType *qmltype = 0;
3126 if (!unit->imports().resolveType(p->customType.toString(), &qmltype, &url, 0, 0, 0))
3127 COMPILE_EXCEPTION(p, tr("Invalid property type"));
3130 QQmlTypeData *tdata = enginePrivate->typeLoader.getType(QUrl(url));
3132 Q_ASSERT(tdata->isComplete());
3134 QQmlCompiledData *data = tdata->compiledData();
3136 if (p->type == Object::DynamicProperty::Custom) {
3137 propertyType = data->metaTypeId;
3138 vmePropertyType = QMetaType::QObjectStar;
3140 propertyType = data->listMetaTypeId;
3141 vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >();
3146 if (p->type == Object::DynamicProperty::Custom) {
3147 propertyType = qmltype->typeId();
3148 vmePropertyType = QMetaType::QObjectStar;
3150 propertyType = qmltype->qListTypeId();
3151 vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >();
3155 if (p->type == Object::DynamicProperty::Custom)
3156 propertyFlags |= QQmlPropertyData::IsQObjectDerived;
3158 propertyFlags |= QQmlPropertyData::IsQList;
3161 if (!p->isReadOnly && p->type != Object::DynamicProperty::CustomList)
3162 propertyFlags |= QQmlPropertyData::IsWritable;
3164 if (p->nameIndex != -1) {
3165 QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(),
3167 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16();
3168 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3169 propertyType, effectiveSignalIndex);
3171 QString propertyName = p->name.toString();
3172 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName;
3173 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3174 propertyType, effectiveSignalIndex);
3177 effectiveSignalIndex++;
3179 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3180 (vmd->propertyData() + vmd->propertyCount)->propertyType = vmePropertyType;
3181 vmd->propertyCount++;
3184 // Now do var properties
3185 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p && varPropCount;
3186 p = obj->dynamicProperties.next(p)) {
3188 if (p->type != Object::DynamicProperty::Var)
3191 quint32 propertyFlags = QQmlPropertyData::IsVarProperty;
3193 propertyFlags |= QQmlPropertyData::IsWritable;
3195 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3196 (vmd->propertyData() + vmd->propertyCount)->propertyType = QMetaType::QVariant;
3197 vmd->propertyCount++;
3198 ((QQmlVMEMetaData *)dynamicData.data())->varPropertyCount++;
3200 if (p->nameIndex != -1) {
3201 QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(),
3203 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16();
3204 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3205 QMetaType::QVariant, effectiveSignalIndex);
3207 QString propertyName = p->name.toString();
3208 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName;
3209 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3210 QMetaType::QVariant, effectiveSignalIndex);
3213 effectiveSignalIndex++;
3216 // Alias property count. Actual data is setup in buildDynamicMetaAliases
3217 ((QQmlVMEMetaData *)dynamicData.data())->aliasCount = aliasCount;
3219 // Dynamic slot data - comes after the property data
3220 for (Object::DynamicSlot *s = obj->dynamicSlots.first(); s; s = obj->dynamicSlots.next(s)) {
3221 int paramCount = s->parameterNames.count();
3225 if (paramCount) namesSize += s->parameterNamesLength() + (paramCount - 1 /* commas */);
3226 funcScript.reserve(strlen("(function ") + s->name.length() + 1 /* lparen */ +
3227 namesSize + 1 /* rparen */ + s->body.length() + 1 /* rparen */);
3228 funcScript = QLatin1String("(function ") + s->name.toString() + QLatin1Char('(');
3229 for (int jj = 0; jj < paramCount; ++jj) {
3230 if (jj) funcScript.append(QLatin1Char(','));
3231 funcScript.append(QLatin1String(s->parameterNames.at(jj)));
3233 funcScript += QLatin1Char(')') + s->body + QLatin1Char(')');
3235 QByteArray utf8 = funcScript.toUtf8();
3236 VMD::MethodData methodData = { s->parameterNames.count(),
3239 s->location.start.line };
3241 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3242 VMD::MethodData &md = *(vmd->methodData() + vmd->methodCount);
3246 dynamicData.append((const char *)utf8.constData(), utf8.length());
3250 compileState->aliasingObjects.append(obj);
3252 obj->synthdata = dynamicData;
3253 obj->synthCache = cache;
3254 obj->metatype = cache;
3259 bool QQmlCompiler::buildDynamicMetaAliases(QQmlScript::Object *obj)
3261 Q_ASSERT(obj->synthCache);
3263 QByteArray &dynamicData = obj->synthdata;
3265 QQmlPropertyCache *cache = obj->synthCache;
3266 char *cStringData = cache->_dynamicStringData.data();
3268 int effectiveSignalIndex = cache->signalHandlerIndexCacheStart + cache->propertyIndexCache.count();
3269 int effectivePropertyIndex = cache->propertyIndexCacheStart + cache->propertyIndexCache.count();
3270 int effectiveAliasIndex = 0;
3272 for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
3273 p = obj->dynamicProperties.next(p)) {
3275 if (p->type != Object::DynamicProperty::Alias)
3278 if (!p->defaultValue)
3279 COMPILE_EXCEPTION(obj, tr("No property alias location"));
3281 if (!p->defaultValue->values.isOne() ||
3282 p->defaultValue->values.first()->object ||
3283 !p->defaultValue->values.first()->value.isScript())
3284 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3286 QQmlJS::AST::Node *node = p->defaultValue->values.first()->value.asAST();
3289 QStringList alias = astNodeToStringList(node);
3290 if (alias.count() < 1 || alias.count() > 3)
3291 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
3293 QQmlScript::Object *idObject = compileState->ids.value(alias.at(0));
3295 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias reference. Unable to find id \"%1\"").arg(alias.at(0)));
3299 int notifySignal = -1;
3302 bool writable = false;
3303 bool resettable = false;
3305 quint32 propertyFlags = QQmlPropertyData::IsAlias;
3307 if (alias.count() == 2 || alias.count() == 3) {
3308 QQmlPropertyData *property = this->property(idObject, alias.at(1));
3310 if (!property || property->coreIndex > 0x0000FFFF)
3311 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3313 propIdx = property->coreIndex;
3314 type = property->propType;
3316 writable = property->isWritable();
3317 resettable = property->isResettable();
3318 notifySignal = property->notifyIndex;
3320 if (alias.count() == 3) {
3321 QQmlValueType *valueType = QQmlValueTypeFactory::valueType(type);
3323 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3327 int valueTypeIndex =
3328 valueType->metaObject()->indexOfProperty(alias.at(2).toUtf8().constData());
3329 if (valueTypeIndex == -1)
3330 COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location"));
3331 Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
3333 propIdx |= (valueTypeIndex << 16);
3334 if (valueType->metaObject()->property(valueTypeIndex).isEnumType())
3335 type = QVariant::Int;
3337 type = valueType->metaObject()->property(valueTypeIndex).userType();
3340 if (property->isEnum()) {
3341 type = QVariant::Int;
3344 propertyFlags |= property->getFlags() & QQmlPropertyData::PropTypeFlagMask;
3346 if (property->isVarProperty())
3347 propertyFlags |= QQmlPropertyData::IsQVariant;
3349 if (property->isQObject())
3350 flags |= QML_ALIAS_FLAG_PTR;
3354 Q_ASSERT(idObject->type != -1); // How else did it get an id?
3356 const QQmlCompiledData::TypeReference &ref = output->types.at(idObject->type);
3358 type = ref.type->typeId();
3360 type = ref.component->metaTypeId;
3362 flags |= QML_ALIAS_FLAG_PTR;
3363 propertyFlags |= QQmlPropertyData::IsQObjectDerived;
3366 QQmlVMEMetaData::AliasData aliasData = { idObject->idIndex, propIdx, propType, flags, notifySignal };
3368 typedef QQmlVMEMetaData VMD;
3369 VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
3370 *(vmd->aliasData() + effectiveAliasIndex++) = aliasData;
3372 if (!p->isReadOnly && writable)
3373 propertyFlags |= QQmlPropertyData::IsWritable;
3375 propertyFlags &= ~QQmlPropertyData::IsWritable;
3378 propertyFlags |= QQmlPropertyData::IsResettable;
3380 propertyFlags &= ~QQmlPropertyData::IsResettable;
3382 if (p->nameIndex != -1) {
3383 QHashedCStringRef propertyName(cStringData + p->nameIndex, p->name.length(),
3385 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16();
3386 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3387 type, effectiveSignalIndex++);
3389 QString propertyName = p->name.toString();
3390 if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName;
3391 cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
3392 type, effectiveSignalIndex++);
3399 bool QQmlCompiler::checkValidId(QQmlScript::Value *v, const QString &val)
3402 COMPILE_EXCEPTION(v, tr( "Invalid empty ID"));
3404 QChar ch = val.at(0);
3405 if (ch.isLetter() && !ch.isLower())
3406 COMPILE_EXCEPTION(v, tr( "IDs cannot start with an uppercase letter"));
3408 QChar u(QLatin1Char('_'));
3409 if (!ch.isLetter() && ch != u)
3410 COMPILE_EXCEPTION(v, tr( "IDs must start with a letter or underscore"));
3412 for (int ii = 1; ii < val.count(); ++ii) {
3414 if (!ch.isLetterOrNumber() && ch != u)
3415 COMPILE_EXCEPTION(v, tr( "IDs must contain only letters, numbers, and underscores"));
3418 if (enginePrivate->v8engine()->illegalNames().contains(val))
3419 COMPILE_EXCEPTION(v, tr( "ID illegally masks global JavaScript property"));
3424 bool QQmlCompiler::buildBinding(QQmlScript::Value *value,
3425 QQmlScript::Property *prop,
3426 const BindingContext &ctxt)
3428 Q_ASSERT(prop->index != -1);
3429 Q_ASSERT(prop->parent);
3430 Q_ASSERT(prop->parent->metatype);
3432 if (!prop->core.isWritable() && !prop->core.isQList() && !prop->isReadOnlyDeclaration)
3433 COMPILE_EXCEPTION(prop, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
3435 JSBindingReference *reference = pool->New<JSBindingReference>();
3436 reference->expression = value->value;
3437 reference->property = prop;
3438 reference->value = value;
3439 reference->bindingContext = ctxt;
3440 addBindingReference(reference);
3445 bool QQmlCompiler::buildLiteralBinding(QQmlScript::Value *v,
3446 QQmlScript::Property *prop,
3447 const QQmlCompilerTypes::BindingContext &)
3449 Q_ASSERT(v->value.isScript());
3451 if (!prop->core.isWritable())
3454 AST::Node *binding = v->value.asAST();
3456 if (prop->type == QVariant::String) {
3457 if (AST::CallExpression *e = AST::cast<AST::CallExpression *>(binding)) {
3458 if (AST::IdentifierExpression *i = AST::cast<AST::IdentifierExpression *>(e->base)) {
3459 if (i->name == qsTrId_string) {
3460 AST::ArgumentList *arg1 = e->arguments?e->arguments:0;
3461 AST::ArgumentList *arg2 = arg1?arg1->next:0;
3463 if (arg1 && arg1->expression->kind == AST::Node::Kind_StringLiteral &&
3464 (!arg2 || arg2->expression->kind == AST::Node::Kind_NumericLiteral) &&
3465 (!arg2 || !arg2->next)) {
3470 text = AST::cast<AST::StringLiteral *>(arg1->expression)->value;
3471 if (arg2) n = (int)AST::cast<AST::NumericLiteral *>(arg2->expression)->value;
3473 TrBindingReference *reference = pool->New<TrBindingReference>();
3474 reference->dataType = BindingReference::TrId;
3475 reference->text = text;
3477 v->bindingReference = reference;
3481 } else if (i->name == qsTr_string) {
3483 AST::ArgumentList *arg1 = e->arguments?e->arguments:0;
3484 AST::ArgumentList *arg2 = arg1?arg1->next:0;
3485 AST::ArgumentList *arg3 = arg2?arg2->next:0;
3487 if (arg1 && arg1->expression->kind == AST::Node::Kind_StringLiteral &&
3488 (!arg2 || arg2->expression->kind == AST::Node::Kind_StringLiteral) &&
3489 (!arg3 || arg3->expression->kind == AST::Node::Kind_NumericLiteral) &&
3490 (!arg3 || !arg3->next)) {
3496 text = AST::cast<AST::StringLiteral *>(arg1->expression)->value;
3497 if (arg2) comment = AST::cast<AST::StringLiteral *>(arg2->expression)->value;
3498 if (arg3) n = (int)AST::cast<AST::NumericLiteral *>(arg3->expression)->value;
3500 TrBindingReference *reference = pool->New<TrBindingReference>();
3501 reference->dataType = BindingReference::Tr;
3502 reference->text = text;
3503 reference->comment = comment;
3505 v->bindingReference = reference;
3518 void QQmlCompiler::genBindingAssignment(QQmlScript::Value *binding,
3519 QQmlScript::Property *prop,
3520 QQmlScript::Object *obj,
3521 QQmlScript::Property *valueTypeProperty)
3524 Q_ASSERT(binding->bindingReference);
3526 const BindingReference &ref = *binding->bindingReference;
3527 if (ref.dataType == BindingReference::TrId) {
3528 const TrBindingReference &tr = static_cast<const TrBindingReference &>(ref);
3530 Instruction::StoreTrIdString store;
3531 store.propertyIndex = prop->core.coreIndex;
3532 store.text = output->indexForByteArray(tr.text.toUtf8());
3534 output->addInstruction(store);
3535 } else if (ref.dataType == BindingReference::Tr) {
3536 const TrBindingReference &tr = static_cast<const TrBindingReference &>(ref);
3538 Instruction::StoreTrString store;
3539 store.propertyIndex = prop->core.coreIndex;
3540 store.context = translationContextIndex();
3541 store.text = output->indexForByteArray(tr.text.toUtf8());
3542 store.comment = output->indexForByteArray(tr.comment.toUtf8());
3544 output->addInstruction(store);
3545 } else if (ref.dataType == BindingReference::V4) {
3546 const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);
3548 Instruction::StoreV4Binding store;
3549 store.value = js.compiledIndex;
3550 store.fallbackValue = js.sharedIndex;
3551 store.context = js.bindingContext.stack;
3552 store.owner = js.bindingContext.owner;
3553 store.isAlias = prop->isAlias;
3554 if (valueTypeProperty) {
3555 store.property = ((prop->index << 16) | valueTypeProperty->index);
3556 store.propType = valueTypeProperty->type;
3557 store.isRoot = (compileState->root == valueTypeProperty->parent);
3559 store.property = prop->index;
3561 store.isRoot = (compileState->root == obj);
3563 store.line = binding->location.start.line;
3564 store.column = binding->location.start.column;
3565 output->addInstruction(store);
3567 if (store.fallbackValue > -1) {
3568 //also create v8 instruction (needed to properly configure the fallback v8 binding)
3569 JSBindingReference &js = static_cast<JSBindingReference &>(*binding->bindingReference);
3570 js.dataType = BindingReference::V8;
3571 genBindingAssignment(binding, prop, obj, valueTypeProperty);
3573 } else if (ref.dataType == BindingReference::V8) {
3574 const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);
3576 Instruction::StoreV8Binding store;
3577 store.value = js.sharedIndex;
3578 store.context = js.bindingContext.stack;
3579 store.owner = js.bindingContext.owner;
3580 store.isAlias = prop->isAlias;
3581 store.isSafe = js.isSafe;
3582 if (valueTypeProperty) {
3583 store.isRoot = (compileState->root == valueTypeProperty->parent);
3585 store.isRoot = (compileState->root == obj);
3587 store.isFallback = js.compiledIndex > -1;
3588 store.line = binding->location.start.line;
3589 store.column = binding->location.start.column;
3591 Q_ASSERT(js.bindingContext.owner == 0 ||
3592 (js.bindingContext.owner != 0 && valueTypeProperty));
3593 if (js.bindingContext.owner) {
3594 store.property = genValueTypeData(prop, valueTypeProperty);
3596 store.property = prop->core;
3599 output->addInstruction(store);
3600 } else if (ref.dataType == BindingReference::QtScript) {
3601 const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);
3603 Instruction::StoreBinding store;
3604 store.value = output->indexForString(js.rewrittenExpression);
3605 store.context = js.bindingContext.stack;
3606 store.owner = js.bindingContext.owner;
3607 store.line = binding->location.start.line;
3608 store.column = binding->location.start.column;
3609 store.isAlias = prop->isAlias;
3611 if (valueTypeProperty) {
3612 store.isRoot = (compileState->root == valueTypeProperty->parent);
3614 store.isRoot = (compileState->root == obj);
3616 store.isFallback = false;
3618 Q_ASSERT(js.bindingContext.owner == 0 ||
3619 (js.bindingContext.owner != 0 && valueTypeProperty));
3620 if (js.bindingContext.owner) {
3621 store.property = genValueTypeData(prop, valueTypeProperty);
3623 store.property = prop->core;
3626 output->addInstruction(store);
3628 Q_ASSERT(!"Unhandled BindingReference::DataType type");
3632 int QQmlCompiler::genContextCache()
3634 if (compileState->ids.count() == 0)
3637 QQmlIntegerCache *cache = new QQmlIntegerCache();
3638 cache->reserve(compileState->ids.count());
3639 for (Object *o = compileState->ids.first(); o; o = compileState->ids.next(o))
3640 cache->add(o->id, o->idIndex);
3642 output->contextCaches.append(cache);
3643 return output->contextCaches.count() - 1;
3647 QQmlCompiler::genValueTypeData(QQmlScript::Property *valueTypeProp,
3648 QQmlScript::Property *prop)
3650 QQmlValueType *vt = QQmlValueTypeFactory::valueType(prop->type);
3652 return QQmlPropertyPrivate::saveValueType(prop->core, vt->metaObject(), valueTypeProp->index, engine);
3655 bool QQmlCompiler::completeComponentBuild()
3658 componentStats->componentStat.ids = compileState->ids.count();
3660 for (Object *aliasObject = compileState->aliasingObjects.first(); aliasObject;
3661 aliasObject = compileState->aliasingObjects.next(aliasObject))
3662 COMPILE_CHECK(buildDynamicMetaAliases(aliasObject));
3664 QV4Compiler::Expression expr(unit->imports());
3665 expr.component = compileState->root;
3666 expr.ids = &compileState->ids;
3667 expr.importCache = output->importCache;
3669 QV4Compiler bindingCompiler;
3671 QList<JSBindingReference*> sharedBindings;
3673 for (JSBindingReference *b = compileState->bindings.first(); b; b = b->nextReference) {
3675 JSBindingReference &binding = *b;
3678 expr.context = binding.bindingContext.object;
3679 expr.property = binding.property;
3680 expr.expression = binding.expression;
3682 bool needsFallback = false;
3683 int index = bindingCompiler.compile(expr, enginePrivate, &needsFallback);
3685 binding.dataType = BindingReference::V4;
3686 binding.compiledIndex = index;
3687 binding.sharedIndex = -1;
3689 componentStats->componentStat.optimizedBindings.append(b->value->location);
3694 // Drop through. We need to create a V8 binding in case the V4 binding is invalidated
3697 // Pre-rewrite the expression
3698 QString expression = binding.expression.asScript();
3700 QQmlRewrite::RewriteBinding rewriteBinding;
3701 rewriteBinding.setName(QLatin1Char('$')+binding.property->name().toString());
3702 bool isSharable = false;
3703 bool isSafe = false;
3704 binding.rewrittenExpression = rewriteBinding(binding.expression.asAST(), expression, &isSharable, &isSafe);
3705 binding.isSafe = isSafe;
3707 if (isSharable && binding.property->type != qMetaTypeId<QQmlBinding*>()) {
3708 sharedBindings.append(b);
3710 if (!needsFallback) {
3711 binding.dataType = BindingReference::V8;
3712 binding.compiledIndex = -1;
3715 componentStats->componentStat.sharedBindings.append(b->value->location);
3718 Q_ASSERT(!needsFallback);
3719 binding.dataType = BindingReference::QtScript;
3722 componentStats->componentStat.scriptBindings.append(b->value->location);
3726 if (!sharedBindings.isEmpty()) {
3728 static bool lt(const JSBindingReference *lhs, const JSBindingReference *rhs)
3730 return lhs->value->location.start.line < rhs->value->location.start.line;
3734 qSort(sharedBindings.begin(), sharedBindings.end(), Sort::lt);
3736 int startLineNumber = sharedBindings.at(0)->value->location.start.line;
3737 int lineNumber = startLineNumber;
3739 QByteArray functionArray("[", 1);
3740 for (int ii = 0; ii < sharedBindings.count(); ++ii) {
3742 JSBindingReference *reference = sharedBindings.at(ii);
3743 QQmlScript::Value *value = reference->value;
3744 const QString &expression = reference->rewrittenExpression;
3746 if (ii != 0) functionArray.append(",", 1);
3748 while (lineNumber < value->location.start.line) {
3750 functionArray.append("\n", 1);
3753 functionArray += expression.toUtf8();
3754 lineNumber += expression.count(QLatin1Char('\n'));
3756 reference->sharedIndex = ii;
3758 functionArray.append("]", 1);
3760 compileState->v8BindingProgram = functionArray;
3761 compileState->v8BindingProgramLine = startLineNumber;
3764 if (bindingCompiler.isValid())
3765 compileState->compiledBindingData = bindingCompiler.program();
3767 // Check pop()'s matched push()'s
3768 Q_ASSERT(compileState->objectDepth.depth() == 0);
3769 Q_ASSERT(compileState->listDepth.depth() == 0);
3771 saveComponentState();
3776 void QQmlCompiler::dumpStats()
3778 Q_ASSERT(componentStats);
3779 qWarning().nospace() << "QML Document: " << output->url.toString();
3780 for (int ii = 0; ii < componentStats->savedComponentStats.count(); ++ii) {
3781 const ComponentStat &stat = componentStats->savedComponentStats.at(ii);
3782 qWarning().nospace() << " Component Line " << stat.lineNumber;
3783 qWarning().nospace() << " Total Objects: " << stat.objects;
3784 qWarning().nospace() << " IDs Used: " << stat.ids;
3785 qWarning().nospace() << " Optimized Bindings: " << stat.optimizedBindings.count();
3789 for (int ii = 0; ii < stat.optimizedBindings.count(); ++ii) {
3790 if (0 == (ii % 10)) {
3791 if (ii) output.append("\n");
3796 output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.line));
3798 output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.column));
3799 output.append(") ");
3801 if (!output.isEmpty())
3802 qWarning().nospace() << output.constData();
3805 qWarning().nospace() << " Shared Bindings: " << stat.sharedBindings.count();
3808 for (int ii = 0; ii < stat.sharedBindings.count(); ++ii) {
3809 if (0 == (ii % 10)) {
3810 if (ii) output.append('\n');
3815 output.append(QByteArray::number(stat.sharedBindings.at(ii).start.line));
3817 output.append(QByteArray::number(stat.sharedBindings.at(ii).start.column));
3818 output.append(") ");
3820 if (!output.isEmpty())
3821 qWarning().nospace() << output.constData();
3824 qWarning().nospace() << " QScript Bindings: " << stat.scriptBindings.count();
3827 for (int ii = 0; ii < stat.scriptBindings.count(); ++ii) {
3828 if (0 == (ii % 10)) {
3829 if (ii) output.append('\n');
3834 output.append(QByteArray::number(stat.scriptBindings.at(ii).start.line));
3836 output.append(QByteArray::number(stat.scriptBindings.at(ii).start.column));
3837 output.append(") ");
3839 if (!output.isEmpty())
3840 qWarning().nospace() << output.constData();
3846 Returns true if from can be assigned to a (QObject) property of type
3849 bool QQmlCompiler::canCoerce(int to, QQmlScript::Object *from)
3851 QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to);
3852 QQmlPropertyCache *fromMo = from->metatype;
3857 fromMo = fromMo->parent();
3863 Returns the element name, as written in the QML file, for o.
3865 QString QQmlCompiler::elementName(QQmlScript::Object *o)
3868 if (o->type != -1) {
3869 return unit->parser().referencedTypes().at(o->type)->name;
3875 QQmlType *QQmlCompiler::toQmlType(QQmlScript::Object *from)
3877 if (from->type != -1 && output->types.at(from->type).type)
3878 return output->types.at(from->type).type;
3880 const QMetaObject *mo = from->metatype->firstCppMetaObject();
3882 while (!type && mo) {
3883 type = QQmlMetaType::qmlType(mo);
3884 mo = mo->superClass();
3889 QStringList QQmlCompiler::deferredProperties(QQmlScript::Object *obj)
3891 const QMetaObject *mo = obj->metatype->firstCppMetaObject();
3893 int idx = mo->indexOfClassInfo("DeferredPropertyNames");
3895 return QStringList();
3897 QMetaClassInfo classInfo = mo->classInfo(idx);
3898 QStringList rv = QString::fromUtf8(classInfo.value()).split(QLatin1Char(','));
3903 QQmlCompiler::propertyCacheForObject(QQmlScript::Object *object)
3905 if (object->synthCache)
3906 return object->synthCache;
3907 else if (object->type != -1)
3908 return output->types[object->type].createPropertyCache(engine);
3910 return object->metatype;
3914 QQmlCompiler::property(QQmlScript::Object *object, int index)
3916 QQmlPropertyCache *cache = propertyCacheForObject(object);
3918 return cache->property(index);
3922 QQmlCompiler::property(QQmlScript::Object *object, const QHashedStringRef &name, bool *notInRevision)
3924 if (notInRevision) *notInRevision = false;
3926 QQmlPropertyCache *cache = propertyCacheForObject(object);
3928 QQmlPropertyData *d = cache->property(name, 0, 0);
3930 // Find the first property
3931 while (d && d->isFunction())
3932 d = cache->overrideData(d);
3934 if (d && !cache->isAllowedInRevision(d)) {
3935 if (notInRevision) *notInRevision = true;
3942 // This code must match the semantics of QQmlPropertyPrivate::findSignalByName
3944 QQmlCompiler::signal(QQmlScript::Object *object, const QHashedStringRef &name, bool *notInRevision)
3946 if (notInRevision) *notInRevision = false;
3948 QQmlPropertyCache *cache = propertyCacheForObject(object);
3951 QQmlPropertyData *d = cache->property(name, 0, 0);
3952 if (notInRevision) *notInRevision = false;
3954 while (d && !(d->isFunction()))
3955 d = cache->overrideData(d);
3957 if (d && !cache->isAllowedInRevision(d)) {
3958 if (notInRevision) *notInRevision = true;
3960 } else if (d && d->isSignal()) {
3964 if (name.endsWith(Changed_string)) {
3965 QHashedStringRef propName = name.mid(0, name.length() - Changed_string.length());
3967 d = property(object, propName, notInRevision);
3969 return cache->signal(d->notifyIndex);
3975 // This code must match the semantics of QQmlPropertyPrivate::findSignalByName
3976 int QQmlCompiler::indexOfSignal(QQmlScript::Object *object, const QString &name,
3977 bool *notInRevision)
3979 QQmlPropertyData *d = signal(object, QStringRef(&name), notInRevision);
3980 return d?d->coreIndex:-1;
3983 int QQmlCompiler::indexOfProperty(QQmlScript::Object *object, const QString &name,
3984 bool *notInRevision)
3986 return indexOfProperty(object, QStringRef(&name), notInRevision);
3989 int QQmlCompiler::indexOfProperty(QQmlScript::Object *object, const QHashedStringRef &name,
3990 bool *notInRevision)
3992 QQmlPropertyData *d = property(object, name, notInRevision);
3993 return d?d->coreIndex:-1;