source: trunk/src/declarative/qml/qdeclarativecompiler.cpp@ 900

Last change on this file since 900 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 107.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtDeclarative module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "private/qdeclarativecompiler_p.h"
43
44#include "private/qdeclarativeparser_p.h"
45#include "private/qdeclarativescriptparser_p.h"
46#include "qdeclarativepropertyvaluesource.h"
47#include "qdeclarativecomponent.h"
48#include "private/qmetaobjectbuilder_p.h"
49#include "private/qdeclarativestringconverters_p.h"
50#include "private/qdeclarativeengine_p.h"
51#include "qdeclarativeengine.h"
52#include "qdeclarativecontext.h"
53#include "private/qdeclarativemetatype_p.h"
54#include "private/qdeclarativecustomparser_p_p.h"
55#include "private/qdeclarativecontext_p.h"
56#include "private/qdeclarativecomponent_p.h"
57#include "parser/qdeclarativejsast_p.h"
58#include "private/qdeclarativevmemetaobject_p.h"
59#include "private/qdeclarativeexpression_p.h"
60#include "private/qdeclarativeproperty_p.h"
61#include "private/qdeclarativerewrite_p.h"
62#include "qdeclarativescriptstring.h"
63#include "private/qdeclarativeglobal_p.h"
64#include "private/qdeclarativescriptparser_p.h"
65#include "private/qdeclarativebinding_p.h"
66#include "private/qdeclarativecompiledbindings_p.h"
67#include "private/qdeclarativeglobalscriptclass_p.h"
68
69#include <QColor>
70#include <QDebug>
71#include <QPointF>
72#include <QSizeF>
73#include <QRectF>
74#include <QAtomicInt>
75#include <QtCore/qdebug.h>
76#include <QtCore/qdatetime.h>
77
78QT_BEGIN_NAMESPACE
79
80DEFINE_BOOL_CONFIG_OPTION(compilerDump, QML_COMPILER_DUMP);
81DEFINE_BOOL_CONFIG_OPTION(compilerStatDump, QML_COMPILER_STATS);
82DEFINE_BOOL_CONFIG_OPTION(bindingsDump, QML_BINDINGS_DUMP);
83
84using namespace QDeclarativeParser;
85
86/*!
87 Instantiate a new QDeclarativeCompiler.
88*/
89QDeclarativeCompiler::QDeclarativeCompiler()
90: output(0), engine(0), unitRoot(0), unit(0)
91{
92}
93
94/*!
95 Returns true if the last call to compile() caused errors.
96
97 \sa errors()
98*/
99bool QDeclarativeCompiler::isError() const
100{
101 return !exceptions.isEmpty();
102}
103
104/*!
105 Return the list of errors from the last call to compile(), or an empty list
106 if there were no errors.
107*/
108QList<QDeclarativeError> QDeclarativeCompiler::errors() const
109{
110 return exceptions;
111}
112
113/*!
114 Returns true if \a name refers to an attached property, false otherwise.
115
116 Attached property names are those that start with a capital letter.
117*/
118bool QDeclarativeCompiler::isAttachedPropertyName(const QByteArray &name)
119{
120 return !name.isEmpty() && name.at(0) >= 'A' && name.at(0) <= 'Z';
121}
122
123/*!
124 Returns true if \a name refers to a signal property, false otherwise.
125
126 Signal property names are those that start with "on", followed by a capital
127 letter.
128*/
129bool QDeclarativeCompiler::isSignalPropertyName(const QByteArray &name)
130{
131 return name.length() >= 3 && name.startsWith("on") &&
132 'A' <= name.at(2) && 'Z' >= name.at(2);
133}
134
135/*!
136 \macro COMPILE_EXCEPTION
137 \internal
138 Inserts an error into the QDeclarativeCompiler error list, and returns false
139 (failure).
140
141 \a token is used to source the error line and column, and \a desc is the
142 error itself. \a desc can be an expression that can be piped into QDebug.
143
144 For example:
145
146 \code
147 COMPILE_EXCEPTION(property, tr("Error for property \"%1\"").arg(QString::fromUtf8(property->name)));
148 \endcode
149*/
150#define COMPILE_EXCEPTION(token, desc) \
151 { \
152 QString exceptionDescription; \
153 QDeclarativeError error; \
154 error.setUrl(output->url); \
155 error.setLine((token)->location.start.line); \
156 error.setColumn((token)->location.start.column); \
157 error.setDescription(desc.trimmed()); \
158 exceptions << error; \
159 return false; \
160 }
161
162/*!
163 \macro COMPILE_CHECK
164 \internal
165 Returns false if \a is false, otherwise does nothing.
166*/
167#define COMPILE_CHECK(a) \
168 { \
169 if (!a) return false; \
170 }
171
172/*!
173 Returns true if literal \a v can be assigned to property \a prop, otherwise
174 false.
175
176 This test corresponds to action taken by genLiteralAssignment(). Any change
177 made here, must have a corresponding action in genLiteralAssigment().
178*/
179bool QDeclarativeCompiler::testLiteralAssignment(const QMetaProperty &prop,
180 QDeclarativeParser::Value *v)
181{
182 QString string = v->value.asString();
183
184 if (!prop.isWritable())
185 COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop.name())));
186
187 if (prop.isEnumType()) {
188 int value;
189 if (prop.isFlagType()) {
190 value = prop.enumerator().keysToValue(string.toUtf8().constData());
191 } else
192 value = prop.enumerator().keyToValue(string.toUtf8().constData());
193 if (value == -1)
194 COMPILE_EXCEPTION(v, tr("Invalid property assignment: unknown enumeration"));
195 return true;
196 }
197 int type = prop.userType();
198 switch(type) {
199 case -1:
200 break;
201 case QVariant::String:
202 if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: string expected"));
203 break;
204 case QVariant::Url:
205 if (!v->value.isString()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: url expected"));
206 break;
207 case QVariant::UInt:
208 {
209 bool ok = v->value.isNumber();
210 if (ok) {
211 double n = v->value.asNumber();
212 if (double(uint(n)) != n)
213 ok = false;
214 }
215 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: unsigned int expected"));
216 }
217 break;
218 case QVariant::Int:
219 {
220 bool ok = v->value.isNumber();
221 if (ok) {
222 double n = v->value.asNumber();
223 if (double(int(n)) != n)
224 ok = false;
225 }
226 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: int expected"));
227 }
228 break;
229 case QMetaType::Float:
230 if (!v->value.isNumber()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: number expected"));
231 break;
232 case QVariant::Double:
233 if (!v->value.isNumber()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: number expected"));
234 break;
235 case QVariant::Color:
236 {
237 bool ok;
238 QDeclarativeStringConverters::colorFromString(string, &ok);
239 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: color expected"));
240 }
241 break;
242#ifndef QT_NO_DATESTRING
243 case QVariant::Date:
244 {
245 bool ok;
246 QDeclarativeStringConverters::dateFromString(string, &ok);
247 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: date expected"));
248 }
249 break;
250 case QVariant::Time:
251 {
252 bool ok;
253 QDeclarativeStringConverters::timeFromString(string, &ok);
254 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: time expected"));
255 }
256 break;
257 case QVariant::DateTime:
258 {
259 bool ok;
260 QDeclarativeStringConverters::dateTimeFromString(string, &ok);
261 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: datetime expected"));
262 }
263 break;
264#endif // QT_NO_DATESTRING
265 case QVariant::Point:
266 case QVariant::PointF:
267 {
268 bool ok;
269 QPointF point = QDeclarativeStringConverters::pointFFromString(string, &ok);
270 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: point expected"));
271 }
272 break;
273 case QVariant::Size:
274 case QVariant::SizeF:
275 {
276 bool ok;
277 QSizeF size = QDeclarativeStringConverters::sizeFFromString(string, &ok);
278 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: size expected"));
279 }
280 break;
281 case QVariant::Rect:
282 case QVariant::RectF:
283 {
284 bool ok;
285 QRectF rect = QDeclarativeStringConverters::rectFFromString(string, &ok);
286 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: rect expected"));
287 }
288 break;
289 case QVariant::Bool:
290 {
291 if (!v->value.isBoolean()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: boolean expected"));
292 }
293 break;
294 case QVariant::Vector3D:
295 {
296 bool ok;
297 QDeclarativeStringConverters::vector3DFromString(string, &ok);
298 if (!ok) COMPILE_EXCEPTION(v, tr("Invalid property assignment: 3D vector expected"));
299 }
300 break;
301 default:
302 {
303 int t = prop.userType();
304 QDeclarativeMetaType::StringConverter converter =
305 QDeclarativeMetaType::customStringConverter(t);
306 if (!converter)
307 COMPILE_EXCEPTION(v, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QVariant::typeToName(prop.type()))));
308 }
309 break;
310 }
311 return true;
312}
313
314/*!
315 Generate a store instruction for assigning literal \a v to property \a prop.
316
317 Any literal assignment that is approved in testLiteralAssignment() must have
318 a corresponding action in this method.
319*/
320void QDeclarativeCompiler::genLiteralAssignment(const QMetaProperty &prop,
321 QDeclarativeParser::Value *v)
322{
323 QString string = v->value.asString();
324
325 QDeclarativeInstruction instr;
326 instr.line = v->location.start.line;
327 if (prop.isEnumType()) {
328 int value;
329 if (prop.isFlagType()) {
330 value = prop.enumerator().keysToValue(string.toUtf8().constData());
331 } else
332 value = prop.enumerator().keyToValue(string.toUtf8().constData());
333
334 instr.type = QDeclarativeInstruction::StoreInteger;
335 instr.storeInteger.propertyIndex = prop.propertyIndex();
336 instr.storeInteger.value = value;
337 output->bytecode << instr;
338 return;
339 }
340
341 int type = prop.userType();
342 switch(type) {
343 case -1:
344 {
345 if (v->value.isNumber()) {
346 double n = v->value.asNumber();
347 if (double(int(n)) == n) {
348 instr.type = QDeclarativeInstruction::StoreVariantInteger;
349 instr.storeInteger.propertyIndex = prop.propertyIndex();
350 instr.storeInteger.value = int(n);
351 } else {
352 instr.type = QDeclarativeInstruction::StoreVariantDouble;
353 instr.storeDouble.propertyIndex = prop.propertyIndex();
354 instr.storeDouble.value = n;
355 }
356 } else if(v->value.isBoolean()) {
357 instr.type = QDeclarativeInstruction::StoreVariantBool;
358 instr.storeBool.propertyIndex = prop.propertyIndex();
359 instr.storeBool.value = v->value.asBoolean();
360 } else {
361 instr.type = QDeclarativeInstruction::StoreVariant;
362 instr.storeString.propertyIndex = prop.propertyIndex();
363 instr.storeString.value = output->indexForString(string);
364 }
365 }
366 break;
367 case QVariant::String:
368 {
369 instr.type = QDeclarativeInstruction::StoreString;
370 instr.storeString.propertyIndex = prop.propertyIndex();
371 instr.storeString.value = output->indexForString(string);
372 }
373 break;
374 case QVariant::Url:
375 {
376 instr.type = QDeclarativeInstruction::StoreUrl;
377 QUrl u = string.isEmpty() ? QUrl() : output->url.resolved(QUrl(string));
378 instr.storeUrl.propertyIndex = prop.propertyIndex();
379 instr.storeUrl.value = output->indexForUrl(u);
380 }
381 break;
382 case QVariant::UInt:
383 {
384 instr.type = QDeclarativeInstruction::StoreInteger;
385 instr.storeInteger.propertyIndex = prop.propertyIndex();
386 instr.storeInteger.value = uint(v->value.asNumber());
387 }
388 break;
389 case QVariant::Int:
390 {
391 instr.type = QDeclarativeInstruction::StoreInteger;
392 instr.storeInteger.propertyIndex = prop.propertyIndex();
393 instr.storeInteger.value = int(v->value.asNumber());
394 }
395 break;
396 case QMetaType::Float:
397 {
398 instr.type = QDeclarativeInstruction::StoreFloat;
399 instr.storeFloat.propertyIndex = prop.propertyIndex();
400 instr.storeFloat.value = float(v->value.asNumber());
401 }
402 break;
403 case QVariant::Double:
404 {
405 instr.type = QDeclarativeInstruction::StoreDouble;
406 instr.storeDouble.propertyIndex = prop.propertyIndex();
407 instr.storeDouble.value = v->value.asNumber();
408 }
409 break;
410 case QVariant::Color:
411 {
412 QColor c = QDeclarativeStringConverters::colorFromString(string);
413 instr.type = QDeclarativeInstruction::StoreColor;
414 instr.storeColor.propertyIndex = prop.propertyIndex();
415 instr.storeColor.value = c.rgba();
416 }
417 break;
418#ifndef QT_NO_DATESTRING
419 case QVariant::Date:
420 {
421 QDate d = QDeclarativeStringConverters::dateFromString(string);
422 instr.type = QDeclarativeInstruction::StoreDate;
423 instr.storeDate.propertyIndex = prop.propertyIndex();
424 instr.storeDate.value = d.toJulianDay();
425 }
426 break;
427 case QVariant::Time:
428 {
429 QTime time = QDeclarativeStringConverters::timeFromString(string);
430 int data[] = { time.hour(), time.minute(),
431 time.second(), time.msec() };
432 int index = output->indexForInt(data, 4);
433 instr.type = QDeclarativeInstruction::StoreTime;
434 instr.storeTime.propertyIndex = prop.propertyIndex();
435 instr.storeTime.valueIndex = index;
436 }
437 break;
438 case QVariant::DateTime:
439 {
440 QDateTime dateTime = QDeclarativeStringConverters::dateTimeFromString(string);
441 int data[] = { dateTime.date().toJulianDay(),
442 dateTime.time().hour(),
443 dateTime.time().minute(),
444 dateTime.time().second(),
445 dateTime.time().msec() };
446 int index = output->indexForInt(data, 5);
447 instr.type = QDeclarativeInstruction::StoreDateTime;
448 instr.storeDateTime.propertyIndex = prop.propertyIndex();
449 instr.storeDateTime.valueIndex = index;
450 }
451 break;
452#endif // QT_NO_DATESTRING
453 case QVariant::Point:
454 case QVariant::PointF:
455 {
456 bool ok;
457 QPointF point =
458 QDeclarativeStringConverters::pointFFromString(string, &ok);
459 float data[] = { float(point.x()), float(point.y()) };
460 int index = output->indexForFloat(data, 2);
461 if (type == QVariant::PointF)
462 instr.type = QDeclarativeInstruction::StorePointF;
463 else
464 instr.type = QDeclarativeInstruction::StorePoint;
465 instr.storeRealPair.propertyIndex = prop.propertyIndex();
466 instr.storeRealPair.valueIndex = index;
467 }
468 break;
469 case QVariant::Size:
470 case QVariant::SizeF:
471 {
472 bool ok;
473 QSizeF size = QDeclarativeStringConverters::sizeFFromString(string, &ok);
474 float data[] = { float(size.width()), float(size.height()) };
475 int index = output->indexForFloat(data, 2);
476 if (type == QVariant::SizeF)
477 instr.type = QDeclarativeInstruction::StoreSizeF;
478 else
479 instr.type = QDeclarativeInstruction::StoreSize;
480 instr.storeRealPair.propertyIndex = prop.propertyIndex();
481 instr.storeRealPair.valueIndex = index;
482 }
483 break;
484 case QVariant::Rect:
485 case QVariant::RectF:
486 {
487 bool ok;
488 QRectF rect = QDeclarativeStringConverters::rectFFromString(string, &ok);
489 float data[] = { float(rect.x()), float(rect.y()),
490 float(rect.width()), float(rect.height()) };
491 int index = output->indexForFloat(data, 4);
492 if (type == QVariant::RectF)
493 instr.type = QDeclarativeInstruction::StoreRectF;
494 else
495 instr.type = QDeclarativeInstruction::StoreRect;
496 instr.storeRect.propertyIndex = prop.propertyIndex();
497 instr.storeRect.valueIndex = index;
498 }
499 break;
500 case QVariant::Bool:
501 {
502 bool b = v->value.asBoolean();
503 instr.type = QDeclarativeInstruction::StoreBool;
504 instr.storeBool.propertyIndex = prop.propertyIndex();
505 instr.storeBool.value = b;
506 }
507 break;
508 case QVariant::Vector3D:
509 {
510 bool ok;
511 QVector3D vector =
512 QDeclarativeStringConverters::vector3DFromString(string, &ok);
513 float data[] = { float(vector.x()), float(vector.y()), float(vector.z()) };
514 int index = output->indexForFloat(data, 3);
515 instr.type = QDeclarativeInstruction::StoreVector3D;
516 instr.storeRealPair.propertyIndex = prop.propertyIndex();
517 instr.storeRealPair.valueIndex = index;
518 }
519 break;
520 default:
521 {
522 int t = prop.userType();
523 int index = output->customTypeData.count();
524 instr.type = QDeclarativeInstruction::AssignCustomType;
525 instr.assignCustomType.propertyIndex = prop.propertyIndex();
526 instr.assignCustomType.valueIndex = index;
527
528 QDeclarativeCompiledData::CustomTypeData data;
529 data.index = output->indexForString(string);
530 data.type = t;
531 output->customTypeData << data;
532 }
533 break;
534 }
535 output->bytecode << instr;
536}
537
538/*!
539 Resets data by clearing the lists that the QDeclarativeCompiler modifies.
540*/
541void QDeclarativeCompiler::reset(QDeclarativeCompiledData *data)
542{
543 data->types.clear();
544 data->primitives.clear();
545 data->floatData.clear();
546 data->intData.clear();
547 data->customTypeData.clear();
548 data->datas.clear();
549 data->bytecode.clear();
550}
551
552/*!
553 Compile \a unit, and store the output in \a out. \a engine is the QDeclarativeEngine
554 with which the QDeclarativeCompiledData will be associated.
555
556 Returns true on success, false on failure. On failure, the compile errors
557 are available from errors().
558
559 If the environment variant QML_COMPILER_DUMP is set
560 (eg. QML_COMPILER_DUMP=1) the compiled instructions will be dumped to stderr
561 on a successful compiler.
562*/
563bool QDeclarativeCompiler::compile(QDeclarativeEngine *engine,
564 QDeclarativeTypeData *unit,
565 QDeclarativeCompiledData *out)
566{
567 exceptions.clear();
568
569 Q_ASSERT(out);
570 reset(out);
571
572 output = out;
573
574 // Compile types
575 const QList<QDeclarativeTypeData::TypeReference> &resolvedTypes = unit->resolvedTypes();
576 QList<QDeclarativeScriptParser::TypeReference *> referencedTypes = unit->parser().referencedTypes();
577
578 for (int ii = 0; ii < resolvedTypes.count(); ++ii) {
579 QDeclarativeCompiledData::TypeReference ref;
580
581 const QDeclarativeTypeData::TypeReference &tref = resolvedTypes.at(ii);
582 QDeclarativeScriptParser::TypeReference *parserRef = referencedTypes.at(ii);
583
584 if (tref.type) {
585 ref.type = tref.type;
586 if (!ref.type->isCreatable()) {
587 QString err = ref.type->noCreationReason();
588 if (err.isEmpty())
589 err = tr( "Element is not creatable.");
590 COMPILE_EXCEPTION(parserRef->refObjects.first(), err);
591 }
592 } else if (tref.typeData) {
593 ref.component = tref.typeData->compiledData();
594 }
595 ref.className = parserRef->name.toUtf8();
596 out->types << ref;
597 }
598
599 Object *root = unit->parser().tree();
600 Q_ASSERT(root);
601
602 this->engine = engine;
603 this->enginePrivate = QDeclarativeEnginePrivate::get(engine);
604 this->unit = unit;
605 this->unitRoot = root;
606 compileTree(root);
607
608 if (!isError()) {
609 if (compilerDump())
610 out->dumpInstructions();
611 if (compilerStatDump())
612 dumpStats();
613 } else {
614 reset(out);
615 }
616
617 compileState = ComponentCompileState();
618 savedCompileStates.clear();
619 output = 0;
620 this->engine = 0;
621 this->enginePrivate = 0;
622 this->unit = 0;
623 this->unitRoot = 0;
624
625 return !isError();
626}
627
628void QDeclarativeCompiler::compileTree(Object *tree)
629{
630 compileState.root = tree;
631 componentStat.lineNumber = tree->location.start.line;
632
633 if (!buildObject(tree, BindingContext()) || !completeComponentBuild())
634 return;
635
636 QDeclarativeInstruction init;
637 init.type = QDeclarativeInstruction::Init;
638 init.line = 0;
639 init.init.bindingsSize = compileState.bindings.count();
640 init.init.parserStatusSize = compileState.parserStatusCount;
641 init.init.contextCache = genContextCache();
642 if (compileState.compiledBindingData.isEmpty())
643 init.init.compiledBinding = -1;
644 else
645 init.init.compiledBinding = output->indexForByteArray(compileState.compiledBindingData);
646 output->bytecode << init;
647
648 // Build global import scripts
649 QHash<QString, Object::ScriptBlock> importedScripts;
650 QStringList importedScriptIndexes;
651
652 foreach (const QDeclarativeTypeData::ScriptReference &script, unit->resolvedScripts()) {
653 QString scriptCode = script.script->scriptSource();
654 Object::ScriptBlock::Pragmas pragmas = script.script->pragmas();
655
656 Q_ASSERT(!importedScripts.contains(script.qualifier));
657
658 if (!scriptCode.isEmpty()) {
659 Object::ScriptBlock &scriptBlock = importedScripts[script.qualifier];
660
661 scriptBlock.code = scriptCode;
662 scriptBlock.file = script.script->finalUrl().toString();
663 scriptBlock.pragmas = pragmas;
664 }
665 }
666
667 for (QHash<QString, Object::ScriptBlock>::Iterator iter = importedScripts.begin();
668 iter != importedScripts.end(); ++iter) {
669
670 importedScriptIndexes.append(iter.key());
671
672 QDeclarativeInstruction import;
673 import.type = QDeclarativeInstruction::StoreImportedScript;
674 import.line = 0;
675 import.storeScript.value = output->scripts.count();
676 output->scripts << *iter;
677 output->bytecode << import;
678 }
679
680 genObject(tree);
681
682 QDeclarativeInstruction def;
683 init.line = 0;
684 def.type = QDeclarativeInstruction::SetDefault;
685 output->bytecode << def;
686
687 output->importCache = new QDeclarativeTypeNameCache(engine);
688
689 for (int ii = 0; ii < importedScriptIndexes.count(); ++ii)
690 output->importCache->add(importedScriptIndexes.at(ii), ii);
691
692 unit->imports().populateCache(output->importCache, engine);
693
694 Q_ASSERT(tree->metatype);
695
696 if (tree->metadata.isEmpty()) {
697 output->root = tree->metatype;
698 } else {
699 static_cast<QMetaObject &>(output->rootData) = *tree->metaObject();
700 output->root = &output->rootData;
701 }
702 if (!tree->metadata.isEmpty())
703 enginePrivate->registerCompositeType(output);
704}
705
706static bool ValuePtrLessThan(const Value *t1, const Value *t2)
707{
708 return t1->location.start.line < t2->location.start.line ||
709 (t1->location.start.line == t2->location.start.line &&
710 t1->location.start.column < t2->location.start.column);
711}
712
713bool QDeclarativeCompiler::buildObject(Object *obj, const BindingContext &ctxt)
714{
715 componentStat.objects++;
716
717 Q_ASSERT (obj->type != -1);
718 const QDeclarativeCompiledData::TypeReference &tr =
719 output->types.at(obj->type);
720 obj->metatype = tr.metaObject();
721
722 if (tr.component)
723 obj->url = tr.component->url;
724 if (tr.type)
725 obj->typeName = tr.type->qmlTypeName();
726 obj->className = tr.className;
727
728 // This object is a "Component" element
729 if (tr.type && obj->metatype == &QDeclarativeComponent::staticMetaObject) {
730 COMPILE_CHECK(buildComponent(obj, ctxt));
731 return true;
732 }
733
734 // Object instantiations reset the binding context
735 BindingContext objCtxt(obj);
736
737 // Create the synthesized meta object, ignoring aliases
738 COMPILE_CHECK(checkDynamicMeta(obj));
739 COMPILE_CHECK(mergeDynamicMetaProperties(obj));
740 COMPILE_CHECK(buildDynamicMeta(obj, IgnoreAliases));
741
742 // Find the native type and check for the QDeclarativeParserStatus interface
743 QDeclarativeType *type = toQmlType(obj);
744 Q_ASSERT(type);
745 obj->parserStatusCast = type->parserStatusCast();
746 if (obj->parserStatusCast != -1)
747 compileState.parserStatusCount++;
748
749 // Check if this is a custom parser type. Custom parser types allow
750 // assignments to non-existent properties. These assignments are then
751 // compiled by the type.
752 bool isCustomParser = output->types.at(obj->type).type &&
753 output->types.at(obj->type).type->customParser() != 0;
754 QList<QDeclarativeCustomParserProperty> customProps;
755
756 // Fetch the list of deferred properties
757 QStringList deferredList = deferredProperties(obj);
758
759 // Must do id property first. This is to ensure that the id given to any
760 // id reference created matches the order in which the objects are
761 // instantiated
762 foreach(Property *prop, obj->properties) {
763 if (prop->name == "id") {
764 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
765 break;
766 }
767 }
768
769 // Merge
770 Property *defaultProperty = 0;
771 Property *skipProperty = 0;
772 if (obj->defaultProperty) {
773 const QMetaObject *metaObject = obj->metaObject();
774 Q_ASSERT(metaObject);
775 QMetaProperty p = QDeclarativeMetaType::defaultProperty(metaObject);
776 if (p.name()) {
777 Property *explicitProperty = obj->getProperty(p.name(), false);
778 if (explicitProperty && !explicitProperty->value) {
779 skipProperty = explicitProperty;
780
781 defaultProperty = new Property;
782 defaultProperty->parent = obj;
783 defaultProperty->isDefault = true;
784 defaultProperty->location = obj->defaultProperty->location;
785 defaultProperty->listValueRange = obj->defaultProperty->listValueRange;
786 defaultProperty->listCommaPositions = obj->defaultProperty->listCommaPositions;
787
788 defaultProperty->values = obj->defaultProperty->values;
789 defaultProperty->values += explicitProperty->values;
790 foreach(Value *value, defaultProperty->values)
791 value->addref();
792 qSort(defaultProperty->values.begin(), defaultProperty->values.end(), ValuePtrLessThan);
793
794 } else {
795 defaultProperty = obj->defaultProperty;
796 defaultProperty->addref();
797 }
798 } else {
799 defaultProperty = obj->defaultProperty;
800 defaultProperty->addref();
801 }
802 }
803
804 QDeclarativeCustomParser *cp = 0;
805 if (isCustomParser)
806 cp = output->types.at(obj->type).type->customParser();
807
808 // Build all explicit properties specified
809 foreach(Property *prop, obj->properties) {
810
811 if (prop == skipProperty)
812 continue;
813 if (prop->name == "id")
814 continue;
815
816 bool canDefer = false;
817 if (isCustomParser) {
818 if (doesPropertyExist(prop, obj) &&
819 (!(cp->flags() & QDeclarativeCustomParser::AcceptsAttachedProperties) ||
820 !isAttachedPropertyName(prop->name))) {
821 int ids = compileState.ids.count();
822 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
823 canDefer = ids == compileState.ids.count();
824 } else {
825 customProps << QDeclarativeCustomParserNodePrivate::fromProperty(prop);
826 }
827 } else {
828 if (isSignalPropertyName(prop->name)) {
829 COMPILE_CHECK(buildSignal(prop,obj,objCtxt));
830 } else {
831 int ids = compileState.ids.count();
832 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
833 canDefer = ids == compileState.ids.count();
834 }
835 }
836
837 if (canDefer && !deferredList.isEmpty() &&
838 deferredList.contains(QString::fromUtf8(prop->name)))
839 prop->isDeferred = true;
840
841 }
842
843 // Build the default property
844 if (defaultProperty) {
845 Property *prop = defaultProperty;
846
847 bool canDefer = false;
848 if (isCustomParser) {
849 if (doesPropertyExist(prop, obj)) {
850 int ids = compileState.ids.count();
851 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
852 canDefer = ids == compileState.ids.count();
853 } else {
854 customProps << QDeclarativeCustomParserNodePrivate::fromProperty(prop);
855 }
856 } else {
857 int ids = compileState.ids.count();
858 COMPILE_CHECK(buildProperty(prop, obj, objCtxt));
859 canDefer = ids == compileState.ids.count();
860 }
861
862 if (canDefer && !deferredList.isEmpty() &&
863 deferredList.contains(QString::fromUtf8(prop->name)))
864 prop->isDeferred = true;
865 }
866
867 if (defaultProperty)
868 defaultProperty->release();
869
870 // Compile custom parser parts
871 if (isCustomParser && !customProps.isEmpty()) {
872 cp->clearErrors();
873 cp->compiler = this;
874 cp->object = obj;
875 obj->custom = cp->compile(customProps);
876 cp->compiler = 0;
877 cp->object = 0;
878 foreach (QDeclarativeError err, cp->errors()) {
879 err.setUrl(output->url);
880 exceptions << err;
881 }
882 }
883
884 return true;
885}
886
887void QDeclarativeCompiler::genObject(QDeclarativeParser::Object *obj)
888{
889 const QDeclarativeCompiledData::TypeReference &tr =
890 output->types.at(obj->type);
891 if (tr.type && obj->metatype == &QDeclarativeComponent::staticMetaObject) {
892 genComponent(obj);
893 return;
894 }
895
896 // Create the object
897 if (obj->custom.isEmpty() && output->types.at(obj->type).type &&
898 !output->types.at(obj->type).type->isExtendedType() && obj != compileState.root) {
899
900 QDeclarativeInstruction create;
901 create.type = QDeclarativeInstruction::CreateSimpleObject;
902 create.line = obj->location.start.line;
903 create.createSimple.create = output->types.at(obj->type).type->createFunction();
904 create.createSimple.typeSize = output->types.at(obj->type).type->createSize();
905 create.createSimple.column = obj->location.start.column;
906 output->bytecode << create;
907
908 } else {
909
910 QDeclarativeInstruction create;
911 create.type = QDeclarativeInstruction::CreateObject;
912 create.line = obj->location.start.line;
913 create.create.column = obj->location.start.column;
914 create.create.data = -1;
915 if (!obj->custom.isEmpty())
916 create.create.data = output->indexForByteArray(obj->custom);
917 create.create.type = obj->type;
918 if (!output->types.at(create.create.type).type &&
919 !obj->bindingBitmask.isEmpty()) {
920 Q_ASSERT(obj->bindingBitmask.size() % 4 == 0);
921 create.create.bindingBits =
922 output->indexForByteArray(obj->bindingBitmask);
923 } else {
924 create.create.bindingBits = -1;
925 }
926 output->bytecode << create;
927
928 }
929
930 // Setup the synthesized meta object if necessary
931 if (!obj->metadata.isEmpty()) {
932 QDeclarativeInstruction meta;
933 meta.type = QDeclarativeInstruction::StoreMetaObject;
934 meta.line = 0;
935 meta.storeMeta.data = output->indexForByteArray(obj->metadata);
936 meta.storeMeta.aliasData = output->indexForByteArray(obj->synthdata);
937 meta.storeMeta.propertyCache = output->propertyCaches.count();
938 // ### Surely the creation of this property cache could be more efficient
939 QDeclarativePropertyCache *propertyCache = 0;
940 if (tr.component)
941 propertyCache = tr.component->rootPropertyCache->copy();
942 else
943 propertyCache = enginePrivate->cache(obj->metaObject()->superClass())->copy();
944
945 propertyCache->append(engine, obj->metaObject(), QDeclarativePropertyCache::Data::NoFlags,
946 QDeclarativePropertyCache::Data::IsVMEFunction,
947 QDeclarativePropertyCache::Data::IsVMESignal);
948
949 // Add flag for alias properties
950 if (!obj->synthdata.isEmpty()) {
951 const QDeclarativeVMEMetaData *vmeMetaData =
952 reinterpret_cast<const QDeclarativeVMEMetaData *>(obj->synthdata.constData());
953 for (int ii = 0; ii < vmeMetaData->aliasCount; ++ii) {
954 int index = obj->metaObject()->propertyOffset() + vmeMetaData->propertyCount + ii;
955 propertyCache->property(index)->flags |= QDeclarativePropertyCache::Data::IsAlias;
956 }
957 }
958
959 if (obj == unitRoot) {
960 propertyCache->addref();
961 output->rootPropertyCache = propertyCache;
962 }
963
964 output->propertyCaches << propertyCache;
965 output->bytecode << meta;
966 } else if (obj == unitRoot) {
967 if (tr.component)
968 output->rootPropertyCache = tr.component->rootPropertyCache;
969 else
970 output->rootPropertyCache = enginePrivate->cache(obj->metaObject());
971
972 output->rootPropertyCache->addref();
973 }
974
975 // Set the object id
976 if (!obj->id.isEmpty()) {
977 QDeclarativeInstruction id;
978 id.type = QDeclarativeInstruction::SetId;
979 id.line = 0;
980 id.setId.value = output->indexForString(obj->id);
981 id.setId.index = obj->idIndex;
982 output->bytecode << id;
983 }
984
985 // Begin the class
986 if (tr.type && obj->parserStatusCast != -1) {
987 QDeclarativeInstruction begin;
988 begin.type = QDeclarativeInstruction::BeginObject;
989 begin.begin.castValue = obj->parserStatusCast;
990 begin.line = obj->location.start.line;
991 output->bytecode << begin;
992 }
993
994 genObjectBody(obj);
995}
996
997void QDeclarativeCompiler::genObjectBody(QDeclarativeParser::Object *obj)
998{
999 typedef QPair<Property *, int> PropPair;
1000 foreach(const PropPair &prop, obj->scriptStringProperties) {
1001 QDeclarativeInstruction ss;
1002 ss.type = QDeclarativeInstruction::StoreScriptString;
1003 ss.storeScriptString.propertyIndex = prop.first->index;
1004 ss.storeScriptString.value =
1005 output->indexForString(prop.first->values.at(0)->value.asScript());
1006 ss.storeScriptString.scope = prop.second;
1007 output->bytecode << ss;
1008 }
1009
1010 bool seenDefer = false;
1011 foreach(Property *prop, obj->valueProperties) {
1012 if (prop->isDeferred) {
1013 seenDefer = true;
1014 continue;
1015 }
1016 if (!prop->isAlias)
1017 genValueProperty(prop, obj);
1018 }
1019 if (seenDefer) {
1020 QDeclarativeInstruction defer;
1021 defer.type = QDeclarativeInstruction::Defer;
1022 defer.line = 0;
1023 defer.defer.deferCount = 0;
1024 int deferIdx = output->bytecode.count();
1025 output->bytecode << defer;
1026
1027 QDeclarativeInstruction init;
1028 init.type = QDeclarativeInstruction::Init;
1029 init.init.bindingsSize = compileState.bindings.count(); // XXX - bigger than necessary
1030 init.init.parserStatusSize = compileState.parserStatusCount; // XXX - bigger than necessary
1031 init.init.contextCache = -1;
1032 init.init.compiledBinding = -1;
1033 output->bytecode << init;
1034
1035 foreach(Property *prop, obj->valueProperties) {
1036 if (!prop->isDeferred)
1037 continue;
1038 genValueProperty(prop, obj);
1039 }
1040
1041 output->bytecode[deferIdx].defer.deferCount =
1042 output->bytecode.count() - deferIdx - 1;
1043 }
1044
1045 foreach(Property *prop, obj->signalProperties) {
1046
1047 QDeclarativeParser::Value *v = prop->values.at(0);
1048
1049 if (v->type == Value::SignalObject) {
1050
1051 genObject(v->object);
1052
1053 QDeclarativeInstruction assign;
1054 assign.type = QDeclarativeInstruction::AssignSignalObject;
1055 assign.line = v->location.start.line;
1056 assign.assignSignalObject.signal =
1057 output->indexForByteArray(prop->name);
1058 output->bytecode << assign;
1059
1060 } else if (v->type == Value::SignalExpression) {
1061
1062 BindingContext ctxt = compileState.signalExpressions.value(v);
1063
1064 QDeclarativeInstruction store;
1065 store.type = QDeclarativeInstruction::StoreSignal;
1066 store.line = v->location.start.line;
1067 store.storeSignal.signalIndex = prop->index;
1068 store.storeSignal.value =
1069 output->indexForString(v->value.asScript().trimmed());
1070 store.storeSignal.context = ctxt.stack;
1071 store.storeSignal.name = output->indexForByteArray(prop->name);
1072 output->bytecode << store;
1073
1074 }
1075
1076 }
1077
1078 foreach(Property *prop, obj->attachedProperties) {
1079 QDeclarativeInstruction fetch;
1080 fetch.type = QDeclarativeInstruction::FetchAttached;
1081 fetch.line = prop->location.start.line;
1082 fetch.fetchAttached.id = prop->index;
1083 output->bytecode << fetch;
1084
1085 genObjectBody(prop->value);
1086
1087 QDeclarativeInstruction pop;
1088 pop.type = QDeclarativeInstruction::PopFetchedObject;
1089 pop.line = prop->location.start.line;
1090 output->bytecode << pop;
1091 }
1092
1093 foreach(Property *prop, obj->groupedProperties) {
1094 QDeclarativeInstruction fetch;
1095 fetch.type = QDeclarativeInstruction::FetchObject;
1096 fetch.fetch.property = prop->index;
1097 fetch.line = prop->location.start.line;
1098 output->bytecode << fetch;
1099
1100 if (!prop->value->metadata.isEmpty()) {
1101 QDeclarativeInstruction meta;
1102 meta.type = QDeclarativeInstruction::StoreMetaObject;
1103 meta.line = 0;
1104 meta.storeMeta.data = output->indexForByteArray(prop->value->metadata);
1105 meta.storeMeta.aliasData = output->indexForByteArray(prop->value->synthdata);
1106 meta.storeMeta.propertyCache = output->propertyCaches.count();
1107 // ### Surely the creation of this property cache could be more efficient
1108 QDeclarativePropertyCache *propertyCache =
1109 enginePrivate->cache(prop->value->metaObject()->superClass())->copy();
1110 propertyCache->append(engine, prop->value->metaObject(), QDeclarativePropertyCache::Data::NoFlags,
1111 QDeclarativePropertyCache::Data::IsVMEFunction,
1112 QDeclarativePropertyCache::Data::IsVMESignal);
1113
1114 output->propertyCaches << propertyCache;
1115 output->bytecode << meta;
1116 }
1117
1118 genObjectBody(prop->value);
1119
1120 QDeclarativeInstruction pop;
1121 pop.type = QDeclarativeInstruction::PopFetchedObject;
1122 pop.line = prop->location.start.line;
1123 output->bytecode << pop;
1124 }
1125
1126 foreach(Property *prop, obj->valueTypeProperties) {
1127 if (!prop->isAlias)
1128 genValueTypeProperty(obj, prop);
1129 }
1130
1131 foreach(Property *prop, obj->valueProperties) {
1132 if (prop->isDeferred)
1133 continue;
1134 if (prop->isAlias)
1135 genValueProperty(prop, obj);
1136 }
1137
1138 foreach(Property *prop, obj->valueTypeProperties) {
1139 if (prop->isAlias)
1140 genValueTypeProperty(obj, prop);
1141 }
1142}
1143
1144void QDeclarativeCompiler::genValueTypeProperty(QDeclarativeParser::Object *obj,QDeclarativeParser::Property *prop)
1145{
1146 QDeclarativeInstruction fetch;
1147 fetch.type = QDeclarativeInstruction::FetchValueType;
1148 fetch.fetchValue.property = prop->index;
1149 fetch.fetchValue.type = prop->type;
1150 fetch.fetchValue.bindingSkipList = 0;
1151 fetch.line = prop->location.start.line;
1152
1153 if (obj->type == -1 || output->types.at(obj->type).component) {
1154 // We only have to do this if this is a composite type. If it is a builtin
1155 // type it can't possibly already have bindings that need to be cleared.
1156 foreach(Property *vprop, prop->value->valueProperties) {
1157 if (!vprop->values.isEmpty()) {
1158 Q_ASSERT(vprop->index >= 0 && vprop->index < 32);
1159 fetch.fetchValue.bindingSkipList |= (1 << vprop->index);
1160 }
1161 }
1162 }
1163
1164 output->bytecode << fetch;
1165
1166 foreach(Property *vprop, prop->value->valueProperties) {
1167 genPropertyAssignment(vprop, prop->value, prop);
1168 }
1169
1170 QDeclarativeInstruction pop;
1171 pop.type = QDeclarativeInstruction::PopValueType;
1172 pop.fetchValue.property = prop->index;
1173 pop.fetchValue.type = prop->type;
1174 pop.fetchValue.bindingSkipList = 0;
1175 pop.line = prop->location.start.line;
1176 output->bytecode << pop;
1177}
1178
1179void QDeclarativeCompiler::genComponent(QDeclarativeParser::Object *obj)
1180{
1181 QDeclarativeParser::Object *root = obj->defaultProperty->values.at(0)->object;
1182 Q_ASSERT(root);
1183
1184 QDeclarativeInstruction create;
1185 create.type = QDeclarativeInstruction::CreateComponent;
1186 create.line = root->location.start.line;
1187 create.createComponent.column = root->location.start.column;
1188 create.createComponent.endLine = root->location.end.line;
1189 output->bytecode << create;
1190 int count = output->bytecode.count();
1191
1192 ComponentCompileState oldCompileState = compileState;
1193 compileState = componentState(root);
1194
1195 QDeclarativeInstruction init;
1196 init.type = QDeclarativeInstruction::Init;
1197 init.init.bindingsSize = compileState.bindings.count();
1198 init.init.parserStatusSize = compileState.parserStatusCount;
1199 init.init.contextCache = genContextCache();
1200 if (compileState.compiledBindingData.isEmpty())
1201 init.init.compiledBinding = -1;
1202 else
1203 init.init.compiledBinding = output->indexForByteArray(compileState.compiledBindingData);
1204 init.line = obj->location.start.line;
1205 output->bytecode << init;
1206
1207 genObject(root);
1208
1209 QDeclarativeInstruction def;
1210 init.line = 0;
1211 def.type = QDeclarativeInstruction::SetDefault;
1212 output->bytecode << def;
1213
1214 output->bytecode[count - 1].createComponent.count =
1215 output->bytecode.count() - count;
1216
1217 compileState = oldCompileState;
1218
1219 if (!obj->id.isEmpty()) {
1220 QDeclarativeInstruction id;
1221 id.type = QDeclarativeInstruction::SetId;
1222 id.line = 0;
1223 id.setId.value = output->indexForString(obj->id);
1224 id.setId.index = obj->idIndex;
1225 output->bytecode << id;
1226 }
1227}
1228
1229bool QDeclarativeCompiler::buildComponent(QDeclarativeParser::Object *obj,
1230 const BindingContext &ctxt)
1231{
1232 // The special "Component" element can only have the id property and a
1233 // default property, that actually defines the component's tree
1234
1235 // Find, check and set the "id" property (if any)
1236 Property *idProp = 0;
1237 if (obj->properties.count() > 1 ||
1238 (obj->properties.count() == 1 && obj->properties.begin().key() != "id"))
1239 COMPILE_EXCEPTION(*obj->properties.begin(), tr("Component elements may not contain properties other than id"));
1240
1241 if (obj->properties.count())
1242 idProp = *obj->properties.begin();
1243
1244 if (idProp) {
1245 if (idProp->value || idProp->values.count() > 1 || idProp->values.at(0)->object)
1246 COMPILE_EXCEPTION(idProp, tr("Invalid component id specification"));
1247 COMPILE_CHECK(checkValidId(idProp->values.first(), idProp->values.first()->primitive()))
1248
1249 QString idVal = idProp->values.first()->primitive();
1250
1251 if (compileState.ids.contains(idVal))
1252 COMPILE_EXCEPTION(idProp, tr("id is not unique"));
1253
1254 obj->id = idVal;
1255 addId(idVal, obj);
1256 }
1257
1258 // Check the Component tree is well formed
1259 if (obj->defaultProperty &&
1260 (obj->defaultProperty->value || obj->defaultProperty->values.count() > 1 ||
1261 (obj->defaultProperty->values.count() == 1 && !obj->defaultProperty->values.first()->object)))
1262 COMPILE_EXCEPTION(obj, tr("Invalid component body specification"));
1263
1264 if (!obj->dynamicProperties.isEmpty())
1265 COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties."));
1266 if (!obj->dynamicSignals.isEmpty())
1267 COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals."));
1268 if (!obj->dynamicSlots.isEmpty())
1269 COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions."));
1270
1271 Object *root = 0;
1272 if (obj->defaultProperty && obj->defaultProperty->values.count())
1273 root = obj->defaultProperty->values.first()->object;
1274
1275 if (!root)
1276 COMPILE_EXCEPTION(obj, tr("Cannot create empty component specification"));
1277
1278 // Build the component tree
1279 COMPILE_CHECK(buildComponentFromRoot(root, ctxt));
1280
1281 return true;
1282}
1283
1284bool QDeclarativeCompiler::buildComponentFromRoot(QDeclarativeParser::Object *obj,
1285 const BindingContext &ctxt)
1286{
1287 ComponentCompileState oldComponentCompileState = compileState;
1288 ComponentStat oldComponentStat = componentStat;
1289
1290 compileState = ComponentCompileState();
1291 compileState.root = obj;
1292
1293 componentStat = ComponentStat();
1294 componentStat.lineNumber = obj->location.start.line;
1295
1296 if (obj)
1297 COMPILE_CHECK(buildObject(obj, ctxt));
1298
1299 COMPILE_CHECK(completeComponentBuild());
1300
1301 compileState = oldComponentCompileState;
1302 componentStat = oldComponentStat;
1303
1304 return true;
1305}
1306
1307
1308// Build a sub-object. A sub-object is one that was not created directly by
1309// QML - such as a grouped property object, or an attached object. Sub-object's
1310// can't have an id, involve a custom parser, have attached properties etc.
1311bool QDeclarativeCompiler::buildSubObject(Object *obj, const BindingContext &ctxt)
1312{
1313 Q_ASSERT(obj->metatype);
1314 Q_ASSERT(!obj->defaultProperty);
1315 Q_ASSERT(ctxt.isSubContext()); // sub-objects must always be in a binding
1316 // sub-context
1317
1318 foreach(Property *prop, obj->properties) {
1319 if (isSignalPropertyName(prop->name)) {
1320 COMPILE_CHECK(buildSignal(prop, obj, ctxt));
1321 } else {
1322 COMPILE_CHECK(buildProperty(prop, obj, ctxt));
1323 }
1324 }
1325
1326 return true;
1327}
1328
1329int QDeclarativeCompiler::componentTypeRef()
1330{
1331 QDeclarativeType *t = QDeclarativeMetaType::qmlType("QtQuick/Component",1,0);
1332 for (int ii = output->types.count() - 1; ii >= 0; --ii) {
1333 if (output->types.at(ii).type == t)
1334 return ii;
1335 }
1336 QDeclarativeCompiledData::TypeReference ref;
1337 ref.className = "Component";
1338 ref.type = t;
1339 output->types << ref;
1340 return output->types.count() - 1;
1341}
1342
1343bool QDeclarativeCompiler::buildSignal(QDeclarativeParser::Property *prop, QDeclarativeParser::Object *obj,
1344 const BindingContext &ctxt)
1345{
1346 Q_ASSERT(obj->metaObject());
1347
1348 QByteArray name = prop->name;
1349 Q_ASSERT(name.startsWith("on"));
1350 name = name.mid(2);
1351 if(name[0] >= 'A' && name[0] <= 'Z')
1352 name[0] = name[0] - 'A' + 'a';
1353
1354 int sigIdx = QDeclarativePropertyPrivate::findSignalByName(obj->metaObject(), name).methodIndex();
1355
1356 if (sigIdx == -1) {
1357
1358 // If the "on<Signal>" name doesn't resolve into a signal, try it as a
1359 // property.
1360 COMPILE_CHECK(buildProperty(prop, obj, ctxt));
1361
1362 } else {
1363
1364 if (prop->value || prop->values.count() != 1)
1365 COMPILE_EXCEPTION(prop, tr("Incorrectly specified signal assignment"));
1366
1367 prop->index = sigIdx;
1368 obj->addSignalProperty(prop);
1369
1370 if (prop->values.at(0)->object) {
1371 COMPILE_CHECK(buildObject(prop->values.at(0)->object, ctxt));
1372 prop->values.at(0)->type = Value::SignalObject;
1373 } else {
1374 prop->values.at(0)->type = Value::SignalExpression;
1375
1376 if (!prop->values.at(0)->value.isScript())
1377 COMPILE_EXCEPTION(prop, tr("Cannot assign a value to a signal (expecting a script to be run)"));
1378
1379 QString script = prop->values.at(0)->value.asScript().trimmed();
1380 if (script.isEmpty())
1381 COMPILE_EXCEPTION(prop, tr("Empty signal assignment"));
1382
1383 compileState.signalExpressions.insert(prop->values.at(0), ctxt);
1384 }
1385 }
1386
1387 return true;
1388}
1389
1390
1391/*!
1392 Returns true if (value) property \a prop exists on obj, false otherwise.
1393*/
1394bool QDeclarativeCompiler::doesPropertyExist(QDeclarativeParser::Property *prop,
1395 QDeclarativeParser::Object *obj)
1396{
1397 if(isAttachedPropertyName(prop->name) || prop->name == "id")
1398 return true;
1399
1400 const QMetaObject *mo = obj->metaObject();
1401 if (mo) {
1402 if (prop->isDefault) {
1403 QMetaProperty p = QDeclarativeMetaType::defaultProperty(mo);
1404 return p.name() != 0;
1405 } else {
1406 int idx = mo->indexOfProperty(prop->name.constData());
1407 return idx != -1 && mo->property(idx).isScriptable();
1408 }
1409 }
1410
1411 return false;
1412}
1413
1414bool QDeclarativeCompiler::buildProperty(QDeclarativeParser::Property *prop,
1415 QDeclarativeParser::Object *obj,
1416 const BindingContext &ctxt)
1417{
1418 if (prop->isEmpty())
1419 COMPILE_EXCEPTION(prop, tr("Empty property assignment"));
1420
1421 const QMetaObject *metaObject = obj->metaObject();
1422 Q_ASSERT(metaObject);
1423
1424 if (isAttachedPropertyName(prop->name)) {
1425 // Setup attached property data
1426
1427 if (ctxt.isSubContext()) {
1428 // Attached properties cannot be used on sub-objects. Sub-objects
1429 // always exist in a binding sub-context, which is what we test
1430 // for here.
1431 COMPILE_EXCEPTION(prop, tr("Attached properties cannot be used here"));
1432 }
1433
1434 QDeclarativeType *type = 0;
1435 QDeclarativeImportedNamespace *typeNamespace = 0;
1436 unit->imports().resolveType(prop->name, &type, 0, 0, 0, &typeNamespace);
1437
1438 if (typeNamespace) {
1439 // ### We might need to indicate that this property is a namespace
1440 // for the DOM API
1441 COMPILE_CHECK(buildPropertyInNamespace(typeNamespace, prop, obj,
1442 ctxt));
1443 return true;
1444 } else if (!type || !type->attachedPropertiesType()) {
1445 COMPILE_EXCEPTION(prop, tr("Non-existent attached object"));
1446 }
1447
1448 if (!prop->value)
1449 COMPILE_EXCEPTION(prop, tr("Invalid attached object assignment"));
1450
1451 Q_ASSERT(type->attachedPropertiesFunction());
1452 prop->index = type->attachedPropertiesId();
1453 prop->value->metatype = type->attachedPropertiesType();
1454 } else {
1455 // Setup regular property data
1456 QMetaProperty p;
1457
1458 if (prop->isDefault) {
1459 p = QDeclarativeMetaType::defaultProperty(metaObject);
1460
1461 if (p.name()) {
1462 prop->index = p.propertyIndex();
1463 prop->name = p.name();
1464 }
1465
1466 } else {
1467 prop->index = metaObject->indexOfProperty(prop->name.constData());
1468
1469 if (prop->index != -1) {
1470 p = metaObject->property(prop->index);
1471 Q_ASSERT(p.name());
1472
1473 if (!p.isScriptable()) {
1474 prop->index = -1;
1475 p = QMetaProperty();
1476 }
1477 }
1478 }
1479
1480 // We can't error here as the "id" property does not require a
1481 // successful index resolution
1482 if (p.name()) {
1483 prop->type = p.userType();
1484 }
1485
1486 // Check if this is an alias
1487 if (prop->index != -1 &&
1488 prop->parent &&
1489 prop->parent->type != -1 &&
1490 output->types.at(prop->parent->type).component) {
1491
1492 QDeclarativePropertyCache *cache = output->types.at(prop->parent->type).component->rootPropertyCache;
1493 if (cache && cache->property(prop->index) &&
1494 cache->property(prop->index)->flags & QDeclarativePropertyCache::Data::IsAlias)
1495 prop->isAlias = true;
1496 }
1497
1498 if (prop->index != -1 && !prop->values.isEmpty())
1499 prop->parent->setBindingBit(prop->index);
1500 }
1501
1502 if (!prop->isDefault && prop->name == "id" && !ctxt.isSubContext()) {
1503
1504 // The magic "id" behavior doesn't apply when "id" is resolved as a
1505 // default property or to sub-objects (which are always in binding
1506 // sub-contexts)
1507 COMPILE_CHECK(buildIdProperty(prop, obj));
1508 if (prop->type == QVariant::String &&
1509 prop->values.at(0)->value.isString())
1510 COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt));
1511
1512 } else if (isAttachedPropertyName(prop->name)) {
1513
1514 COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt));
1515
1516 } else if (prop->index == -1) {
1517
1518 if (prop->isDefault) {
1519 COMPILE_EXCEPTION(prop->values.first(), tr("Cannot assign to non-existent default property"));
1520 } else {
1521 COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(QString::fromUtf8(prop->name)));
1522 }
1523
1524 } else if (prop->value) {
1525
1526 COMPILE_CHECK(buildGroupedProperty(prop, obj, ctxt));
1527
1528 } else if (enginePrivate->isList(prop->type)) {
1529
1530 COMPILE_CHECK(buildListProperty(prop, obj, ctxt));
1531
1532 } else if (prop->type == qMetaTypeId<QDeclarativeScriptString>()) {
1533
1534 COMPILE_CHECK(buildScriptStringProperty(prop, obj, ctxt));
1535
1536 } else {
1537
1538 COMPILE_CHECK(buildPropertyAssignment(prop, obj, ctxt));
1539
1540 }
1541
1542 return true;
1543}
1544
1545bool QDeclarativeCompiler::buildPropertyInNamespace(QDeclarativeImportedNamespace *ns,
1546 QDeclarativeParser::Property *nsProp,
1547 QDeclarativeParser::Object *obj,
1548 const BindingContext &ctxt)
1549{
1550 if (!nsProp->value)
1551 COMPILE_EXCEPTION(nsProp, tr("Invalid use of namespace"));
1552
1553 foreach (Property *prop, nsProp->value->properties) {
1554
1555 if (!isAttachedPropertyName(prop->name))
1556 COMPILE_EXCEPTION(prop, tr("Not an attached property name"));
1557
1558 // Setup attached property data
1559
1560 QDeclarativeType *type = 0;
1561 unit->imports().resolveType(ns, prop->name, &type, 0, 0, 0);
1562
1563 if (!type || !type->attachedPropertiesType())
1564 COMPILE_EXCEPTION(prop, tr("Non-existent attached object"));
1565
1566 if (!prop->value)
1567 COMPILE_EXCEPTION(prop, tr("Invalid attached object assignment"));
1568
1569 Q_ASSERT(type->attachedPropertiesFunction());
1570 prop->index = type->index();
1571 prop->value->metatype = type->attachedPropertiesType();
1572
1573 COMPILE_CHECK(buildAttachedProperty(prop, obj, ctxt));
1574 }
1575
1576 return true;
1577}
1578
1579void QDeclarativeCompiler::genValueProperty(QDeclarativeParser::Property *prop,
1580 QDeclarativeParser::Object *obj)
1581{
1582 if (enginePrivate->isList(prop->type)) {
1583 genListProperty(prop, obj);
1584 } else {
1585 genPropertyAssignment(prop, obj);
1586 }
1587}
1588
1589void QDeclarativeCompiler::genListProperty(QDeclarativeParser::Property *prop,
1590 QDeclarativeParser::Object *obj)
1591{
1592 int listType = enginePrivate->listType(prop->type);
1593
1594 QDeclarativeInstruction fetch;
1595 fetch.type = QDeclarativeInstruction::FetchQList;
1596 fetch.line = prop->location.start.line;
1597 fetch.fetchQmlList.property = prop->index;
1598 bool listTypeIsInterface = QDeclarativeMetaType::isInterface(listType);
1599 fetch.fetchQmlList.type = listType;
1600 output->bytecode << fetch;
1601
1602 for (int ii = 0; ii < prop->values.count(); ++ii) {
1603 Value *v = prop->values.at(ii);
1604
1605 if (v->type == Value::CreatedObject) {
1606
1607 genObject(v->object);
1608 if (listTypeIsInterface) {
1609 QDeclarativeInstruction assign;
1610 assign.type = QDeclarativeInstruction::AssignObjectList;
1611 assign.line = prop->location.start.line;
1612 output->bytecode << assign;
1613 } else {
1614 QDeclarativeInstruction store;
1615 store.type = QDeclarativeInstruction::StoreObjectQList;
1616 store.line = prop->location.start.line;
1617 output->bytecode << store;
1618 }
1619
1620 } else if (v->type == Value::PropertyBinding) {
1621
1622 genBindingAssignment(v, prop, obj);
1623
1624 }
1625
1626 }
1627
1628 QDeclarativeInstruction pop;
1629 pop.type = QDeclarativeInstruction::PopQList;
1630 pop.line = prop->location.start.line;
1631 output->bytecode << pop;
1632}
1633
1634void QDeclarativeCompiler::genPropertyAssignment(QDeclarativeParser::Property *prop,
1635 QDeclarativeParser::Object *obj,
1636 QDeclarativeParser::Property *valueTypeProperty)
1637{
1638 for (int ii = 0; ii < prop->values.count(); ++ii) {
1639 QDeclarativeParser::Value *v = prop->values.at(ii);
1640
1641 Q_ASSERT(v->type == Value::CreatedObject ||
1642 v->type == Value::PropertyBinding ||
1643 v->type == Value::Literal);
1644
1645 if (v->type == Value::CreatedObject) {
1646
1647 genObject(v->object);
1648
1649 if (QDeclarativeMetaType::isInterface(prop->type)) {
1650
1651 QDeclarativeInstruction store;
1652 store.type = QDeclarativeInstruction::StoreInterface;
1653 store.line = v->object->location.start.line;
1654 store.storeObject.propertyIndex = prop->index;
1655 output->bytecode << store;
1656
1657 } else if (prop->type == -1) {
1658
1659 QDeclarativeInstruction store;
1660 store.type = QDeclarativeInstruction::StoreVariantObject;
1661 store.line = v->object->location.start.line;
1662 store.storeObject.propertyIndex = prop->index;
1663 output->bytecode << store;
1664
1665 } else {
1666
1667 QDeclarativeInstruction store;
1668 store.type = QDeclarativeInstruction::StoreObject;
1669 store.line = v->object->location.start.line;
1670 store.storeObject.propertyIndex = prop->index;
1671 output->bytecode << store;
1672
1673 }
1674 } else if (v->type == Value::PropertyBinding) {
1675
1676 genBindingAssignment(v, prop, obj, valueTypeProperty);
1677
1678 } else if (v->type == Value::Literal) {
1679
1680 QMetaProperty mp = obj->metaObject()->property(prop->index);
1681 genLiteralAssignment(mp, v);
1682
1683 }
1684
1685 }
1686
1687 for (int ii = 0; ii < prop->onValues.count(); ++ii) {
1688
1689 QDeclarativeParser::Value *v = prop->onValues.at(ii);
1690
1691 Q_ASSERT(v->type == Value::ValueSource ||
1692 v->type == Value::ValueInterceptor);
1693
1694 if (v->type == Value::ValueSource) {
1695 genObject(v->object);
1696
1697 QDeclarativeInstruction store;
1698 store.type = QDeclarativeInstruction::StoreValueSource;
1699 store.line = v->object->location.start.line;
1700 if (valueTypeProperty) {
1701 store.assignValueSource.property = genValueTypeData(prop, valueTypeProperty);
1702 store.assignValueSource.owner = 1;
1703 } else {
1704 store.assignValueSource.property = genPropertyData(prop);
1705 store.assignValueSource.owner = 0;
1706 }
1707 QDeclarativeType *valueType = toQmlType(v->object);
1708 store.assignValueSource.castValue = valueType->propertyValueSourceCast();
1709 output->bytecode << store;
1710
1711 } else if (v->type == Value::ValueInterceptor) {
1712 genObject(v->object);
1713
1714 QDeclarativeInstruction store;
1715 store.type = QDeclarativeInstruction::StoreValueInterceptor;
1716 store.line = v->object->location.start.line;
1717 if (valueTypeProperty) {
1718 store.assignValueInterceptor.property = genValueTypeData(prop, valueTypeProperty);
1719 store.assignValueInterceptor.owner = 1;
1720 } else {
1721 store.assignValueInterceptor.property = genPropertyData(prop);
1722 store.assignValueInterceptor.owner = 0;
1723 }
1724 QDeclarativeType *valueType = toQmlType(v->object);
1725 store.assignValueInterceptor.castValue = valueType->propertyValueInterceptorCast();
1726 output->bytecode << store;
1727 }
1728
1729 }
1730}
1731
1732bool QDeclarativeCompiler::buildIdProperty(QDeclarativeParser::Property *prop,
1733 QDeclarativeParser::Object *obj)
1734{
1735 if (prop->value ||
1736 prop->values.count() > 1 ||
1737 prop->values.at(0)->object)
1738 COMPILE_EXCEPTION(prop, tr("Invalid use of id property"));
1739
1740 QDeclarativeParser::Value *idValue = prop->values.at(0);
1741 QString val = idValue->primitive();
1742
1743 COMPILE_CHECK(checkValidId(idValue, val));
1744
1745 if (compileState.ids.contains(val))
1746 COMPILE_EXCEPTION(prop, tr("id is not unique"));
1747
1748 prop->values.at(0)->type = Value::Id;
1749
1750 obj->id = val;
1751 addId(val, obj);
1752
1753 return true;
1754}
1755
1756void QDeclarativeCompiler::addId(const QString &id, QDeclarativeParser::Object *obj)
1757{
1758 Q_ASSERT(!compileState.ids.contains(id));
1759 Q_ASSERT(obj->id == id);
1760 obj->idIndex = compileState.ids.count();
1761 compileState.ids.insert(id, obj);
1762 compileState.idIndexes.insert(obj->idIndex, obj);
1763}
1764
1765void QDeclarativeCompiler::addBindingReference(const BindingReference &ref)
1766{
1767 Q_ASSERT(ref.value && !compileState.bindings.contains(ref.value));
1768 compileState.bindings.insert(ref.value, ref);
1769}
1770
1771void QDeclarativeCompiler::saveComponentState()
1772{
1773 Q_ASSERT(compileState.root);
1774 Q_ASSERT(!savedCompileStates.contains(compileState.root));
1775
1776 savedCompileStates.insert(compileState.root, compileState);
1777 savedComponentStats.append(componentStat);
1778}
1779
1780QDeclarativeCompiler::ComponentCompileState
1781QDeclarativeCompiler::componentState(QDeclarativeParser::Object *obj)
1782{
1783 Q_ASSERT(savedCompileStates.contains(obj));
1784 return savedCompileStates.value(obj);
1785}
1786
1787// Build attached property object. In this example,
1788// Text {
1789// GridView.row: 10
1790// }
1791// GridView is an attached property object.
1792bool QDeclarativeCompiler::buildAttachedProperty(QDeclarativeParser::Property *prop,
1793 QDeclarativeParser::Object *obj,
1794 const BindingContext &ctxt)
1795{
1796 Q_ASSERT(prop->value);
1797 Q_ASSERT(prop->index != -1); // This is set in buildProperty()
1798
1799 obj->addAttachedProperty(prop);
1800
1801 COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr()));
1802
1803 return true;
1804}
1805
1806
1807// Build "grouped" properties. In this example:
1808// Text {
1809// font.pointSize: 12
1810// font.family: "Helvetica"
1811// }
1812// font is a nested property. pointSize and family are not.
1813bool QDeclarativeCompiler::buildGroupedProperty(QDeclarativeParser::Property *prop,
1814 QDeclarativeParser::Object *obj,
1815 const BindingContext &ctxt)
1816{
1817 Q_ASSERT(prop->type != 0);
1818 Q_ASSERT(prop->index != -1);
1819
1820 if (QDeclarativeValueTypeFactory::isValueType(prop->type)) {
1821 if (prop->type >= 0 /* QVariant == -1 */ && enginePrivate->valueTypes[prop->type]) {
1822
1823 if (prop->values.count()) {
1824 if (prop->values.at(0)->location < prop->value->location) {
1825 COMPILE_EXCEPTION(prop->value, tr( "Property has already been assigned a value"));
1826 } else {
1827 COMPILE_EXCEPTION(prop->values.at(0), tr( "Property has already been assigned a value"));
1828 }
1829 }
1830
1831 if (!obj->metaObject()->property(prop->index).isWritable()) {
1832 COMPILE_EXCEPTION(prop, tr( "Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop->name)));
1833 }
1834
1835
1836 if (prop->isAlias) {
1837 foreach (Property *vtProp, prop->value->properties)
1838 vtProp->isAlias = true;
1839 }
1840
1841 COMPILE_CHECK(buildValueTypeProperty(enginePrivate->valueTypes[prop->type],
1842 prop->value, obj, ctxt.incr()));
1843 obj->addValueTypeProperty(prop);
1844 } else {
1845 COMPILE_EXCEPTION(prop, tr("Invalid grouped property access"));
1846 }
1847
1848 } else {
1849 // Load the nested property's meta type
1850 prop->value->metatype = enginePrivate->metaObjectForType(prop->type);
1851 if (!prop->value->metatype)
1852 COMPILE_EXCEPTION(prop, tr("Invalid grouped property access"));
1853
1854 if (prop->values.count())
1855 COMPILE_EXCEPTION(prop->values.at(0), tr( "Cannot assign a value directly to a grouped property"));
1856
1857 obj->addGroupedProperty(prop);
1858
1859 COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr()));
1860 }
1861
1862 return true;
1863}
1864
1865bool QDeclarativeCompiler::buildValueTypeProperty(QObject *type,
1866 QDeclarativeParser::Object *obj,
1867 QDeclarativeParser::Object *baseObj,
1868 const BindingContext &ctxt)
1869{
1870 if (obj->defaultProperty)
1871 COMPILE_EXCEPTION(obj, tr("Invalid property use"));
1872 obj->metatype = type->metaObject();
1873
1874 foreach (Property *prop, obj->properties) {
1875 int idx = type->metaObject()->indexOfProperty(prop->name.constData());
1876 if (idx == -1)
1877 COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(QString::fromUtf8(prop->name)));
1878 QMetaProperty p = type->metaObject()->property(idx);
1879 if (!p.isScriptable())
1880 COMPILE_EXCEPTION(prop, tr("Cannot assign to non-existent property \"%1\"").arg(QString::fromUtf8(prop->name)));
1881 prop->index = idx;
1882 prop->type = p.userType();
1883 prop->isValueTypeSubProperty = true;
1884
1885 if (prop->value)
1886 COMPILE_EXCEPTION(prop, tr("Property assignment expected"));
1887
1888 if (prop->values.count() > 1) {
1889 COMPILE_EXCEPTION(prop, tr("Single property assignment expected"));
1890 } else if (prop->values.count()) {
1891 Value *value = prop->values.at(0);
1892
1893 if (value->object) {
1894 COMPILE_EXCEPTION(prop, tr("Unexpected object assignment"));
1895 } else if (value->value.isScript()) {
1896 // ### Check for writability
1897 BindingReference reference;
1898 reference.expression = value->value;
1899 reference.property = prop;
1900 reference.value = value;
1901 reference.bindingContext = ctxt;
1902 reference.bindingContext.owner++;
1903 addBindingReference(reference);
1904 value->type = Value::PropertyBinding;
1905 } else {
1906 COMPILE_CHECK(testLiteralAssignment(p, value));
1907 value->type = Value::Literal;
1908 }
1909 }
1910
1911 for (int ii = 0; ii < prop->onValues.count(); ++ii) {
1912 Value *v = prop->onValues.at(ii);
1913 Q_ASSERT(v->object);
1914
1915 COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, baseObj, v, ctxt));
1916 }
1917
1918 obj->addValueProperty(prop);
1919 }
1920
1921 return true;
1922}
1923
1924// Build assignments to QML lists. QML lists are properties of type
1925// QDeclarativeListProperty<T>. List properties can accept a list of
1926// objects, or a single binding.
1927bool QDeclarativeCompiler::buildListProperty(QDeclarativeParser::Property *prop,
1928 QDeclarativeParser::Object *obj,
1929 const BindingContext &ctxt)
1930{
1931 Q_ASSERT(enginePrivate->isList(prop->type));
1932
1933 int t = prop->type;
1934
1935 obj->addValueProperty(prop);
1936
1937 int listType = enginePrivate->listType(t);
1938 bool listTypeIsInterface = QDeclarativeMetaType::isInterface(listType);
1939
1940 bool assignedBinding = false;
1941 for (int ii = 0; ii < prop->values.count(); ++ii) {
1942 Value *v = prop->values.at(ii);
1943 if (v->object) {
1944 v->type = Value::CreatedObject;
1945 COMPILE_CHECK(buildObject(v->object, ctxt));
1946
1947 // We check object coercian here. We check interface assignment
1948 // at runtime.
1949 if (!listTypeIsInterface) {
1950 if (!canCoerce(listType, v->object)) {
1951 COMPILE_EXCEPTION(v, tr("Cannot assign object to list"));
1952 }
1953 }
1954
1955 } else if (v->value.isScript()) {
1956 if (assignedBinding)
1957 COMPILE_EXCEPTION(v, tr("Can only assign one binding to lists"));
1958
1959 assignedBinding = true;
1960 COMPILE_CHECK(buildBinding(v, prop, ctxt));
1961 v->type = Value::PropertyBinding;
1962 } else {
1963 COMPILE_EXCEPTION(v, tr("Cannot assign primitives to lists"));
1964 }
1965 }
1966
1967 return true;
1968}
1969
1970// Compiles an assignment to a QDeclarativeScriptString property
1971bool QDeclarativeCompiler::buildScriptStringProperty(QDeclarativeParser::Property *prop,
1972 QDeclarativeParser::Object *obj,
1973 const BindingContext &ctxt)
1974{
1975 if (prop->values.count() > 1)
1976 COMPILE_EXCEPTION(prop->values.at(1), tr( "Cannot assign multiple values to a script property"));
1977
1978 if (prop->values.at(0)->object)
1979 COMPILE_EXCEPTION(prop->values.at(0), tr( "Invalid property assignment: script expected"));
1980
1981 obj->addScriptStringProperty(prop, ctxt.stack);
1982
1983 return true;
1984}
1985
1986// Compile regular property assignments of the form "property: <value>"
1987bool QDeclarativeCompiler::buildPropertyAssignment(QDeclarativeParser::Property *prop,
1988 QDeclarativeParser::Object *obj,
1989 const BindingContext &ctxt)
1990{
1991 obj->addValueProperty(prop);
1992
1993 if (prop->values.count() > 1)
1994 COMPILE_EXCEPTION(prop->values.at(0), tr( "Cannot assign multiple values to a singular property") );
1995
1996 for (int ii = 0; ii < prop->values.count(); ++ii) {
1997 Value *v = prop->values.at(ii);
1998 if (v->object) {
1999
2000 COMPILE_CHECK(buildPropertyObjectAssignment(prop, obj, v, ctxt));
2001
2002 } else {
2003
2004 COMPILE_CHECK(buildPropertyLiteralAssignment(prop, obj, v, ctxt));
2005
2006 }
2007 }
2008
2009 for (int ii = 0; ii < prop->onValues.count(); ++ii) {
2010 Value *v = prop->onValues.at(ii);
2011
2012 Q_ASSERT(v->object);
2013 COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, obj, v, ctxt));
2014 }
2015
2016 return true;
2017}
2018
2019// Compile assigning a single object instance to a regular property
2020bool QDeclarativeCompiler::buildPropertyObjectAssignment(QDeclarativeParser::Property *prop,
2021 QDeclarativeParser::Object *obj,
2022 QDeclarativeParser::Value *v,
2023 const BindingContext &ctxt)
2024{
2025 Q_ASSERT(prop->index != -1);
2026 Q_ASSERT(v->object->type != -1);
2027
2028 if (!obj->metaObject()->property(prop->index).isWritable())
2029 COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop->name)));
2030
2031 if (QDeclarativeMetaType::isInterface(prop->type)) {
2032
2033 // Assigning an object to an interface ptr property
2034 COMPILE_CHECK(buildObject(v->object, ctxt));
2035
2036 v->type = Value::CreatedObject;
2037
2038 } else if (prop->type == -1) {
2039
2040 // Assigning an object to a QVariant
2041 COMPILE_CHECK(buildObject(v->object, ctxt));
2042
2043 v->type = Value::CreatedObject;
2044 } else {
2045 // Normally buildObject() will set this up, but we need the static
2046 // meta object earlier to test for assignability. It doesn't matter
2047 // that there may still be outstanding synthesized meta object changes
2048 // on this type, as they are not relevant for assignability testing
2049 v->object->metatype = output->types.at(v->object->type).metaObject();
2050 Q_ASSERT(v->object->metaObject());
2051
2052 // We want to raw metaObject here as the raw metaobject is the
2053 // actual property type before we applied any extensions that might
2054 // effect the properties on the type, but don't effect assignability
2055 const QMetaObject *propertyMetaObject = enginePrivate->rawMetaObjectForType(prop->type);
2056
2057 // Will be true if the assgned type inherits propertyMetaObject
2058 bool isAssignable = false;
2059 // Determine isAssignable value
2060 if (propertyMetaObject) {
2061 const QMetaObject *c = v->object->metatype;
2062 while(c) {
2063 isAssignable |= (QDeclarativePropertyPrivate::equal(c, propertyMetaObject));
2064 c = c->superClass();
2065 }
2066 }
2067
2068 if (isAssignable) {
2069 // Simple assignment
2070 COMPILE_CHECK(buildObject(v->object, ctxt));
2071
2072 v->type = Value::CreatedObject;
2073 } else if (propertyMetaObject == &QDeclarativeComponent::staticMetaObject) {
2074 // Automatic "Component" insertion
2075 QDeclarativeParser::Object *root = v->object;
2076 QDeclarativeParser::Object *component = new QDeclarativeParser::Object;
2077 component->type = componentTypeRef();
2078 component->typeName = "Qt/Component";
2079 component->metatype = &QDeclarativeComponent::staticMetaObject;
2080 component->location = root->location;
2081 QDeclarativeParser::Value *componentValue = new QDeclarativeParser::Value;
2082 componentValue->object = root;
2083 component->getDefaultProperty()->addValue(componentValue);
2084 v->object = component;
2085 COMPILE_CHECK(buildPropertyObjectAssignment(prop, obj, v, ctxt));
2086 } else {
2087 COMPILE_EXCEPTION(v->object, tr("Cannot assign object to property"));
2088 }
2089 }
2090
2091 return true;
2092}
2093
2094// Compile assigning a single object instance to a regular property using the "on" syntax.
2095//
2096// For example:
2097// Item {
2098// NumberAnimation on x { }
2099// }
2100bool QDeclarativeCompiler::buildPropertyOnAssignment(QDeclarativeParser::Property *prop,
2101 QDeclarativeParser::Object *obj,
2102 QDeclarativeParser::Object *baseObj,
2103 QDeclarativeParser::Value *v,
2104 const BindingContext &ctxt)
2105{
2106 Q_ASSERT(prop->index != -1);
2107 Q_ASSERT(v->object->type != -1);
2108
2109 if (!obj->metaObject()->property(prop->index).isWritable())
2110 COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop->name)));
2111
2112
2113 // Normally buildObject() will set this up, but we need the static
2114 // meta object earlier to test for assignability. It doesn't matter
2115 // that there may still be outstanding synthesized meta object changes
2116 // on this type, as they are not relevant for assignability testing
2117 v->object->metatype = output->types.at(v->object->type).metaObject();
2118 Q_ASSERT(v->object->metaObject());
2119
2120 // Will be true if the assigned type inherits QDeclarativePropertyValueSource
2121 bool isPropertyValue = false;
2122 // Will be true if the assigned type inherits QDeclarativePropertyValueInterceptor
2123 bool isPropertyInterceptor = false;
2124 if (QDeclarativeType *valueType = toQmlType(v->object)) {
2125 isPropertyValue = valueType->propertyValueSourceCast() != -1;
2126 isPropertyInterceptor = valueType->propertyValueInterceptorCast() != -1;
2127 }
2128
2129 if (isPropertyValue || isPropertyInterceptor) {
2130 // Assign as a property value source
2131 COMPILE_CHECK(buildObject(v->object, ctxt));
2132
2133 if (isPropertyInterceptor && prop->parent->synthdata.isEmpty())
2134 buildDynamicMeta(baseObj, ForceCreation);
2135 v->type = isPropertyValue ? Value::ValueSource : Value::ValueInterceptor;
2136 } else {
2137 COMPILE_EXCEPTION(v, tr("\"%1\" cannot operate on \"%2\"").arg(QString::fromUtf8(v->object->typeName)).arg(QString::fromUtf8(prop->name.constData())));
2138 }
2139
2140 return true;
2141}
2142
2143// Compile assigning a literal or binding to a regular property
2144bool QDeclarativeCompiler::buildPropertyLiteralAssignment(QDeclarativeParser::Property *prop,
2145 QDeclarativeParser::Object *obj,
2146 QDeclarativeParser::Value *v,
2147 const BindingContext &ctxt)
2148{
2149 Q_ASSERT(prop->index != -1);
2150
2151 if (v->value.isScript()) {
2152
2153 //optimization for <Type>.<EnumValue> enum assignments
2154 bool isEnumAssignment = false;
2155 COMPILE_CHECK(testQualifiedEnumAssignment(obj->metaObject()->property(prop->index), obj, v, &isEnumAssignment));
2156 if (isEnumAssignment) {
2157 v->type = Value::Literal;
2158 return true;
2159 }
2160
2161 COMPILE_CHECK(buildBinding(v, prop, ctxt));
2162
2163 v->type = Value::PropertyBinding;
2164
2165 } else {
2166
2167 COMPILE_CHECK(testLiteralAssignment(obj->metaObject()->property(prop->index), v));
2168
2169 v->type = Value::Literal;
2170 }
2171
2172 return true;
2173}
2174
2175bool QDeclarativeCompiler::testQualifiedEnumAssignment(const QMetaProperty &prop,
2176 QDeclarativeParser::Object *obj,
2177 QDeclarativeParser::Value *v,
2178 bool *isAssignment)
2179{
2180 *isAssignment = false;
2181 if (!prop.isEnumType())
2182 return true;
2183
2184 if (!prop.isWritable())
2185 COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop.name())));
2186
2187 QString string = v->value.asString();
2188 if (!string.at(0).isUpper())
2189 return true;
2190
2191 QStringList parts = string.split(QLatin1Char('.'));
2192 if (parts.count() != 2)
2193 return true;
2194
2195 QString typeName = parts.at(0);
2196 QDeclarativeType *type = 0;
2197 unit->imports().resolveType(typeName.toUtf8(), &type, 0, 0, 0, 0);
2198
2199 if (!type || obj->typeName != type->qmlTypeName())
2200 return true;
2201
2202 QString enumValue = parts.at(1);
2203 int value;
2204 if (prop.isFlagType()) {
2205 value = prop.enumerator().keysToValue(enumValue.toUtf8().constData());
2206 } else
2207 value = prop.enumerator().keyToValue(enumValue.toUtf8().constData());
2208 if (value == -1)
2209 return true;
2210
2211 v->type = Value::Literal;
2212 v->value = QDeclarativeParser::Variant(enumValue);
2213 *isAssignment = true;
2214
2215 return true;
2216}
2217
2218// Similar logic to above, but not knowing target property.
2219int QDeclarativeCompiler::evaluateEnum(const QByteArray& script) const
2220{
2221 int dot = script.indexOf('.');
2222 if (dot > 0) {
2223 QDeclarativeType *type = 0;
2224 unit->imports().resolveType(script.left(dot), &type, 0, 0, 0, 0);
2225 if (!type)
2226 return -1;
2227 const QMetaObject *mo = type->metaObject();
2228 const char *key = script.constData() + dot+1;
2229 int i = mo->enumeratorCount();
2230 while (i--) {
2231 int v = mo->enumerator(i).keyToValue(key);
2232 if (v >= 0)
2233 return v;
2234 }
2235 }
2236 return -1;
2237}
2238
2239const QMetaObject *QDeclarativeCompiler::resolveType(const QByteArray& name) const
2240{
2241 QDeclarativeType *qmltype = 0;
2242 if (!unit->imports().resolveType(name, &qmltype, 0, 0, 0, 0))
2243 return 0;
2244 if (!qmltype)
2245 return 0;
2246 return qmltype->metaObject();
2247}
2248
2249
2250// Ensures that the dynamic meta specification on obj is valid
2251bool QDeclarativeCompiler::checkDynamicMeta(QDeclarativeParser::Object *obj)
2252{
2253 QSet<QByteArray> propNames;
2254 QSet<QByteArray> methodNames;
2255 bool seenDefaultProperty = false;
2256
2257 // Check properties
2258 for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) {
2259 const QDeclarativeParser::Object::DynamicProperty &prop =
2260 obj->dynamicProperties.at(ii);
2261
2262 if (prop.isDefaultProperty) {
2263 if (seenDefaultProperty)
2264 COMPILE_EXCEPTION(&prop, tr("Duplicate default property"));
2265 seenDefaultProperty = true;
2266 }
2267
2268 if (propNames.contains(prop.name))
2269 COMPILE_EXCEPTION(&prop, tr("Duplicate property name"));
2270
2271 QString propName = QString::fromUtf8(prop.name);
2272 if (propName.at(0).isUpper())
2273 COMPILE_EXCEPTION(&prop, tr("Property names cannot begin with an upper case letter"));
2274
2275 if (enginePrivate->globalClass->illegalNames().contains(propName))
2276 COMPILE_EXCEPTION(&prop, tr("Illegal property name"));
2277
2278 propNames.insert(prop.name);
2279 }
2280
2281 for (int ii = 0; ii < obj->dynamicSignals.count(); ++ii) {
2282 QByteArray name = obj->dynamicSignals.at(ii).name;
2283 if (methodNames.contains(name))
2284 COMPILE_EXCEPTION(obj, tr("Duplicate signal name"));
2285 QString nameStr = QString::fromUtf8(name);
2286 if (nameStr.at(0).isUpper())
2287 COMPILE_EXCEPTION(obj, tr("Signal names cannot begin with an upper case letter"));
2288 if (enginePrivate->globalClass->illegalNames().contains(nameStr))
2289 COMPILE_EXCEPTION(obj, tr("Illegal signal name"));
2290 methodNames.insert(name);
2291 }
2292 for (int ii = 0; ii < obj->dynamicSlots.count(); ++ii) {
2293 QByteArray name = obj->dynamicSlots.at(ii).name;
2294 if (methodNames.contains(name))
2295 COMPILE_EXCEPTION(obj, tr("Duplicate method name"));
2296 QString nameStr = QString::fromUtf8(name);
2297 if (nameStr.at(0).isUpper())
2298 COMPILE_EXCEPTION(obj, tr("Method names cannot begin with an upper case letter"));
2299 if (enginePrivate->globalClass->illegalNames().contains(nameStr))
2300 COMPILE_EXCEPTION(obj, tr("Illegal method name"));
2301 methodNames.insert(name);
2302 }
2303
2304 return true;
2305}
2306
2307bool QDeclarativeCompiler::mergeDynamicMetaProperties(QDeclarativeParser::Object *obj)
2308{
2309 for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) {
2310 const Object::DynamicProperty &p = obj->dynamicProperties.at(ii);
2311
2312 if (!p.defaultValue || p.type == Object::DynamicProperty::Alias)
2313 continue;
2314
2315 Property *property = 0;
2316 if (p.isDefaultProperty) {
2317 property = obj->getDefaultProperty();
2318 } else {
2319 property = obj->getProperty(p.name);
2320 if (!property->values.isEmpty())
2321 COMPILE_EXCEPTION(property, tr("Property value set multiple times"));
2322 }
2323
2324 if (property->value)
2325 COMPILE_EXCEPTION(property, tr("Invalid property nesting"));
2326
2327 for (int ii = 0; ii < p.defaultValue->values.count(); ++ii) {
2328 Value *v = p.defaultValue->values.at(ii);
2329 v->addref();
2330 property->values.append(v);
2331 }
2332 }
2333 return true;
2334}
2335
2336Q_GLOBAL_STATIC(QAtomicInt, classIndexCounter)
2337
2338bool QDeclarativeCompiler::buildDynamicMeta(QDeclarativeParser::Object *obj, DynamicMetaMode mode)
2339{
2340 Q_ASSERT(obj);
2341 Q_ASSERT(obj->metatype);
2342
2343 if (mode != ForceCreation &&
2344 obj->dynamicProperties.isEmpty() &&
2345 obj->dynamicSignals.isEmpty() &&
2346 obj->dynamicSlots.isEmpty())
2347 return true;
2348
2349 QByteArray dynamicData(sizeof(QDeclarativeVMEMetaData), (char)0);
2350
2351 QByteArray newClassName = obj->metatype->className();
2352 newClassName.append("_QML_");
2353 int idx = classIndexCounter()->fetchAndAddRelaxed(1);
2354 newClassName.append(QByteArray::number(idx));
2355 if (compileState.root == obj) {
2356 QString path = output->url.path();
2357 int lastSlash = path.lastIndexOf(QLatin1Char('/'));
2358 if (lastSlash > -1) {
2359 QString nameBase = path.mid(lastSlash + 1, path.length()-lastSlash-5);
2360 if (!nameBase.isEmpty() && nameBase.at(0).isUpper())
2361 newClassName = nameBase.toUtf8() + "_QMLTYPE_" + QByteArray::number(idx);
2362 }
2363 }
2364
2365 QMetaObjectBuilder builder;
2366 builder.setClassName(newClassName);
2367 builder.setFlags(QMetaObjectBuilder::DynamicMetaObject);
2368
2369 bool hasAlias = false;
2370 for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) {
2371 const Object::DynamicProperty &p = obj->dynamicProperties.at(ii);
2372
2373 int propIdx =
2374 obj->metaObject()->indexOfProperty(p.name.constData());
2375 if (-1 != propIdx) {
2376 QMetaProperty prop = obj->metaObject()->property(propIdx);
2377 if (prop.isFinal())
2378 COMPILE_EXCEPTION(&p, tr("Cannot override FINAL property"));
2379 }
2380
2381 if (p.isDefaultProperty &&
2382 (p.type != Object::DynamicProperty::Alias ||
2383 mode == ResolveAliases))
2384 builder.addClassInfo("DefaultProperty", p.name);
2385
2386 QByteArray type;
2387 int propertyType = 0;
2388 bool readonly = false;
2389 switch(p.type) {
2390 case Object::DynamicProperty::Alias:
2391 hasAlias = true;
2392 continue;
2393 break;
2394 case Object::DynamicProperty::CustomList:
2395 case Object::DynamicProperty::Custom:
2396 {
2397 QByteArray customTypeName;
2398 QDeclarativeType *qmltype = 0;
2399 QUrl url;
2400 if (!unit->imports().resolveType(p.customType, &qmltype, &url, 0, 0, 0))
2401 COMPILE_EXCEPTION(&p, tr("Invalid property type"));
2402
2403 if (!qmltype) {
2404 QDeclarativeTypeData *tdata = enginePrivate->typeLoader.get(url);
2405 Q_ASSERT(tdata);
2406 Q_ASSERT(tdata->isComplete());
2407
2408 QDeclarativeCompiledData *data = tdata->compiledData();
2409 customTypeName = data->root->className();
2410 data->release();
2411 tdata->release();
2412 } else {
2413 customTypeName = qmltype->typeName();
2414 }
2415
2416 if (p.type == Object::DynamicProperty::Custom) {
2417 type = customTypeName + '*';
2418 propertyType = QMetaType::QObjectStar;
2419 } else {
2420 readonly = true;
2421 type = "QDeclarativeListProperty<";
2422 type.append(customTypeName);
2423 type.append(">");
2424 propertyType = qMetaTypeId<QDeclarativeListProperty<QObject> >();
2425 }
2426 }
2427 break;
2428 case Object::DynamicProperty::Variant:
2429 propertyType = -1;
2430 type = "QVariant";
2431 break;
2432 case Object::DynamicProperty::Int:
2433 propertyType = QVariant::Int;
2434 type = "int";
2435 break;
2436 case Object::DynamicProperty::Bool:
2437 propertyType = QVariant::Bool;
2438 type = "bool";
2439 break;
2440 case Object::DynamicProperty::Real:
2441 propertyType = QVariant::Double;
2442 type = "double";
2443 break;
2444 case Object::DynamicProperty::String:
2445 propertyType = QVariant::String;
2446 type = "QString";
2447 break;
2448 case Object::DynamicProperty::Url:
2449 propertyType = QVariant::Url;
2450 type = "QUrl";
2451 break;
2452 case Object::DynamicProperty::Color:
2453 propertyType = QVariant::Color;
2454 type = "QColor";
2455 break;
2456 case Object::DynamicProperty::Time:
2457 propertyType = QVariant::Time;
2458 type = "QTime";
2459 break;
2460 case Object::DynamicProperty::Date:
2461 propertyType = QVariant::Date;
2462 type = "QDate";
2463 break;
2464 case Object::DynamicProperty::DateTime:
2465 propertyType = QVariant::DateTime;
2466 type = "QDateTime";
2467 break;
2468 }
2469
2470 ((QDeclarativeVMEMetaData *)dynamicData.data())->propertyCount++;
2471 QDeclarativeVMEMetaData::PropertyData propertyData = { propertyType };
2472 dynamicData.append((char *)&propertyData, sizeof(propertyData));
2473
2474 builder.addSignal(p.name + "Changed()");
2475 QMetaPropertyBuilder propBuilder =
2476 builder.addProperty(p.name, type, builder.methodCount() - 1);
2477 propBuilder.setWritable(!readonly);
2478 }
2479
2480 for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) {
2481 const Object::DynamicProperty &p = obj->dynamicProperties.at(ii);
2482
2483 if (p.type == Object::DynamicProperty::Alias) {
2484 if (mode == ResolveAliases) {
2485 ((QDeclarativeVMEMetaData *)dynamicData.data())->aliasCount++;
2486 COMPILE_CHECK(compileAlias(builder, dynamicData, obj, p));
2487 } else {
2488 // Need a fake signal so that the metaobject remains consistent across
2489 // the resolve and non-resolve alias runs
2490 builder.addSignal(p.name + "Changed()");
2491 }
2492 }
2493 }
2494
2495 for (int ii = 0; ii < obj->dynamicSignals.count(); ++ii) {
2496 const Object::DynamicSignal &s = obj->dynamicSignals.at(ii);
2497 QByteArray sig(s.name + '(');
2498 for (int jj = 0; jj < s.parameterTypes.count(); ++jj) {
2499 if (jj) sig.append(',');
2500 sig.append(s.parameterTypes.at(jj));
2501 }
2502 sig.append(')');
2503 QMetaMethodBuilder b = builder.addSignal(sig);
2504 b.setParameterNames(s.parameterNames);
2505 ((QDeclarativeVMEMetaData *)dynamicData.data())->signalCount++;
2506 }
2507
2508 QStringList funcScripts;
2509
2510 for (int ii = 0; ii < obj->dynamicSlots.count(); ++ii) {
2511 Object::DynamicSlot &s = obj->dynamicSlots[ii];
2512 QByteArray sig(s.name + '(');
2513 QString funcScript(QLatin1String("(function ") + s.name + QLatin1Char('('));
2514
2515 for (int jj = 0; jj < s.parameterNames.count(); ++jj) {
2516 if (jj) {
2517 sig.append(',');
2518 funcScript.append(QLatin1Char(','));
2519 }
2520 funcScript.append(QLatin1String(s.parameterNames.at(jj)));
2521 sig.append("QVariant");
2522 }
2523 sig.append(')');
2524 funcScript.append(QLatin1Char(')'));
2525 funcScript.append(s.body);
2526 funcScript.append(QLatin1Char(')'));
2527 funcScripts << funcScript;
2528
2529 QMetaMethodBuilder b = builder.addSlot(sig);
2530 b.setReturnType("QVariant");
2531 b.setParameterNames(s.parameterNames);
2532
2533 ((QDeclarativeVMEMetaData *)dynamicData.data())->methodCount++;
2534 QDeclarativeVMEMetaData::MethodData methodData =
2535 { s.parameterNames.count(), 0, funcScript.length(), s.location.start.line };
2536
2537 dynamicData.append((char *)&methodData, sizeof(methodData));
2538 }
2539
2540 for (int ii = 0; ii < obj->dynamicSlots.count(); ++ii) {
2541 const QString &funcScript = funcScripts.at(ii);
2542 QDeclarativeVMEMetaData::MethodData *data =
2543 ((QDeclarativeVMEMetaData *)dynamicData.data())->methodData() + ii;
2544
2545 data->bodyOffset = dynamicData.size();
2546
2547 dynamicData.append((const char *)funcScript.constData(),
2548 (funcScript.length() * sizeof(QChar)));
2549 }
2550
2551 obj->metadata = builder.toRelocatableData();
2552 builder.fromRelocatableData(&obj->extObject, obj->metatype, obj->metadata);
2553
2554 if (mode == IgnoreAliases && hasAlias)
2555 compileState.aliasingObjects << obj;
2556
2557 obj->synthdata = dynamicData;
2558
2559 return true;
2560}
2561
2562bool QDeclarativeCompiler::checkValidId(QDeclarativeParser::Value *v, const QString &val)
2563{
2564 if (val.isEmpty())
2565 COMPILE_EXCEPTION(v, tr( "Invalid empty ID"));
2566
2567 if (val.at(0).isLetter() && !val.at(0).isLower())
2568 COMPILE_EXCEPTION(v, tr( "IDs cannot start with an uppercase letter"));
2569
2570 QChar u(QLatin1Char('_'));
2571 for (int ii = 0; ii < val.count(); ++ii) {
2572
2573 if (ii == 0 && !val.at(ii).isLetter() && val.at(ii) != u) {
2574 COMPILE_EXCEPTION(v, tr( "IDs must start with a letter or underscore"));
2575 } else if (ii != 0 && !val.at(ii).isLetterOrNumber() && val.at(ii) != u) {
2576 COMPILE_EXCEPTION(v, tr( "IDs must contain only letters, numbers, and underscores"));
2577 }
2578
2579 }
2580
2581 if (enginePrivate->globalClass->illegalNames().contains(val))
2582 COMPILE_EXCEPTION(v, tr( "ID illegally masks global JavaScript property"));
2583
2584 return true;
2585}
2586
2587#include <qdeclarativejsparser_p.h>
2588
2589static QStringList astNodeToStringList(QDeclarativeJS::AST::Node *node)
2590{
2591 if (node->kind == QDeclarativeJS::AST::Node::Kind_IdentifierExpression) {
2592 QString name =
2593 static_cast<QDeclarativeJS::AST::IdentifierExpression *>(node)->name->asString();
2594 return QStringList() << name;
2595 } else if (node->kind == QDeclarativeJS::AST::Node::Kind_FieldMemberExpression) {
2596 QDeclarativeJS::AST::FieldMemberExpression *expr = static_cast<QDeclarativeJS::AST::FieldMemberExpression *>(node);
2597
2598 QStringList rv = astNodeToStringList(expr->base);
2599 if (rv.isEmpty())
2600 return rv;
2601 rv.append(expr->name->asString());
2602 return rv;
2603 }
2604 return QStringList();
2605}
2606
2607bool QDeclarativeCompiler::compileAlias(QMetaObjectBuilder &builder,
2608 QByteArray &data,
2609 Object *obj,
2610 const Object::DynamicProperty &prop)
2611{
2612 if (!prop.defaultValue)
2613 COMPILE_EXCEPTION(obj, tr("No property alias location"));
2614
2615 if (prop.defaultValue->values.count() != 1 ||
2616 prop.defaultValue->values.at(0)->object ||
2617 !prop.defaultValue->values.at(0)->value.isScript())
2618 COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location"));
2619
2620 QDeclarativeJS::AST::Node *node = prop.defaultValue->values.at(0)->value.asAST();
2621 if (!node)
2622 COMPILE_EXCEPTION(obj, tr("No property alias location")); // ### Can this happen?
2623
2624 QStringList alias = astNodeToStringList(node);
2625
2626 if (alias.count() < 1 || alias.count() > 3)
2627 COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
2628
2629 if (!compileState.ids.contains(alias.at(0)))
2630 COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias reference. Unable to find id \"%1\"").arg(alias.at(0)));
2631
2632 Object *idObject = compileState.ids[alias.at(0)];
2633
2634 QByteArray typeName;
2635
2636 int propIdx = -1;
2637 int flags = 0;
2638 bool writable = false;
2639 if (alias.count() == 2 || alias.count() == 3) {
2640 propIdx = idObject->metaObject()->indexOfProperty(alias.at(1).toUtf8().constData());
2641
2642 if (-1 == propIdx) {
2643 COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location"));
2644 } else if (propIdx > 0xFFFF) {
2645 COMPILE_EXCEPTION(prop.defaultValue, tr("Alias property exceeds alias bounds"));
2646 }
2647
2648 QMetaProperty aliasProperty = idObject->metaObject()->property(propIdx);
2649 if (!aliasProperty.isScriptable())
2650 COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location"));
2651
2652 writable = aliasProperty.isWritable();
2653
2654 if (alias.count() == 3) {
2655 QDeclarativeValueType *valueType = enginePrivate->valueTypes[aliasProperty.type()];
2656 if (!valueType)
2657 COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location"));
2658
2659 propIdx |= ((unsigned int)aliasProperty.type()) << 24;
2660
2661 int valueTypeIndex = valueType->metaObject()->indexOfProperty(alias.at(2).toUtf8().constData());
2662 if (valueTypeIndex == -1)
2663 COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location"));
2664 Q_ASSERT(valueTypeIndex <= 0xFF);
2665
2666 aliasProperty = valueType->metaObject()->property(valueTypeIndex);
2667 propIdx |= (valueTypeIndex << 16);
2668 }
2669
2670 if (aliasProperty.isEnumType())
2671 typeName = "int"; // Avoid introducing a dependency on the aliased metaobject
2672 else
2673 typeName = aliasProperty.typeName();
2674 } else {
2675 typeName = idObject->metaObject()->className();
2676
2677 //use the base type since it has been registered with metatype system
2678 int index = typeName.indexOf("_QML_");
2679 if (index != -1) {
2680 typeName = typeName.left(index);
2681 } else {
2682 index = typeName.indexOf("_QMLTYPE_");
2683 const QMetaObject *mo = idObject->metaObject();
2684 while (index != -1 && mo) {
2685 typeName = mo->superClass()->className();
2686 index = typeName.indexOf("_QMLTYPE_");
2687 mo = mo->superClass();
2688 }
2689 }
2690
2691 typeName += '*';
2692 }
2693
2694 if (typeName.endsWith('*'))
2695 flags |= QML_ALIAS_FLAG_PTR;
2696
2697 data.append((const char *)&idObject->idIndex, sizeof(idObject->idIndex));
2698 data.append((const char *)&propIdx, sizeof(propIdx));
2699 data.append((const char *)&flags, sizeof(flags));
2700
2701 builder.addSignal(prop.name + "Changed()");
2702 QMetaPropertyBuilder propBuilder =
2703 builder.addProperty(prop.name, typeName.constData(), builder.methodCount() - 1);
2704 propBuilder.setWritable(writable);
2705 return true;
2706}
2707
2708bool QDeclarativeCompiler::buildBinding(QDeclarativeParser::Value *value,
2709 QDeclarativeParser::Property *prop,
2710 const BindingContext &ctxt)
2711{
2712 Q_ASSERT(prop->index != -1);
2713 Q_ASSERT(prop->parent);
2714 Q_ASSERT(prop->parent->metaObject());
2715
2716 QMetaProperty mp = prop->parent->metaObject()->property(prop->index);
2717 if (!mp.isWritable() && !QDeclarativeMetaType::isList(prop->type))
2718 COMPILE_EXCEPTION(prop, tr("Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop->name)));
2719
2720 BindingReference reference;
2721 reference.expression = value->value;
2722 reference.property = prop;
2723 reference.value = value;
2724 reference.bindingContext = ctxt;
2725 addBindingReference(reference);
2726
2727 return true;
2728}
2729
2730void QDeclarativeCompiler::genBindingAssignment(QDeclarativeParser::Value *binding,
2731 QDeclarativeParser::Property *prop,
2732 QDeclarativeParser::Object *obj,
2733 QDeclarativeParser::Property *valueTypeProperty)
2734{
2735 Q_UNUSED(obj);
2736 Q_ASSERT(compileState.bindings.contains(binding));
2737
2738 const BindingReference &ref = compileState.bindings.value(binding);
2739 if (ref.dataType == BindingReference::Experimental) {
2740 QDeclarativeInstruction store;
2741 store.type = QDeclarativeInstruction::StoreCompiledBinding;
2742 store.assignBinding.value = ref.compiledIndex;
2743 store.assignBinding.context = ref.bindingContext.stack;
2744 store.assignBinding.owner = ref.bindingContext.owner;
2745 if (valueTypeProperty)
2746 store.assignBinding.property = (valueTypeProperty->index & 0xFFFF) |
2747 ((valueTypeProperty->type & 0xFF)) << 16 |
2748 ((prop->index & 0xFF) << 24);
2749 else
2750 store.assignBinding.property = prop->index;
2751 store.line = binding->location.start.line;
2752 output->bytecode << store;
2753 return;
2754 }
2755
2756 QDeclarativeInstruction store;
2757 if (!prop->isAlias)
2758 store.type = QDeclarativeInstruction::StoreBinding;
2759 else
2760 store.type = QDeclarativeInstruction::StoreBindingOnAlias;
2761 store.assignBinding.value = output->indexForByteArray(ref.compiledData);
2762 store.assignBinding.context = ref.bindingContext.stack;
2763 store.assignBinding.owner = ref.bindingContext.owner;
2764 store.line = binding->location.start.line;
2765
2766 Q_ASSERT(ref.bindingContext.owner == 0 ||
2767 (ref.bindingContext.owner != 0 && valueTypeProperty));
2768 if (ref.bindingContext.owner) {
2769 store.assignBinding.property = genValueTypeData(prop, valueTypeProperty);
2770 } else {
2771 store.assignBinding.property = genPropertyData(prop);
2772 }
2773
2774 output->bytecode << store;
2775}
2776
2777int QDeclarativeCompiler::genContextCache()
2778{
2779 if (compileState.ids.count() == 0)
2780 return -1;
2781
2782 QDeclarativeIntegerCache *cache = new QDeclarativeIntegerCache(engine);
2783
2784 for (QHash<QString, QDeclarativeParser::Object *>::ConstIterator iter = compileState.ids.begin();
2785 iter != compileState.ids.end();
2786 ++iter)
2787 cache->add(iter.key(), (*iter)->idIndex);
2788
2789 output->contextCaches.append(cache);
2790 return output->contextCaches.count() - 1;
2791}
2792
2793int QDeclarativeCompiler::genValueTypeData(QDeclarativeParser::Property *valueTypeProp,
2794 QDeclarativeParser::Property *prop)
2795{
2796 QByteArray data =
2797 QDeclarativePropertyPrivate::saveValueType(prop->parent->metaObject(), prop->index,
2798 enginePrivate->valueTypes[prop->type]->metaObject(),
2799 valueTypeProp->index);
2800// valueTypeProp->index, valueTypeProp->type);
2801
2802 return output->indexForByteArray(data);
2803}
2804
2805int QDeclarativeCompiler::genPropertyData(QDeclarativeParser::Property *prop)
2806{
2807 return output->indexForByteArray(QDeclarativePropertyPrivate::saveProperty(prop->parent->metaObject(), prop->index));
2808}
2809
2810bool QDeclarativeCompiler::completeComponentBuild()
2811{
2812 componentStat.ids = compileState.ids.count();
2813
2814 for (int ii = 0; ii < compileState.aliasingObjects.count(); ++ii) {
2815 Object *aliasObject = compileState.aliasingObjects.at(ii);
2816 COMPILE_CHECK(buildDynamicMeta(aliasObject, ResolveAliases));
2817 }
2818
2819 QDeclarativeBindingCompiler::Expression expr;
2820 expr.component = compileState.root;
2821 expr.ids = compileState.ids;
2822
2823 QDeclarativeBindingCompiler bindingCompiler;
2824
2825 for (QHash<QDeclarativeParser::Value*,BindingReference>::Iterator iter = compileState.bindings.begin();
2826 iter != compileState.bindings.end(); ++iter) {
2827
2828 BindingReference &binding = *iter;
2829
2830 expr.context = binding.bindingContext.object;
2831 expr.property = binding.property;
2832 expr.expression = binding.expression;
2833 expr.imports = unit->imports();
2834
2835 // ### We don't currently optimize for bindings on alias's - because
2836 // of the solution to QTBUG-13719
2837 if (!binding.property->isAlias) {
2838 int index = bindingCompiler.compile(expr, enginePrivate);
2839 if (index != -1) {
2840 binding.dataType = BindingReference::Experimental;
2841 binding.compiledIndex = index;
2842 componentStat.optimizedBindings.append(iter.key()->location);
2843 continue;
2844 }
2845 }
2846
2847 binding.dataType = BindingReference::QtScript;
2848
2849 // Pre-rewrite the expression
2850 QString expression = binding.expression.asScript();
2851
2852 // ### Optimize
2853 QDeclarativeRewrite::SharedBindingTester sharableTest;
2854 bool isSharable = sharableTest.isSharable(expression);
2855
2856 QDeclarativeRewrite::RewriteBinding rewriteBinding;
2857 rewriteBinding.setName('$'+binding.property->name);
2858 expression = rewriteBinding(expression);
2859
2860 quint32 length = expression.length();
2861 quint32 pc;
2862
2863 if (isSharable) {
2864 pc = output->cachedClosures.count();
2865 pc |= 0x80000000;
2866 output->cachedClosures.append(0);
2867 } else {
2868 pc = output->cachedPrograms.length();
2869 output->cachedPrograms.append(0);
2870 }
2871
2872 binding.compiledData =
2873 QByteArray((const char *)&pc, sizeof(quint32)) +
2874 QByteArray((const char *)&length, sizeof(quint32)) +
2875 QByteArray((const char *)expression.constData(),
2876 expression.length() * sizeof(QChar));
2877
2878 componentStat.scriptBindings.append(iter.key()->location);
2879 }
2880
2881 if (bindingCompiler.isValid()) {
2882 compileState.compiledBindingData = bindingCompiler.program();
2883 if (bindingsDump())
2884 QDeclarativeBindingCompiler::dump(compileState.compiledBindingData);
2885 }
2886
2887 saveComponentState();
2888
2889 return true;
2890}
2891
2892void QDeclarativeCompiler::dumpStats()
2893{
2894 qWarning().nospace() << "QML Document: " << output->url.toString();
2895 for (int ii = 0; ii < savedComponentStats.count(); ++ii) {
2896 const ComponentStat &stat = savedComponentStats.at(ii);
2897 qWarning().nospace() << " Component Line " << stat.lineNumber;
2898 qWarning().nospace() << " Total Objects: " << stat.objects;
2899 qWarning().nospace() << " IDs Used: " << stat.ids;
2900 qWarning().nospace() << " Optimized Bindings: " << stat.optimizedBindings.count();
2901
2902 {
2903 QByteArray output;
2904 for (int ii = 0; ii < stat.optimizedBindings.count(); ++ii) {
2905 if (0 == (ii % 10)) {
2906 if (ii) output.append("\n");
2907 output.append(" ");
2908 }
2909
2910 output.append("(");
2911 output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.line));
2912 output.append(":");
2913 output.append(QByteArray::number(stat.optimizedBindings.at(ii).start.column));
2914 output.append(") ");
2915 }
2916 if (!output.isEmpty())
2917 qWarning().nospace() << output.constData();
2918 }
2919
2920 qWarning().nospace() << " QScript Bindings: " << stat.scriptBindings.count();
2921 {
2922 QByteArray output;
2923 for (int ii = 0; ii < stat.scriptBindings.count(); ++ii) {
2924 if (0 == (ii % 10)) {
2925 if (ii) output.append("\n");
2926 output.append(" ");
2927 }
2928
2929 output.append("(");
2930 output.append(QByteArray::number(stat.scriptBindings.at(ii).start.line));
2931 output.append(":");
2932 output.append(QByteArray::number(stat.scriptBindings.at(ii).start.column));
2933 output.append(") ");
2934 }
2935 if (!output.isEmpty())
2936 qWarning().nospace() << output.constData();
2937 }
2938 }
2939}
2940
2941/*!
2942 Returns true if from can be assigned to a (QObject) property of type
2943 to.
2944*/
2945bool QDeclarativeCompiler::canCoerce(int to, QDeclarativeParser::Object *from)
2946{
2947 const QMetaObject *toMo =
2948 enginePrivate->rawMetaObjectForType(to);
2949 const QMetaObject *fromMo = from->metaObject();
2950
2951 while (fromMo) {
2952 if (QDeclarativePropertyPrivate::equal(fromMo, toMo))
2953 return true;
2954 fromMo = fromMo->superClass();
2955 }
2956 return false;
2957}
2958
2959QDeclarativeType *QDeclarativeCompiler::toQmlType(QDeclarativeParser::Object *from)
2960{
2961 // ### Optimize
2962 const QMetaObject *mo = from->metatype;
2963 QDeclarativeType *type = 0;
2964 while (!type && mo) {
2965 type = QDeclarativeMetaType::qmlType(mo);
2966 mo = mo->superClass();
2967 }
2968 return type;
2969}
2970
2971QStringList QDeclarativeCompiler::deferredProperties(QDeclarativeParser::Object *obj)
2972{
2973 const QMetaObject *mo = obj->metatype;
2974
2975 int idx = mo->indexOfClassInfo("DeferredPropertyNames");
2976 if (idx == -1)
2977 return QStringList();
2978
2979 QMetaClassInfo classInfo = mo->classInfo(idx);
2980 QStringList rv = QString::fromUtf8(classInfo.value()).split(QLatin1Char(','));
2981 return rv;
2982}
2983
2984QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.