Fix lookups of enums in singletons
[qt:qtdeclarative.git] / src / qml / compiler / qqmlcodegenerator.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qqmlcodegenerator_p.h"
43
44 #include <private/qv4compileddata_p.h>
45 #include <private/qqmljsparser_p.h>
46 #include <private/qqmljslexer_p.h>
47 #include <private/qqmlcompiler_p.h>
48 #include <private/qqmlglobal_p.h>
49 #include <QCoreApplication>
50
51 #ifdef CONST
52 #undef CONST
53 #endif
54
55 QT_USE_NAMESPACE
56
57 DEFINE_BOOL_CONFIG_OPTION(lookupHints, QML_LOOKUP_HINTS);
58
59 using namespace QtQml;
60
61 #define COMPILE_EXCEPTION(location, desc) \
62     { \
63         recordError(location, desc); \
64         return false; \
65     }
66
67 void QmlObject::dump(DebugStream &out)
68 {
69     out << inheritedTypeNameIndex << " {" << endl;
70     out.indent++;
71
72     out.indent--;
73     out << "}" << endl;
74 }
75
76 QStringList Signal::parameterStringList(const QStringList &stringPool) const
77 {
78     QStringList result;
79     result.reserve(parameters->count);
80     for (SignalParameter *param = parameters->first; param; param = param->next)
81         result << stringPool.at(param->nameIndex);
82     return result;
83 }
84
85 QQmlCodeGenerator::QQmlCodeGenerator()
86     : _object(0)
87     , jsGenerator(0)
88 {
89 }
90
91 bool QQmlCodeGenerator::generateFromQml(const QString &code, const QUrl &url, const QString &urlString, ParsedQML *output)
92 {
93     this->url = url;
94     AST::UiProgram *program = 0;
95     {
96         QQmlJS::Lexer lexer(&output->jsParserEngine);
97         lexer.setCode(code, /*line = */ 1);
98
99         QQmlJS::Parser parser(&output->jsParserEngine);
100
101         if (! parser.parse() || !parser.diagnosticMessages().isEmpty()) {
102
103             // Extract errors from the parser
104             foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) {
105
106                 if (m.isWarning()) {
107                     qWarning("%s:%d : %s", qPrintable(urlString), m.loc.startLine, qPrintable(m.message));
108                     continue;
109                 }
110
111                 QQmlError error;
112                 error.setUrl(url);
113                 error.setDescription(m.message);
114                 error.setLine(m.loc.startLine);
115                 error.setColumn(m.loc.startColumn);
116                 errors << error;
117             }
118             return false;
119         }
120         program = parser.ast();
121         Q_ASSERT(program);
122     }
123
124     output->code = code;
125     output->program = program;
126
127     qSwap(_imports, output->imports);
128     qSwap(_pragmas, output->pragmas);
129     qSwap(_objects, output->objects);
130     qSwap(_functions, output->functions);
131     qSwap(_typeReferences, output->typeReferences);
132     this->pool = output->jsParserEngine.pool();
133     this->jsGenerator = &output->jsGenerator;
134
135     emptyStringIndex = registerString(QString());
136
137     sourceCode = code;
138
139     accept(program->headers);
140
141     if (program->members->next) {
142         QQmlError error;
143         error.setDescription(QCoreApplication::translate("QQmlParser", "Unexpected object definition"));
144         AST::SourceLocation loc = program->members->next->firstSourceLocation();
145         error.setLine(loc.startLine);
146         error.setColumn(loc.startColumn);
147         errors << error;
148         return false;
149     }
150
151     AST::UiObjectDefinition *rootObject = AST::cast<AST::UiObjectDefinition*>(program->members->member);
152     Q_ASSERT(rootObject);
153     output->indexOfRootObject = defineQMLObject(rootObject);
154
155     collectTypeReferences();
156
157     qSwap(_imports, output->imports);
158     qSwap(_pragmas, output->pragmas);
159     qSwap(_objects, output->objects);
160     qSwap(_functions, output->functions);
161     qSwap(_typeReferences, output->typeReferences);
162     return errors.isEmpty();
163 }
164
165 bool QQmlCodeGenerator::isSignalPropertyName(const QString &name)
166 {
167     if (name.length() < 3) return false;
168     if (!name.startsWith(QStringLiteral("on"))) return false;
169     int ns = name.length();
170     for (int i = 2; i < ns; ++i) {
171         const QChar curr = name.at(i);
172         if (curr.unicode() == '_') continue;
173         if (curr.isUpper()) return true;
174         return false;
175     }
176     return false; // consists solely of underscores - invalid.
177 }
178
179 bool QQmlCodeGenerator::visit(AST::UiArrayMemberList *ast)
180 {
181     return AST::Visitor::visit(ast);
182 }
183
184 bool QQmlCodeGenerator::visit(AST::UiProgram *)
185 {
186     Q_ASSERT(!"should not happen");
187     return false;
188 }
189
190 bool QQmlCodeGenerator::visit(AST::UiObjectDefinition *node)
191 {
192     // The grammar can't distinguish between two different definitions here:
193     //     Item { ... }
194     // versus
195     //     font { ... }
196     // The former is a new binding with no property name and "Item" as type name,
197     // and the latter is a binding to the font property with no type name but
198     // only initializer.
199
200     AST::UiQualifiedId *lastId = node->qualifiedTypeNameId;
201     while (lastId->next)
202         lastId = lastId->next;
203     bool isType = lastId->name.unicode()->isUpper();
204     if (isType) {
205         int idx = defineQMLObject(node);
206         appendBinding(node->qualifiedTypeNameId->identifierToken, emptyStringIndex, idx);
207     } else {
208         int idx = defineQMLObject(/*qualfied type name id*/0, node->initializer);
209         appendBinding(node->qualifiedTypeNameId, idx);
210     }
211     return false;
212 }
213
214 bool QQmlCodeGenerator::visit(AST::UiObjectBinding *node)
215 {
216     int idx = defineQMLObject(node->qualifiedTypeNameId, node->initializer);
217     appendBinding(node->qualifiedId, idx);
218     return false;
219 }
220
221 bool QQmlCodeGenerator::visit(AST::UiScriptBinding *node)
222 {
223     appendBinding(node->qualifiedId, node->statement);
224     return false;
225 }
226
227 bool QQmlCodeGenerator::visit(AST::UiArrayBinding *node)
228 {
229     QmlObject *object = 0;
230     AST::UiQualifiedId *name = node->qualifiedId;
231     if (!resolveQualifiedId(&name, &object))
232         return false;
233
234     qSwap(_object, object);
235
236     AST::UiArrayMemberList *member = node->members;
237     while (member) {
238         AST::UiObjectDefinition *def = AST::cast<AST::UiObjectDefinition*>(member->member);
239
240         int idx = defineQMLObject(def);
241         appendBinding(name->identifierToken, registerString(name->name.toString()), idx, /*isListItem*/ true);
242
243         member = member->next;
244     }
245
246     qSwap(_object, object);
247     return false;
248 }
249
250 bool QQmlCodeGenerator::visit(AST::UiHeaderItemList *list)
251 {
252     return AST::Visitor::visit(list);
253 }
254
255 bool QQmlCodeGenerator::visit(AST::UiObjectInitializer *ast)
256 {
257     return AST::Visitor::visit(ast);
258 }
259
260 bool QQmlCodeGenerator::visit(AST::UiObjectMemberList *ast)
261 {
262     return AST::Visitor::visit(ast);
263 }
264
265 bool QQmlCodeGenerator::visit(AST::UiParameterList *ast)
266 {
267     return AST::Visitor::visit(ast);
268 }
269
270 bool QQmlCodeGenerator::visit(AST::UiQualifiedId *id)
271 {
272     return AST::Visitor::visit(id);
273 }
274
275 void QQmlCodeGenerator::accept(AST::Node *node)
276 {
277     AST::Node::acceptChild(node, this);
278 }
279
280 bool QQmlCodeGenerator::sanityCheckFunctionNames()
281 {
282     QSet<QString> functionNames;
283     for (Function *f = _object->functions->first; f; f = f->next) {
284         AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(_functions.at(f->index).node);
285         Q_ASSERT(function);
286         QString name = function->name.toString();
287         if (functionNames.contains(name))
288             COMPILE_EXCEPTION(function->identifierToken, tr("Duplicate method name"));
289         functionNames.insert(name);
290         if (_signalNames.contains(name))
291             COMPILE_EXCEPTION(function->identifierToken, tr("Duplicate method name"));
292
293         if (name.at(0).isUpper())
294             COMPILE_EXCEPTION(function->identifierToken, tr("Method names cannot begin with an upper case letter"));
295 #if 0 // ###
296         if (enginePrivate->v8engine()->illegalNames().contains(currSlot.name.toString()))
297             COMPILE_EXCEPTION(&currSlot, tr("Illegal method name"));
298 #endif
299     }
300     return true;
301 }
302
303 int QQmlCodeGenerator::defineQMLObject(AST::UiQualifiedId *qualifiedTypeNameId, AST::UiObjectInitializer *initializer)
304 {
305     QmlObject *obj = New<QmlObject>();
306     _objects.append(obj);
307     const int objectIndex = _objects.size() - 1;
308     qSwap(_object, obj);
309
310     _object->inheritedTypeNameIndex = registerString(asString(qualifiedTypeNameId));
311
312     AST::SourceLocation loc;
313     if (qualifiedTypeNameId)
314         loc = qualifiedTypeNameId->firstSourceLocation();
315     _object->location.line = loc.startLine;
316     _object->location.column = loc.startColumn;
317
318     _object->idIndex = emptyStringIndex;
319     _object->indexOfDefaultProperty = -1;
320     _object->properties = New<PoolList<QmlProperty> >();
321     _object->qmlSignals = New<PoolList<Signal> >();
322     _object->bindings = New<PoolList<Binding> >();
323     _object->functions = New<PoolList<Function> >();
324
325     QSet<QString> propertyNames;
326     qSwap(_propertyNames, propertyNames);
327     QSet<QString> signalNames;
328     qSwap(_signalNames, signalNames);
329
330     accept(initializer);
331
332     sanityCheckFunctionNames();
333
334     qSwap(_propertyNames, propertyNames);
335     qSwap(_signalNames, signalNames);
336     qSwap(_object, obj);
337     return objectIndex;
338 }
339
340 bool QQmlCodeGenerator::visit(AST::UiImport *node)
341 {
342     QString uri;
343     QV4::CompiledData::Import *import = New<QV4::CompiledData::Import>();
344
345     if (!node->fileName.isNull()) {
346         uri = node->fileName.toString();
347
348         if (uri.endsWith(QLatin1String(".js"))) {
349             import->type = QV4::CompiledData::Import::ImportScript;
350         } else {
351             import->type = QV4::CompiledData::Import::ImportFile;
352         }
353     } else {
354         import->type = QV4::CompiledData::Import::ImportLibrary;
355         uri = asString(node->importUri);
356     }
357
358     import->qualifierIndex = emptyStringIndex;
359
360     // Qualifier
361     if (!node->importId.isNull()) {
362         QString qualifier = node->importId.toString();
363         if (!qualifier.at(0).isUpper()) {
364             QQmlError error;
365             error.setDescription(QCoreApplication::translate("QQmlParser","Invalid import qualifier ID"));
366             error.setLine(node->importIdToken.startLine);
367             error.setColumn(node->importIdToken.startColumn);
368             errors << error;
369             return false;
370         }
371         if (qualifier == QLatin1String("Qt")) {
372             QQmlError error;
373             error.setDescription(QCoreApplication::translate("QQmlParser","Reserved name \"Qt\" cannot be used as an qualifier"));
374             error.setLine(node->importIdToken.startLine);
375             error.setColumn(node->importIdToken.startColumn);
376             errors << error;
377             return false;
378         }
379         import->qualifierIndex = registerString(qualifier);
380
381         // Check for script qualifier clashes
382         bool isScript = import->type == QV4::CompiledData::Import::ImportScript;
383         for (int ii = 0; ii < _imports.count(); ++ii) {
384             QV4::CompiledData::Import *other = _imports.at(ii);
385             bool otherIsScript = other->type == QV4::CompiledData::Import::ImportScript;
386
387             if ((isScript || otherIsScript) && qualifier == jsGenerator->strings.at(other->qualifierIndex)) {
388                 QQmlError error;
389                 error.setDescription(QCoreApplication::translate("QQmlParser","Script import qualifiers must be unique."));
390                 error.setLine(node->importIdToken.startLine);
391                 error.setColumn(node->importIdToken.startColumn);
392                 errors << error;
393                 return false;
394             }
395         }
396
397     } else if (import->type == QV4::CompiledData::Import::ImportScript) {
398         QQmlError error;
399         error.setDescription(QCoreApplication::translate("QQmlParser","Script import requires a qualifier"));
400         error.setLine(node->fileNameToken.startLine);
401         error.setColumn(node->fileNameToken.startColumn);
402         errors << error;
403         return false;
404     }
405
406     if (node->versionToken.isValid()) {
407         extractVersion(textRefAt(node->versionToken), &import->majorVersion, &import->minorVersion);
408     } else if (import->type == QV4::CompiledData::Import::ImportLibrary) {
409         QQmlError error;
410         error.setDescription(QCoreApplication::translate("QQmlParser","Library import requires a version"));
411         error.setLine(node->importIdToken.startLine);
412         error.setColumn(node->importIdToken.startColumn);
413         errors << error;
414         return false;
415     } else {
416         // For backward compatibility in how the imports are loaded we
417         // must otherwise initialize the major and minor version to -1.
418         import->majorVersion = -1;
419         import->minorVersion = -1;
420     }
421
422     import->location.line = node->importToken.startLine;
423     import->location.column = node->importToken.startColumn;
424
425     import->uriIndex = registerString(uri);
426
427     _imports.append(import);
428
429     return false;
430 }
431
432 bool QQmlCodeGenerator::visit(AST::UiPragma *node)
433 {
434     Pragma *pragma = New<Pragma>();
435
436     // For now the only valid pragma is Singleton, so lets validate the input
437     if (!node->pragmaType->name.isNull())
438     {
439         if (QLatin1String("Singleton") == node->pragmaType->name)
440         {
441             pragma->type = Pragma::PragmaSingleton;
442         } else {
443             QQmlError error;
444             error.setDescription(QCoreApplication::translate("QQmlParser","Pragma requires a valid qualifier"));
445             error.setLine(node->pragmaToken.startLine);
446             error.setColumn(node->pragmaToken.startColumn);
447             errors << error;
448             return false;
449         }
450     } else {
451         QQmlError error;
452         error.setDescription(QCoreApplication::translate("QQmlParser","Pragma requires a valid qualifier"));
453         error.setLine(node->pragmaToken.startLine);
454         error.setColumn(node->pragmaToken.startColumn);
455         errors << error;
456         return false;
457     }
458
459     pragma->location.line = node->pragmaToken.startLine;
460     pragma->location.column = node->pragmaToken.startColumn;
461     _pragmas.append(pragma);
462
463     return false;
464 }
465
466 static QStringList astNodeToStringList(QQmlJS::AST::Node *node)
467 {
468     if (node->kind == QQmlJS::AST::Node::Kind_IdentifierExpression) {
469         QString name =
470             static_cast<QQmlJS::AST::IdentifierExpression *>(node)->name.toString();
471         return QStringList() << name;
472     } else if (node->kind == QQmlJS::AST::Node::Kind_FieldMemberExpression) {
473         QQmlJS::AST::FieldMemberExpression *expr = static_cast<QQmlJS::AST::FieldMemberExpression *>(node);
474
475         QStringList rv = astNodeToStringList(expr->base);
476         if (rv.isEmpty())
477             return rv;
478         rv.append(expr->name.toString());
479         return rv;
480     }
481     return QStringList();
482 }
483
484 bool QQmlCodeGenerator::visit(AST::UiPublicMember *node)
485 {
486     static const struct TypeNameToType {
487         const char *name;
488         size_t nameLength;
489         QV4::CompiledData::Property::Type type;
490     } propTypeNameToTypes[] = {
491         { "int", strlen("int"), QV4::CompiledData::Property::Int },
492         { "bool", strlen("bool"), QV4::CompiledData::Property::Bool },
493         { "double", strlen("double"), QV4::CompiledData::Property::Real },
494         { "real", strlen("real"), QV4::CompiledData::Property::Real },
495         { "string", strlen("string"), QV4::CompiledData::Property::String },
496         { "url", strlen("url"), QV4::CompiledData::Property::Url },
497         { "color", strlen("color"), QV4::CompiledData::Property::Color },
498         // Internally QTime, QDate and QDateTime are all supported.
499         // To be more consistent with JavaScript we expose only
500         // QDateTime as it matches closely with the Date JS type.
501         // We also call it "date" to match.
502         // { "time", strlen("time"), Property::Time },
503         // { "date", strlen("date"), Property::Date },
504         { "date", strlen("date"), QV4::CompiledData::Property::DateTime },
505         { "rect", strlen("rect"), QV4::CompiledData::Property::Rect },
506         { "point", strlen("point"), QV4::CompiledData::Property::Point },
507         { "size", strlen("size"), QV4::CompiledData::Property::Size },
508         { "font", strlen("font"), QV4::CompiledData::Property::Font },
509         { "vector2d", strlen("vector2d"), QV4::CompiledData::Property::Vector2D },
510         { "vector3d", strlen("vector3d"), QV4::CompiledData::Property::Vector3D },
511         { "vector4d", strlen("vector4d"), QV4::CompiledData::Property::Vector4D },
512         { "quaternion", strlen("quaternion"), QV4::CompiledData::Property::Quaternion },
513         { "matrix4x4", strlen("matrix4x4"), QV4::CompiledData::Property::Matrix4x4 },
514         { "variant", strlen("variant"), QV4::CompiledData::Property::Variant },
515         { "var", strlen("var"), QV4::CompiledData::Property::Var }
516     };
517     static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) /
518                                                 sizeof(propTypeNameToTypes[0]);
519
520     if (node->type == AST::UiPublicMember::Signal) {
521         Signal *signal = New<Signal>();
522         QString signalName = node->name.toString();
523         signal->nameIndex = registerString(signalName);
524
525         AST::SourceLocation loc = node->firstSourceLocation();
526         signal->location.line = loc.startLine;
527         signal->location.column = loc.startColumn;
528
529         signal->parameters = New<PoolList<SignalParameter> >();
530
531         AST::UiParameterList *p = node->parameters;
532         while (p) {
533             const QStringRef &memberType = p->type;
534
535             if (memberType.isEmpty()) {
536                 QQmlError error;
537                 error.setDescription(QCoreApplication::translate("QQmlParser","Expected parameter type"));
538                 error.setLine(node->typeToken.startLine);
539                 error.setColumn(node->typeToken.startColumn);
540                 errors << error;
541                 return false;
542             }
543
544             const TypeNameToType *type = 0;
545             for (int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) {
546                 const TypeNameToType *t = propTypeNameToTypes + typeIndex;
547                 if (t->nameLength == size_t(memberType.length()) &&
548                     QHashedString::compare(memberType.constData(), t->name, static_cast<int>(t->nameLength))) {
549                     type = t;
550                     break;
551                 }
552             }
553
554             SignalParameter *param = New<SignalParameter>();
555
556             if (!type) {
557                 if (memberType.at(0).isUpper()) {
558                     // Must be a QML object type.
559                     // Lazily determine type during compilation.
560                     param->type = QV4::CompiledData::Property::Custom;
561                     param->customTypeNameIndex = registerString(p->type.toString());
562                 } else {
563                     QQmlError error;
564                     QString errStr = QCoreApplication::translate("QQmlParser","Invalid signal parameter type: ");
565                     errStr.append(memberType.toString());
566                     error.setDescription(errStr);
567                     error.setLine(node->typeToken.startLine);
568                     error.setColumn(node->typeToken.startColumn);
569                     errors << error;
570                     return false;
571                 }
572             } else {
573                 // the parameter is a known basic type
574                 param->type = type->type;
575                 param->customTypeNameIndex = emptyStringIndex;
576             }
577
578             param->nameIndex = registerString(p->name.toString());
579             param->location.line = p->identifierToken.startLine;
580             param->location.column = p->identifierToken.startColumn;
581             signal->parameters->append(param);
582             p = p->next;
583         }
584
585         if (_signalNames.contains(signalName))
586             COMPILE_EXCEPTION(node->identifierToken, tr("Duplicate signal name"));
587         _signalNames.insert(signalName);
588
589         if (signalName.at(0).isUpper())
590             COMPILE_EXCEPTION(node->identifierToken, tr("Signal names cannot begin with an upper case letter"));
591
592 #if 0 // ### cannot access identifier table from separate thread
593         if (enginePrivate->v8engine()->illegalNames().contains(currSig.name.toString()))
594             COMPILE_EXCEPTION(&currSig, tr("Illegal signal name"));
595 #endif
596
597         _object->qmlSignals->append(signal);
598     } else {
599         const QStringRef &memberType = node->memberType;
600         const QStringRef &name = node->name;
601
602         bool typeFound = false;
603         QV4::CompiledData::Property::Type type;
604
605         if ((unsigned)memberType.length() == strlen("alias") &&
606             QHashedString::compare(memberType.constData(), "alias", static_cast<int>(strlen("alias")))) {
607             type = QV4::CompiledData::Property::Alias;
608             typeFound = true;
609         }
610
611         for (int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) {
612             const TypeNameToType *t = propTypeNameToTypes + ii;
613             if (t->nameLength == size_t(memberType.length()) &&
614                 QHashedString::compare(memberType.constData(), t->name, static_cast<int>(t->nameLength))) {
615                 type = t->type;
616                 typeFound = true;
617             }
618         }
619
620         if (!typeFound && memberType.at(0).isUpper()) {
621             const QStringRef &typeModifier = node->typeModifier;
622
623             if (typeModifier.isEmpty()) {
624                 type = QV4::CompiledData::Property::Custom;
625             } else if ((unsigned)typeModifier.length() == strlen("list") &&
626                       QHashedString::compare(typeModifier.constData(), "list", static_cast<int>(strlen("list")))) {
627                 type = QV4::CompiledData::Property::CustomList;
628             } else {
629                 QQmlError error;
630                 error.setDescription(QCoreApplication::translate("QQmlParser","Invalid property type modifier"));
631                 error.setLine(node->typeModifierToken.startLine);
632                 error.setColumn(node->typeModifierToken.startColumn);
633                 errors << error;
634                 return false;
635             }
636             typeFound = true;
637         } else if (!node->typeModifier.isNull()) {
638             QQmlError error;
639             error.setDescription(QCoreApplication::translate("QQmlParser","Unexpected property type modifier"));
640             error.setLine(node->typeModifierToken.startLine);
641             error.setColumn(node->typeModifierToken.startColumn);
642             errors << error;
643             return false;
644         }
645
646         if (!typeFound) {
647             QQmlError error;
648             error.setDescription(QCoreApplication::translate("QQmlParser","Expected property type"));
649             error.setLine(node->typeToken.startLine);
650             error.setColumn(node->typeToken.startColumn);
651             errors << error;
652             return false;
653         }
654
655         QmlProperty *property = New<QmlProperty>();
656         property->flags = 0;
657         if (node->isReadonlyMember)
658             property->flags |= QV4::CompiledData::Property::IsReadOnly;
659         property->type = type;
660         if (type >= QV4::CompiledData::Property::Custom)
661             property->customTypeNameIndex = registerString(memberType.toString());
662         else
663             property->customTypeNameIndex = emptyStringIndex;
664
665         property->nameIndex = registerString(name.toString());
666
667         AST::SourceLocation loc = node->firstSourceLocation();
668         property->location.line = loc.startLine;
669         property->location.column = loc.startColumn;
670
671         property->aliasPropertyValueIndex = emptyStringIndex;
672
673         if (type == QV4::CompiledData::Property::Alias) {
674             if (!node->statement && !node->binding)
675                 COMPILE_EXCEPTION(loc, tr("No property alias location"));
676
677             AST::SourceLocation rhsLoc;
678             if (node->binding)
679                 rhsLoc = node->binding->firstSourceLocation();
680             else if (node->statement)
681                 rhsLoc = node->statement->firstSourceLocation();
682             else
683                 rhsLoc = node->semicolonToken;
684             property->aliasLocation.line = rhsLoc.startLine;
685             property->aliasLocation.column = rhsLoc.startColumn;
686
687             QStringList alias;
688
689             if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement*>(node->statement)) {
690                 alias = astNodeToStringList(stmt->expression);
691                 if (alias.isEmpty()) {
692                     if (isStatementNodeScript(node->statement)) {
693                         COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
694                     } else {
695                         COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias location"));
696                     }
697                 }
698             } else {
699                 COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
700             }
701
702             if (alias.count() < 1 || alias.count() > 3)
703                 COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
704
705              property->aliasIdValueIndex = registerString(alias.first());
706
707              QString propertyValue = alias.value(1);
708              if (alias.count() == 3) {
709                  propertyValue += QLatin1Char('.');
710                  propertyValue += alias.at(2);
711              }
712              property->aliasPropertyValueIndex = registerString(propertyValue);
713         } else if (node->statement)
714             appendBinding(node->identifierToken, property->nameIndex, node->statement);
715
716         _object->properties->append(property);
717
718         if (node->isDefaultMember) {
719             if (_object->indexOfDefaultProperty != -1) {
720                 QQmlError error;
721                 error.setDescription(QCoreApplication::translate("QQmlParser","Duplicate default property"));
722                 error.setLine(node->defaultToken.startLine);
723                 error.setColumn(node->defaultToken.startColumn);
724                 errors << error;
725                 return false;
726             }
727             _object->indexOfDefaultProperty = _object->properties->count - 1;
728         }
729
730         // process QML-like initializers (e.g. property Object o: Object {})
731         AST::Node::accept(node->binding, this);
732     }
733
734     return false;
735 }
736
737 bool QQmlCodeGenerator::visit(AST::UiSourceElement *node)
738 {
739     if (AST::FunctionDeclaration *funDecl = AST::cast<AST::FunctionDeclaration *>(node->sourceElement)) {
740         _functions << funDecl;
741         Function *f = New<Function>();
742         f->index = _functions.size() - 1;
743         _object->functions->append(f);
744     } else {
745         QQmlError error;
746         error.setDescription(QCoreApplication::translate("QQmlParser","JavaScript declaration outside Script element"));
747         error.setLine(node->firstSourceLocation().startLine);
748         error.setColumn(node->firstSourceLocation().startColumn);
749         errors << error;
750     }
751     return false;
752 }
753
754 QString QQmlCodeGenerator::asString(AST::UiQualifiedId *node)
755 {
756     QString s;
757
758     for (AST::UiQualifiedId *it = node; it; it = it->next) {
759         s.append(it->name);
760
761         if (it->next)
762             s.append(QLatin1Char('.'));
763     }
764
765     return s;
766 }
767
768 QStringRef QQmlCodeGenerator::asStringRef(AST::Node *node)
769 {
770     if (!node)
771         return QStringRef();
772
773     return textRefAt(node->firstSourceLocation(), node->lastSourceLocation());
774 }
775
776 void QQmlCodeGenerator::extractVersion(QStringRef string, int *maj, int *min)
777 {
778     *maj = -1; *min = -1;
779
780     if (!string.isEmpty()) {
781
782         int dot = string.indexOf(QLatin1Char('.'));
783
784         if (dot < 0) {
785             *maj = string.toInt();
786             *min = 0;
787         } else {
788             *maj = string.left(dot).toInt();
789             *min = string.mid(dot + 1).toInt();
790         }
791     }
792 }
793
794 QStringRef QQmlCodeGenerator::textRefAt(const AST::SourceLocation &first, const AST::SourceLocation &last) const
795 {
796     return QStringRef(&sourceCode, first.offset, last.offset + last.length - first.offset);
797 }
798
799 void QQmlCodeGenerator::setBindingValue(QV4::CompiledData::Binding *binding, AST::Statement *statement)
800 {
801     binding->type = QV4::CompiledData::Binding::Type_Invalid;
802
803     if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(statement)) {
804         AST::ExpressionNode *expr = stmt->expression;
805         if (AST::StringLiteral *lit = AST::cast<AST::StringLiteral *>(expr)) {
806             binding->type = QV4::CompiledData::Binding::Type_String;
807             binding->stringIndex = registerString(lit->value.toString());
808         } else if (expr->kind == AST::Node::Kind_TrueLiteral) {
809             binding->type = QV4::CompiledData::Binding::Type_Boolean;
810             binding->value.b = true;
811         } else if (expr->kind == AST::Node::Kind_FalseLiteral) {
812             binding->type = QV4::CompiledData::Binding::Type_Boolean;
813             binding->value.b = false;
814         } else if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(expr)) {
815             binding->type = QV4::CompiledData::Binding::Type_Number;
816             binding->value.d = lit->value;
817         } else {
818
819             if (AST::UnaryMinusExpression *unaryMinus = AST::cast<AST::UnaryMinusExpression *>(expr)) {
820                if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(unaryMinus->expression)) {
821                    binding->type = QV4::CompiledData::Binding::Type_Number;
822                    binding->value.d = -lit->value;
823                }
824             }
825         }
826     }
827
828     // Do binding instead
829     if (binding->type == QV4::CompiledData::Binding::Type_Invalid) {
830         binding->type = QV4::CompiledData::Binding::Type_Script;
831         _functions << statement;
832         binding->value.compiledScriptIndex = _functions.size() - 1;
833         binding->stringIndex = registerString(asStringRef(statement).toString());
834     }
835 }
836
837 void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, AST::Statement *value)
838 {
839     QmlObject *object = 0;
840     if (!resolveQualifiedId(&name, &object))
841         return;
842     qSwap(_object, object);
843     appendBinding(name->identifierToken, registerString(name->name.toString()), value);
844     qSwap(_object, object);
845 }
846
847 void QQmlCodeGenerator::appendBinding(AST::UiQualifiedId *name, int objectIndex)
848 {
849     QmlObject *object = 0;
850     if (!resolveQualifiedId(&name, &object))
851         return;
852     qSwap(_object, object);
853     appendBinding(name->identifierToken, registerString(name->name.toString()), objectIndex);
854     qSwap(_object, object);
855 }
856
857 void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, AST::Statement *value)
858 {
859     if (!sanityCheckPropertyName(nameLocation, propertyNameIndex))
860         return;
861
862     if (stringAt(propertyNameIndex) == QStringLiteral("id")) {
863         setId(value);
864         return;
865     }
866
867     Binding *binding = New<Binding>();
868     binding->propertyNameIndex = propertyNameIndex;
869     binding->location.line = nameLocation.startLine;
870     binding->location.column = nameLocation.startColumn;
871     binding->flags = 0;
872     setBindingValue(binding, value);
873     _object->bindings->append(binding);
874 }
875
876 void QQmlCodeGenerator::appendBinding(const AST::SourceLocation &nameLocation, int propertyNameIndex, int objectIndex, bool isListItem)
877 {
878     if (!sanityCheckPropertyName(nameLocation, propertyNameIndex, isListItem))
879         return;
880
881     if (stringAt(propertyNameIndex) == QStringLiteral("id")) {
882         recordError(nameLocation, tr("Invalid component id specification"));
883         return;
884     }
885
886     Binding *binding = New<Binding>();
887     binding->propertyNameIndex = propertyNameIndex;
888     binding->location.line = nameLocation.startLine;
889     binding->location.column = nameLocation.startColumn;
890     binding->flags = 0;
891     binding->type = QV4::CompiledData::Binding::Type_Object;
892     binding->value.objectIndex = objectIndex;
893     _object->bindings->append(binding);
894 }
895
896 bool QQmlCodeGenerator::setId(AST::Statement *value)
897 {
898     AST::SourceLocation loc = value->firstSourceLocation();
899     QStringRef str;
900
901     AST::Node *node = value;
902     if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(node)) {
903         if (AST::StringLiteral *lit = AST::cast<AST::StringLiteral *>(stmt->expression))
904             str = lit->value;
905         else
906             node = stmt->expression;
907     }
908
909     if (str.isEmpty())
910         str = asStringRef(node);
911
912     if (str.isEmpty())
913         COMPILE_EXCEPTION(loc, tr( "Invalid empty ID"));
914
915     QChar ch = str.at(0);
916     if (ch.isLetter() && !ch.isLower())
917         COMPILE_EXCEPTION(loc, tr( "IDs cannot start with an uppercase letter"));
918
919     QChar u(QLatin1Char('_'));
920     if (!ch.isLetter() && ch != u)
921         COMPILE_EXCEPTION(loc, tr( "IDs must start with a letter or underscore"));
922
923     for (int ii = 1; ii < str.count(); ++ii) {
924         ch = str.at(ii);
925         if (!ch.isLetterOrNumber() && ch != u)
926             COMPILE_EXCEPTION(loc, tr( "IDs must contain only letters, numbers, and underscores"));
927     }
928
929 #if 0 // ###
930     if (enginePrivate->v8engine()->illegalNames().contains(str))
931         COMPILE_EXCEPTION(v, tr( "ID illegally masks global JavaScript property"));
932 #endif
933
934     _object->idIndex = registerString(str.toString());
935     _object->locationOfIdProperty.line = loc.startLine;
936     _object->locationOfIdProperty.column = loc.startColumn;
937
938     return true;
939 }
940
941 bool QQmlCodeGenerator::resolveQualifiedId(AST::UiQualifiedId **nameToResolve, QmlObject **object)
942 {
943     AST::UiQualifiedId *name = *nameToResolve;
944
945     if (name->name == QStringLiteral("id") && name->next)
946         COMPILE_EXCEPTION(name->identifierToken, tr( "Invalid use of id property"));
947
948     *object = _object;
949     while (name->next) {
950         Binding *binding = New<Binding>();
951         binding->propertyNameIndex = registerString(name->name.toString());
952         binding->location.line = name->identifierToken.startLine;
953         binding->location.column = name->identifierToken.startColumn;
954         binding->flags = 0;
955
956         if (name->name.unicode()->isUpper())
957             binding->type = QV4::CompiledData::Binding::Type_AttachedProperty;
958         else
959             binding->type = QV4::CompiledData::Binding::Type_GroupProperty;
960
961         int objIndex = defineQMLObject(0, 0);
962         binding->value.objectIndex = objIndex;
963
964         (*object)->bindings->append(binding);
965         *object = _objects[objIndex];
966
967         name = name->next;
968     }
969     *nameToResolve = name;
970     return true;
971 }
972
973 bool QQmlCodeGenerator::sanityCheckPropertyName(const AST::SourceLocation &nameLocation, int nameIndex, bool isListItem)
974 {
975     const QString &name = jsGenerator->strings.at(nameIndex);
976     if (name.isEmpty())
977         return true;
978
979     // List items are implement by multiple bindings to the same name, so allow duplicates.
980     if (!isListItem) {
981         if (_propertyNames.contains(name))
982             COMPILE_EXCEPTION(nameLocation, tr("Duplicate property name"));
983
984         _propertyNames.insert(name);
985     }
986
987     if (name.at(0).isUpper())
988         COMPILE_EXCEPTION(nameLocation, tr("Property names cannot begin with an upper case letter"));
989
990 #if 0 // ### how to check against illegalNames when in separate thread?
991     if (enginePrivate->v8engine()->illegalNames().contains(prop.name.toString())) {
992         COMPILE_EXCEPTION_LOCATION(prop.nameLocation.line,
993                                    prop.nameLocation.column,
994                                    tr("Illegal property name"));
995     }
996 #endif
997
998     return true;
999 }
1000
1001 void QQmlCodeGenerator::recordError(const AST::SourceLocation &location, const QString &description)
1002 {
1003     QQmlError error;
1004     error.setUrl(url);
1005     error.setLine(location.startLine);
1006     error.setColumn(location.startColumn);
1007     error.setDescription(description);
1008     errors << error;
1009 }
1010
1011 void QQmlCodeGenerator::collectTypeReferences()
1012 {
1013     foreach (QmlObject *obj, _objects) {
1014         if (!stringAt(obj->inheritedTypeNameIndex).isEmpty())
1015             _typeReferences.add(obj->inheritedTypeNameIndex, obj->location);
1016
1017         for (QmlProperty *prop = obj->properties->first; prop; prop = prop->next) {
1018             if (prop->type >= QV4::CompiledData::Property::Custom)
1019                 _typeReferences.add(prop->customTypeNameIndex, prop->location);
1020         }
1021
1022         for (Signal *sig = obj->qmlSignals->first; sig; sig = sig->next)
1023             for (SignalParameter *param = sig->parameters->first; param; param = param->next)
1024                 if (!stringAt(param->customTypeNameIndex).isEmpty())
1025                     _typeReferences.add(param->customTypeNameIndex, param->location);
1026
1027         for (Binding *binding = obj->bindings->first; binding; binding = binding->next) {
1028             if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty)
1029                 _typeReferences.add(binding->propertyNameIndex, binding->location);
1030         }
1031     }
1032 }
1033
1034 QQmlScript::LocationSpan QQmlCodeGenerator::location(AST::SourceLocation start, AST::SourceLocation end)
1035 {
1036     QQmlScript::LocationSpan rv;
1037     rv.start.line = start.startLine;
1038     rv.start.column = start.startColumn;
1039     rv.end.line = end.startLine;
1040     rv.end.column = end.startColumn + end.length - 1;
1041     rv.range.offset = start.offset;
1042     rv.range.length = end.offset + end.length - start.offset;
1043     return rv;
1044 }
1045
1046 bool QQmlCodeGenerator::isStatementNodeScript(AST::Statement *statement)
1047 {
1048     if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(statement)) {
1049         AST::ExpressionNode *expr = stmt->expression;
1050         if (AST::cast<AST::StringLiteral *>(expr))
1051             return false;
1052         else if (expr->kind == AST::Node::Kind_TrueLiteral)
1053             return false;
1054         else if (expr->kind == AST::Node::Kind_FalseLiteral)
1055             return false;
1056         else if (AST::cast<AST::NumericLiteral *>(expr))
1057             return false;
1058         else {
1059
1060             if (AST::UnaryMinusExpression *unaryMinus = AST::cast<AST::UnaryMinusExpression *>(expr)) {
1061                if (AST::cast<AST::NumericLiteral *>(unaryMinus->expression)) {
1062                    return false;
1063                }
1064             }
1065         }
1066     }
1067
1068     return true;
1069 }
1070
1071 QV4::CompiledData::QmlUnit *QmlUnitGenerator::generate(ParsedQML &output, const QVector<int> &runtimeFunctionIndices)
1072 {
1073     jsUnitGenerator = &output.jsGenerator;
1074     int unitSize = 0;
1075     QV4::CompiledData::Unit *jsUnit = jsUnitGenerator->generateUnit(&unitSize);
1076
1077     const int importSize = sizeof(QV4::CompiledData::Import) * output.imports.count();
1078     const int objectOffsetTableSize = output.objects.count() * sizeof(quint32);
1079
1080     QHash<QmlObject*, quint32> objectOffsets;
1081
1082     int objectsSize = 0;
1083     foreach (QmlObject *o, output.objects) {
1084         objectOffsets.insert(o, unitSize + importSize + objectOffsetTableSize + objectsSize);
1085         objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functions->count, o->properties->count, o->qmlSignals->count, o->bindings->count);
1086
1087         int signalTableSize = 0;
1088         for (Signal *s = o->qmlSignals->first; s; s = s->next)
1089             signalTableSize += QV4::CompiledData::Signal::calculateSize(s->parameters->count);
1090
1091         objectsSize += signalTableSize;
1092     }
1093
1094     const int totalSize = unitSize + importSize + objectOffsetTableSize + objectsSize;
1095     char *data = (char*)malloc(totalSize);
1096     memcpy(data, jsUnit, unitSize);
1097     free(jsUnit);
1098     jsUnit = 0;
1099
1100     QV4::CompiledData::QmlUnit *qmlUnit = reinterpret_cast<QV4::CompiledData::QmlUnit *>(data);
1101     qmlUnit->header.flags |= QV4::CompiledData::Unit::IsQml;
1102     qmlUnit->offsetToImports = unitSize;
1103     qmlUnit->nImports = output.imports.count();
1104     qmlUnit->offsetToObjects = unitSize + importSize;
1105     qmlUnit->nObjects = output.objects.count();
1106     qmlUnit->indexOfRootObject = output.indexOfRootObject;
1107
1108     // write imports
1109     char *importPtr = data + qmlUnit->offsetToImports;
1110     foreach (QV4::CompiledData::Import *imp, output.imports) {
1111         QV4::CompiledData::Import *importToWrite = reinterpret_cast<QV4::CompiledData::Import*>(importPtr);
1112         *importToWrite = *imp;
1113         importPtr += sizeof(QV4::CompiledData::Import);
1114     }
1115
1116     // write objects
1117     quint32 *objectTable = reinterpret_cast<quint32*>(data + qmlUnit->offsetToObjects);
1118     char *objectPtr = data + qmlUnit->offsetToObjects + objectOffsetTableSize;
1119     foreach (QmlObject *o, output.objects) {
1120         *objectTable++ = objectOffsets.value(o);
1121
1122         QV4::CompiledData::Object *objectToWrite = reinterpret_cast<QV4::CompiledData::Object*>(objectPtr);
1123         objectToWrite->inheritedTypeNameIndex = o->inheritedTypeNameIndex;
1124         objectToWrite->indexOfDefaultProperty = o->indexOfDefaultProperty;
1125         objectToWrite->idIndex = o->idIndex;
1126         objectToWrite->location = o->location;
1127         objectToWrite->locationOfIdProperty = o->locationOfIdProperty;
1128
1129         quint32 nextOffset = sizeof(QV4::CompiledData::Object);
1130
1131         objectToWrite->nFunctions = o->functions->count;
1132         objectToWrite->offsetToFunctions = nextOffset;
1133         nextOffset += objectToWrite->nFunctions * sizeof(quint32);
1134
1135         objectToWrite->nProperties = o->properties->count;
1136         objectToWrite->offsetToProperties = nextOffset;
1137         nextOffset += objectToWrite->nProperties * sizeof(QV4::CompiledData::Property);
1138
1139         objectToWrite->nSignals = o->qmlSignals->count;
1140         objectToWrite->offsetToSignals = nextOffset;
1141         nextOffset += objectToWrite->nSignals * sizeof(quint32);
1142
1143         objectToWrite->nBindings = o->bindings->count;
1144         objectToWrite->offsetToBindings = nextOffset;
1145         nextOffset += objectToWrite->nBindings * sizeof(QV4::CompiledData::Binding);
1146
1147         quint32 *functionsTable = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToFunctions);
1148         for (Function *f = o->functions->first; f; f = f->next)
1149             *functionsTable++ = runtimeFunctionIndices[f->index];
1150
1151         char *propertiesPtr = objectPtr + objectToWrite->offsetToProperties;
1152         for (QmlProperty *p = o->properties->first; p; p = p->next) {
1153             QV4::CompiledData::Property *propertyToWrite = reinterpret_cast<QV4::CompiledData::Property*>(propertiesPtr);
1154             *propertyToWrite = *p;
1155             propertiesPtr += sizeof(QV4::CompiledData::Property);
1156         }
1157
1158         char *bindingPtr = objectPtr + objectToWrite->offsetToBindings;
1159         for (Binding *b = o->bindings->first; b; b = b->next) {
1160             QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast<QV4::CompiledData::Binding*>(bindingPtr);
1161             *bindingToWrite = *b;
1162             if (b->type == QV4::CompiledData::Binding::Type_Script)
1163                 bindingToWrite->value.compiledScriptIndex = runtimeFunctionIndices[b->value.compiledScriptIndex];
1164             bindingPtr += sizeof(QV4::CompiledData::Binding);
1165         }
1166
1167         quint32 *signalOffsetTable = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToSignals);
1168         quint32 signalTableSize = 0;
1169         char *signalPtr = objectPtr + nextOffset;
1170         for (Signal *s = o->qmlSignals->first; s; s = s->next) {
1171             *signalOffsetTable++ = signalPtr - objectPtr;
1172             QV4::CompiledData::Signal *signalToWrite = reinterpret_cast<QV4::CompiledData::Signal*>(signalPtr);
1173
1174             signalToWrite->nameIndex = s->nameIndex;
1175             signalToWrite->location = s->location;
1176             signalToWrite->nParameters = s->parameters->count;
1177
1178             QV4::CompiledData::Parameter *parameterToWrite = reinterpret_cast<QV4::CompiledData::Parameter*>(signalPtr + sizeof(*signalToWrite));
1179             for (SignalParameter *param = s->parameters->first; param; param = param->next, ++parameterToWrite)
1180                 *parameterToWrite = *param;
1181
1182             int size = QV4::CompiledData::Signal::calculateSize(s->parameters->count);
1183             signalTableSize += size;
1184             signalPtr += size;
1185         }
1186
1187         objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functions->count, o->properties->count, o->qmlSignals->count, o->bindings->count);
1188         objectPtr += signalTableSize;
1189     }
1190
1191     // enable flag if we encountered pragma Singleton
1192     foreach (Pragma *p, output.pragmas) {
1193         if (p->type == Pragma::PragmaSingleton) {
1194             qmlUnit->header.flags |= QV4::CompiledData::Unit::IsSingleton;
1195             break;
1196         }
1197     }
1198
1199     return qmlUnit;
1200 }
1201
1202 int QmlUnitGenerator::getStringId(const QString &str) const
1203 {
1204     return jsUnitGenerator->getStringId(str);
1205 }
1206
1207 JSCodeGen::JSCodeGen(const QString &fileName, const QString &sourceCode, V4IR::Module *jsModule, Engine *jsEngine, AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports)
1208     : QQmlJS::Codegen(/*strict mode*/false)
1209     , sourceCode(sourceCode)
1210     , jsEngine(jsEngine)
1211     , qmlRoot(qmlRoot)
1212     , imports(imports)
1213     , _disableAcceleratedLookups(false)
1214     , _contextObject(0)
1215     , _scopeObject(0)
1216     , _contextObjectTemp(-1)
1217     , _scopeObjectTemp(-1)
1218     , _importedScriptsTemp(-1)
1219     , _idArrayTemp(-1)
1220 {
1221     _module = jsModule;
1222     _module->setFileName(fileName);
1223     _fileNameIsUrl = true;
1224 }
1225
1226 void JSCodeGen::beginContextScope(const JSCodeGen::ObjectIdMapping &objectIds, QQmlPropertyCache *contextObject)
1227 {
1228     _idObjects = objectIds;
1229     _contextObject = contextObject;
1230     _scopeObject = 0;
1231 }
1232
1233 void JSCodeGen::beginObjectScope(QQmlPropertyCache *scopeObject)
1234 {
1235     _scopeObject = scopeObject;
1236 }
1237
1238 QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions)
1239 {
1240     QVector<int> runtimeFunctionIndices(functions.size());
1241
1242     ScanFunctions scan(this, sourceCode, GlobalCode);
1243     scan.enterEnvironment(0, QmlBinding);
1244     scan.enterQmlScope(qmlRoot, QStringLiteral("context scope"));
1245     foreach (const CompiledFunctionOrExpression &f, functions) {
1246         Q_ASSERT(f.node != qmlRoot);
1247         AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(f.node);
1248
1249         if (function)
1250             scan.enterQmlFunction(function);
1251         else
1252             scan.enterEnvironment(f.node, QmlBinding);
1253
1254         scan(function ? function->body : f.node);
1255         scan.leaveEnvironment();
1256     }
1257     scan.leaveEnvironment();
1258     scan.leaveEnvironment();
1259
1260     _env = 0;
1261     _function = _module->functions.at(defineFunction(QStringLiteral("context scope"), qmlRoot, 0, 0));
1262
1263     for (int i = 0; i < functions.count(); ++i) {
1264         const CompiledFunctionOrExpression &qmlFunction = functions.at(i);
1265         AST::Node *node = qmlFunction.node;
1266         Q_ASSERT(node != qmlRoot);
1267
1268         AST::FunctionDeclaration *function = AST::cast<AST::FunctionDeclaration*>(node);
1269
1270         QString name;
1271         if (function)
1272             name = function->name.toString();
1273         else if (!qmlFunction.name.isEmpty())
1274             name = qmlFunction.name;
1275         else
1276             name = QStringLiteral("%qml-expression-entry");
1277
1278         AST::SourceElements *body;
1279         if (function)
1280             body = function->body ? function->body->elements : 0;
1281         else {
1282             // Synthesize source elements.
1283             QQmlJS::MemoryPool *pool = jsEngine->pool();
1284
1285             AST::Statement *stmt = node->statementCast();
1286             if (!stmt) {
1287                 Q_ASSERT(node->expressionCast());
1288                 AST::ExpressionNode *expr = node->expressionCast();
1289                 stmt = new (pool) AST::ExpressionStatement(expr);
1290             }
1291             AST::SourceElement *element = new (pool) AST::StatementSourceElement(stmt);
1292             body = new (pool) AST::SourceElements(element);
1293             body = body->finish();
1294         }
1295
1296         _disableAcceleratedLookups = qmlFunction.disableAcceleratedLookups;
1297         int idx = defineFunction(name, node,
1298                                  function ? function->formals : 0,
1299                                  body);
1300         runtimeFunctionIndices[i] = idx;
1301     }
1302
1303     qDeleteAll(_envMap);
1304     _envMap.clear();
1305     return runtimeFunctionIndices;
1306 }
1307
1308 QQmlPropertyData *JSCodeGen::lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name, bool *propertyExistsButForceNameLookup)
1309 {
1310     if (propertyExistsButForceNameLookup)
1311         *propertyExistsButForceNameLookup = false;
1312     QQmlPropertyData *pd = cache->property(name, /*object*/0, /*context*/0);
1313
1314     // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time
1315     if (pd && pd->isFunction()) {
1316         if (propertyExistsButForceNameLookup)
1317             *propertyExistsButForceNameLookup = true;
1318         pd = 0;
1319     }
1320
1321     if (pd && !cache->isAllowedInRevision(pd))
1322         pd = 0;
1323
1324     // Return a copy allocated from our memory pool. Property data pointers can change
1325     // otherwise when the QQmlPropertyCache changes later in the QML type compilation process.
1326     if (pd) {
1327         QQmlPropertyData *copy = pd;
1328         pd = _function->New<QQmlPropertyData>();
1329         *pd = *copy;
1330     }
1331     return pd;
1332 }
1333
1334 static void initMetaObjectResolver(V4IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject);
1335
1336 enum MetaObjectResolverFlags {
1337     AllPropertiesAreFinal      = 0x1,
1338     LookupsIncludeEnums        = 0x2,
1339     LookupsExcludeProperties   = 0x4,
1340     ResolveTypeInformationOnly = 0x8
1341 };
1342
1343 static void initMetaObjectResolver(V4IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject);
1344
1345 static V4IR::Type resolveQmlType(QQmlEnginePrivate *qmlEngine, V4IR::MemberExpressionResolver *resolver, V4IR::Member *member)
1346 {
1347     V4IR::Type result = V4IR::VarType;
1348
1349     QQmlType *type = static_cast<QQmlType*>(resolver->data);
1350
1351     if (member->name->constData()->isUpper()) {
1352         bool ok = false;
1353         int value = type->enumValue(*member->name, &ok);
1354         if (ok) {
1355             member->setEnumValue(value);
1356             resolver->clear();
1357             return V4IR::SInt32Type;
1358         }
1359     }
1360
1361     if (type->isCompositeSingleton()) {
1362         QQmlTypeData *tdata = qmlEngine->typeLoader.getType(type->singletonInstanceInfo()->url);
1363         Q_ASSERT(tdata);
1364         Q_ASSERT(tdata->isComplete());
1365         initMetaObjectResolver(resolver, qmlEngine->propertyCacheForType(tdata->compiledData()->metaTypeId));
1366         resolver->flags |= AllPropertiesAreFinal;
1367         return resolver->resolveMember(qmlEngine, resolver, member);
1368     } else if (const QMetaObject *attachedMeta = type->attachedPropertiesType()) {
1369         QQmlPropertyCache *cache = qmlEngine->cache(attachedMeta);
1370         initMetaObjectResolver(resolver, cache);
1371         member->setAttachedPropertiesId(type->attachedPropertiesId());
1372         return resolver->resolveMember(qmlEngine, resolver, member);
1373     }
1374
1375     resolver->clear();
1376     return result;
1377 }
1378
1379 static void initQmlTypeResolver(V4IR::MemberExpressionResolver *resolver, QQmlType *qmlType)
1380 {
1381     resolver->resolveMember = &resolveQmlType;
1382     resolver->data = qmlType;
1383     resolver->extraData = 0;
1384     resolver->flags = 0;
1385 }
1386
1387 static V4IR::Type resolveImportNamespace(QQmlEnginePrivate *, V4IR::MemberExpressionResolver *resolver, V4IR::Member *member)
1388 {
1389     V4IR::Type result = V4IR::VarType;
1390     QQmlTypeNameCache *typeNamespace = static_cast<QQmlTypeNameCache*>(resolver->extraData);
1391     void *importNamespace = resolver->data;
1392
1393     QQmlTypeNameCache::Result r = typeNamespace->query(*member->name, importNamespace);
1394     if (r.isValid()) {
1395         member->freeOfSideEffects = true;
1396         if (r.scriptIndex != -1) {
1397             // TODO: remember the index and replace with subscript later.
1398             result = V4IR::VarType;
1399         } else if (r.type) {
1400             // TODO: Propagate singleton information, so that it is loaded
1401             // through the singleton getter in the run-time. Until then we
1402             // can't accelerate access :(
1403             if (!r.type->isSingleton()) {
1404                 initQmlTypeResolver(resolver, r.type);
1405                 return V4IR::QObjectType;
1406             }
1407         } else {
1408             Q_ASSERT(false); // How can this happen?
1409         }
1410     }
1411
1412     resolver->clear();
1413     return result;
1414 }
1415
1416 static void initImportNamespaceResolver(V4IR::MemberExpressionResolver *resolver, QQmlTypeNameCache *imports, const void *importNamespace)
1417 {
1418     resolver->resolveMember = &resolveImportNamespace;
1419     resolver->data = const_cast<void*>(importNamespace);
1420     resolver->extraData = imports;
1421     resolver->flags = 0;
1422 }
1423
1424 static V4IR::Type resolveMetaObjectProperty(QQmlEnginePrivate *qmlEngine, V4IR::MemberExpressionResolver *resolver, V4IR::Member *member)
1425 {
1426     V4IR::Type result = V4IR::VarType;
1427     QQmlPropertyCache *metaObject = static_cast<QQmlPropertyCache*>(resolver->data);
1428
1429     if (member->name->constData()->isUpper() && (resolver->flags & LookupsIncludeEnums)) {
1430         const QMetaObject *mo = metaObject->createMetaObject();
1431         QByteArray enumName = member->name->toUtf8();
1432         for (int ii = mo->enumeratorCount() - 1; ii >= 0; --ii) {
1433             QMetaEnum metaEnum = mo->enumerator(ii);
1434             bool ok;
1435             int value = metaEnum.keyToValue(enumName.constData(), &ok);
1436             if (ok) {
1437                 member->setEnumValue(value);
1438                 resolver->clear();
1439                 return V4IR::SInt32Type;
1440             }
1441         }
1442     }
1443
1444     if (qmlEngine && !(resolver->flags & LookupsExcludeProperties)) {
1445         QQmlPropertyData *property = member->property;
1446         if (!property && metaObject) {
1447             if (QQmlPropertyData *candidate = metaObject->property(*member->name, /*object*/0, /*context*/0)) {
1448                 const bool isFinalProperty = (candidate->isFinal() || (resolver->flags & AllPropertiesAreFinal))
1449                                              && !candidate->isFunction();
1450
1451                 if (lookupHints()
1452                     && !(resolver->flags & AllPropertiesAreFinal)
1453                     && !candidate->isFinal()
1454                     && !candidate->isFunction()
1455                     && candidate->isDirect()) {
1456                     qWarning() << "Hint: Access to property" << *member->name << "of" << metaObject->className() << "could be accelerated if it was marked as FINAL";
1457                 }
1458
1459                 if (isFinalProperty && metaObject->isAllowedInRevision(candidate)) {
1460                     property = candidate;
1461                     member->inhibitTypeConversionOnWrite = true;
1462                     if (!(resolver->flags & ResolveTypeInformationOnly))
1463                         member->property = candidate; // Cache for next iteration and isel needs it.
1464                 }
1465             }
1466         }
1467
1468         if (property) {
1469             // Enums cannot be mapped to IR types, they need to go through the run-time handling
1470             // of accepting strings that will then be converted to the right values.
1471             if (property->isEnum())
1472                 return V4IR::VarType;
1473
1474             switch (property->propType) {
1475             case QMetaType::Bool: result = V4IR::BoolType; break;
1476             case QMetaType::Int: result = V4IR::SInt32Type; break;
1477             case QMetaType::Double: result = V4IR::DoubleType; break;
1478             case QMetaType::QString: result = V4IR::StringType; break;
1479             default:
1480                 if (property->isQObject()) {
1481                     if (QQmlPropertyCache *cache = qmlEngine->propertyCacheForType(property->propType)) {
1482                         initMetaObjectResolver(resolver, cache);
1483                         return V4IR::QObjectType;
1484                     }
1485                 } else if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(property->propType)) {
1486                     if (QQmlPropertyCache *cache = qmlEngine->cache(valueType->metaObject())) {
1487                         initMetaObjectResolver(resolver, cache);
1488                         resolver->flags |= ResolveTypeInformationOnly;
1489                         return V4IR::QObjectType;
1490                     }
1491                 }
1492                 break;
1493             }
1494         }
1495     }
1496     resolver->clear();
1497     return result;
1498 }
1499
1500 static void initMetaObjectResolver(V4IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject)
1501 {
1502     resolver->resolveMember = &resolveMetaObjectProperty;
1503     resolver->data = metaObject;
1504     resolver->flags = 0;
1505     resolver->isQObjectResolver = true;
1506 }
1507
1508 void JSCodeGen::beginFunctionBodyHook()
1509 {
1510     _contextObjectTemp = _block->newTemp();
1511     _scopeObjectTemp = _block->newTemp();
1512     _importedScriptsTemp = _block->newTemp();
1513     _idArrayTemp = _block->newTemp();
1514
1515     V4IR::Temp *temp = _block->TEMP(_contextObjectTemp);
1516     initMetaObjectResolver(&temp->memberResolver, _contextObject);
1517     move(temp, _block->NAME(V4IR::Name::builtin_qml_context_object, 0, 0));
1518
1519     temp = _block->TEMP(_scopeObjectTemp);
1520     initMetaObjectResolver(&temp->memberResolver, _scopeObject);
1521     move(temp, _block->NAME(V4IR::Name::builtin_qml_scope_object, 0, 0));
1522
1523     move(_block->TEMP(_importedScriptsTemp), _block->NAME(V4IR::Name::builtin_qml_imported_scripts_object, 0, 0));
1524     move(_block->TEMP(_idArrayTemp), _block->NAME(V4IR::Name::builtin_qml_id_array, 0, 0));
1525 }
1526
1527 V4IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int col)
1528 {
1529     if (_disableAcceleratedLookups)
1530         return 0;
1531
1532     Q_UNUSED(line)
1533     Q_UNUSED(col)
1534     // Implement QML lookup semantics in the current file context.
1535     //
1536     // Note: We do not check if properties of the qml scope object or context object
1537     // are final. That's because QML tries to get as close as possible to lexical scoping,
1538     // which means in terms of properties that only those visible at compile time are chosen.
1539     // I.e. access to a "foo" property declared within the same QML component as "property int foo"
1540     // will always access that instance and as integer. If a sub-type implements its own property string foo,
1541     // then that one is not chosen for accesses from within this file, because it wasn't visible at compile
1542     // time. This corresponds to the logic in QQmlPropertyCache::findProperty to find the property associated
1543     // with the correct QML context.
1544
1545     // Look for IDs first.
1546     foreach (const IdMapping &mapping, _idObjects)
1547         if (name == mapping.name) {
1548             _function->idObjectDependencies.insert(mapping.idIndex);
1549             V4IR::Expr *s = subscript(_block->TEMP(_idArrayTemp), _block->CONST(V4IR::SInt32Type, mapping.idIndex));
1550             V4IR::Temp *result = _block->TEMP(_block->newTemp());
1551             _block->MOVE(result, s);
1552             result = _block->TEMP(result->index);
1553             initMetaObjectResolver(&result->memberResolver, mapping.type);
1554             result->memberResolver.flags |= AllPropertiesAreFinal;
1555             result->isReadOnly = true; // don't allow use as lvalue
1556             return result;
1557         }
1558
1559     {
1560         QQmlTypeNameCache::Result r = imports->query(name);
1561         if (r.isValid()) {
1562             if (r.scriptIndex != -1) {
1563                 return subscript(_block->TEMP(_importedScriptsTemp), _block->CONST(V4IR::SInt32Type, r.scriptIndex));
1564             } else if (r.type) {
1565                 V4IR::Name *typeName = _block->NAME(name, line, col);
1566                 // Make sure the run-time loads this through the more efficient singleton getter.
1567                 typeName->qmlSingleton = r.type->isCompositeSingleton();
1568                 typeName->freeOfSideEffects = true;
1569                 V4IR::Temp *result = _block->TEMP(_block->newTemp());
1570                 _block->MOVE(result, typeName);
1571
1572                 result = _block->TEMP(result->index);
1573                 initQmlTypeResolver(&result->memberResolver, r.type);
1574                 return result;
1575             } else {
1576                 Q_ASSERT(r.importNamespace);
1577                 V4IR::Name *namespaceName = _block->NAME(name, line, col);
1578                 namespaceName->freeOfSideEffects = true;
1579                 V4IR::Temp *result = _block->TEMP(_block->newTemp());
1580                 initImportNamespaceResolver(&result->memberResolver, imports, r.importNamespace);
1581
1582                 _block->MOVE(result, namespaceName);
1583                 return _block->TEMP(result->index);
1584             }
1585         }
1586     }
1587
1588     if (_scopeObject) {
1589         bool propertyExistsButForceNameLookup = false;
1590         QQmlPropertyData *pd = lookupQmlCompliantProperty(_scopeObject, name, &propertyExistsButForceNameLookup);
1591         if (propertyExistsButForceNameLookup)
1592             return 0;
1593         if (pd) {
1594             V4IR::Temp *base = _block->TEMP(_scopeObjectTemp);
1595             initMetaObjectResolver(&base->memberResolver, _scopeObject);
1596             return _block->MEMBER(base, _function->newString(name), pd, V4IR::Member::MemberOfQmlScopeObject);
1597         }
1598     }
1599
1600     if (_contextObject) {
1601         bool propertyExistsButForceNameLookup = false;
1602         QQmlPropertyData *pd = lookupQmlCompliantProperty(_contextObject, name, &propertyExistsButForceNameLookup);
1603         if (propertyExistsButForceNameLookup)
1604             return 0;
1605         if (pd) {
1606             V4IR::Temp *base = _block->TEMP(_contextObjectTemp);
1607             initMetaObjectResolver(&base->memberResolver, _contextObject);
1608             return _block->MEMBER(base, _function->newString(name), pd, V4IR::Member::MemberOfQmlContextObject);
1609         }
1610     }
1611
1612     // fall back to name lookup at run-time.
1613     return 0;
1614 }
1615
1616 SignalHandlerConverter::SignalHandlerConverter(QQmlEnginePrivate *enginePrivate, ParsedQML *parsedQML,
1617                                                QQmlCompiledData *unit)
1618     : enginePrivate(enginePrivate)
1619     , parsedQML(parsedQML)
1620     , unit(unit)
1621 {
1622 }
1623
1624 bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclarations()
1625 {
1626     foreach (QmlObject *obj, parsedQML->objects) {
1627         QString elementName = stringAt(obj->inheritedTypeNameIndex);
1628         if (elementName.isEmpty())
1629             continue;
1630         QQmlPropertyCache *cache = unit->resolvedTypes[obj->inheritedTypeNameIndex].createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
1631         if (!convertSignalHandlerExpressionsToFunctionDeclarations(obj, elementName, cache))
1632             return false;
1633     }
1634     return true;
1635 }
1636
1637 bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclarations(QmlObject *obj, const QString &typeName, QQmlPropertyCache *propertyCache)
1638 {
1639     // map from signal name defined in qml itself to list of parameters
1640     QHash<QString, QStringList> customSignals;
1641
1642     for (Binding *binding = obj->bindings->first; binding; binding = binding->next) {
1643         QString propertyName = stringAt(binding->propertyNameIndex);
1644         // Attached property?
1645         if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
1646             QmlObject *attachedObj = parsedQML->objects[binding->value.objectIndex];
1647             QQmlType *type = unit->resolvedTypes.value(binding->propertyNameIndex).type;
1648             const QMetaObject *attachedType = type ? type->attachedPropertiesType() : 0;
1649             if (!attachedType)
1650                 COMPILE_EXCEPTION(binding->location, tr("Non-existent attached object"));
1651             QQmlPropertyCache *cache = enginePrivate->cache(attachedType);
1652             if (!convertSignalHandlerExpressionsToFunctionDeclarations(attachedObj, propertyName, cache))
1653                 return false;
1654             continue;
1655         }
1656
1657         if (!QQmlCodeGenerator::isSignalPropertyName(propertyName))
1658             continue;
1659
1660         if (binding->type != QV4::CompiledData::Binding::Type_Script) {
1661             COMPILE_EXCEPTION(binding->location, tr("Incorrectly specified signal assignment"));
1662         }
1663
1664         PropertyResolver resolver(propertyCache);
1665
1666         Q_ASSERT(propertyName.startsWith(QStringLiteral("on")));
1667         propertyName.remove(0, 2);
1668
1669         // Note that the property name could start with any alpha or '_' or '$' character,
1670         // so we need to do the lower-casing of the first alpha character.
1671         for (int firstAlphaIndex = 0; firstAlphaIndex < propertyName.size(); ++firstAlphaIndex) {
1672             if (propertyName.at(firstAlphaIndex).isUpper()) {
1673                 propertyName[firstAlphaIndex] = propertyName.at(firstAlphaIndex).toLower();
1674                 break;
1675             }
1676         }
1677
1678         QList<QString> parameters;
1679
1680         bool notInRevision = false;
1681         QQmlPropertyData *signal = resolver.signal(propertyName, &notInRevision);
1682         if (signal) {
1683             int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex);
1684             foreach (const QByteArray &param, propertyCache->signalParameterNames(sigIndex))
1685                 parameters << QString::fromUtf8(param);
1686         } else {
1687             if (notInRevision) {
1688                 // Try assinging it as a property later
1689                 if (resolver.property(propertyName, /*notInRevision ptr*/0))
1690                     continue;
1691
1692                 const QString &originalPropertyName = stringAt(binding->propertyNameIndex);
1693
1694                 const QQmlType *type = unit->resolvedTypes.value(obj->inheritedTypeNameIndex).type;
1695                 if (type) {
1696                     COMPILE_EXCEPTION(binding->location, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(originalPropertyName).arg(type->module()).arg(type->majorVersion()).arg(type->minorVersion()));
1697                 } else {
1698                     COMPILE_EXCEPTION(binding->location, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(originalPropertyName));
1699                 }
1700             }
1701
1702             // Try to look up the signal parameter names in the object itself
1703
1704             // build cache if necessary
1705             if (customSignals.isEmpty()) {
1706                 for (Signal *signal = obj->qmlSignals->first; signal; signal = signal->next) {
1707                     const QString &signalName = stringAt(signal->nameIndex);
1708                     customSignals.insert(signalName, signal->parameterStringList(parsedQML->jsGenerator.strings));
1709                 }
1710
1711                 for (QmlProperty *property = obj->properties->first; property; property = property->next) {
1712                     const QString propName = stringAt(property->nameIndex);
1713                     customSignals.insert(propName, QStringList());
1714                 }
1715             }
1716
1717             QHash<QString, QStringList>::ConstIterator entry = customSignals.find(propertyName);
1718             if (entry == customSignals.constEnd() && propertyName.endsWith(QStringLiteral("Changed"))) {
1719                 QString alternateName = propertyName.mid(0, propertyName.length() - static_cast<int>(strlen("Changed")));
1720                 entry = customSignals.find(alternateName);
1721             }
1722
1723             if (entry == customSignals.constEnd()) {
1724                 // Can't find even a custom signal, then just don't do anything and try
1725                 // keeping the binding as a regular property assignment.
1726                 continue;
1727             }
1728
1729             parameters = entry.value();
1730         }
1731
1732         QQmlJS::Engine &jsEngine = parsedQML->jsParserEngine;
1733         QQmlJS::MemoryPool *pool = jsEngine.pool();
1734
1735         AST::FormalParameterList *paramList = 0;
1736         foreach (const QString &param, parameters) {
1737             QStringRef paramNameRef = jsEngine.newStringRef(param);
1738
1739             if (paramList)
1740                 paramList = new (pool) AST::FormalParameterList(paramList, paramNameRef);
1741             else
1742                 paramList = new (pool) AST::FormalParameterList(paramNameRef);
1743         }
1744
1745         if (paramList)
1746             paramList = paramList->finish();
1747
1748         AST::Statement *statement = static_cast<AST::Statement*>(parsedQML->functions[binding->value.compiledScriptIndex].node);
1749         AST::SourceElement *sourceElement = new (pool) AST::StatementSourceElement(statement);
1750         AST::SourceElements *elements = new (pool) AST::SourceElements(sourceElement);
1751         elements = elements->finish();
1752
1753         AST::FunctionBody *body = new (pool) AST::FunctionBody(elements);
1754
1755         AST::FunctionDeclaration *functionDeclaration = new (pool) AST::FunctionDeclaration(jsEngine.newStringRef(propertyName), paramList, body);
1756
1757         parsedQML->functions[binding->value.compiledScriptIndex] = functionDeclaration;
1758         binding->flags |= QV4::CompiledData::Binding::IsSignalHandlerExpression;
1759         binding->propertyNameIndex = parsedQML->jsGenerator.registerString(propertyName);
1760     }
1761     return true;
1762 }
1763
1764 void SignalHandlerConverter::recordError(const QV4::CompiledData::Location &location, const QString &description)
1765 {
1766     QQmlError error;
1767     error.setUrl(unit->url);
1768     error.setLine(location.line);
1769     error.setColumn(location.column);
1770     error.setDescription(description);
1771     errors << error;
1772 }
1773
1774 QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision)
1775 {
1776     if (notInRevision) *notInRevision = false;
1777
1778     QQmlPropertyData *d = cache->property(name, 0, 0);
1779
1780     // Find the first property
1781     while (d && d->isFunction())
1782         d = cache->overrideData(d);
1783
1784     if (d && !cache->isAllowedInRevision(d)) {
1785         if (notInRevision) *notInRevision = true;
1786         return 0;
1787     } else {
1788         return d;
1789     }
1790 }
1791
1792
1793 QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision)
1794 {
1795     if (notInRevision) *notInRevision = false;
1796
1797     QQmlPropertyData *d = cache->property(name, 0, 0);
1798     if (notInRevision) *notInRevision = false;
1799
1800     while (d && !(d->isFunction()))
1801         d = cache->overrideData(d);
1802
1803     if (d && !cache->isAllowedInRevision(d)) {
1804         if (notInRevision) *notInRevision = true;
1805         return 0;
1806     } else if (d && d->isSignal()) {
1807         return d;
1808     }
1809
1810     if (name.endsWith(QStringLiteral("Changed"))) {
1811         QString propName = name.mid(0, name.length() - static_cast<int>(strlen("Changed")));
1812
1813         d = property(propName, notInRevision);
1814         if (d)
1815             return cache->signal(d->notifyIndex);
1816     }
1817
1818     return 0;
1819 }