Add missing QT_{BEGIN,END}_NAMESPACE
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmlscript.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qqmlscript_p.h"
43
44 #include "parser/qqmljsengine_p.h"
45 #include "parser/qqmljsparser_p.h"
46 #include "parser/qqmljslexer_p.h"
47 #include "parser/qqmljsmemorypool_p.h"
48 #include "parser/qqmljsastvisitor_p.h"
49 #include "parser/qqmljsast_p.h"
50 #include <private/qqmlrewrite_p.h>
51
52 #include <QStack>
53 #include <QStringList>
54 #include <QCoreApplication>
55 #include <QtDebug>
56
57 QT_BEGIN_NAMESPACE
58
59 using namespace QQmlJS;
60 using namespace QQmlScript;
61
62 // 
63 // Parser IR classes
64 //
65 QQmlScript::Object::Object()
66 : type(-1), typeReference(0), idIndex(-1), metatype(0), synthCache(0),
67   defaultProperty(0), parserStatusCast(-1), componentCompileState(0), nextAliasingObject(0),
68   nextIdObject(0)
69 {
70 }
71
72 QQmlScript::Object::~Object() 
73
74     if (synthCache) synthCache->release();
75 }
76
77 void Object::setBindingBit(int b)
78 {
79     while (bindingBitmask.size() < 4 * (1 + b / 32))
80         bindingBitmask.append(char(0));
81
82     quint32 *bits = (quint32 *)bindingBitmask.data();
83     bits[b / 32] |= (1 << (b % 32));
84 }
85
86 QQmlScript::Property *Object::getDefaultProperty()
87 {
88     if (!defaultProperty) {
89         defaultProperty = pool()->New<Property>();
90         defaultProperty->parent = this;
91     }
92     return defaultProperty;
93 }
94
95 void QQmlScript::Object::addValueProperty(Property *p)
96 {
97     valueProperties.append(p);
98 }
99
100 void QQmlScript::Object::addSignalProperty(Property *p)
101 {
102     signalProperties.append(p);
103 }
104
105 void QQmlScript::Object::addAttachedProperty(Property *p)
106 {
107     attachedProperties.append(p);
108 }
109
110 void QQmlScript::Object::addGroupedProperty(Property *p)
111 {
112     groupedProperties.append(p);
113 }
114
115 void QQmlScript::Object::addValueTypeProperty(Property *p)
116 {
117     valueTypeProperties.append(p);
118 }
119
120 void QQmlScript::Object::addScriptStringProperty(Property *p)
121 {
122     scriptStringProperties.append(p);
123 }
124
125 // This lookup is optimized for missing, and having to create a new property.
126 Property *QQmlScript::Object::getProperty(const QHashedStringRef &name, bool create)
127 {
128     if (create) {
129         quint32 h = name.hash();
130         if (propertiesHashField.testAndSet(h)) {
131             for (Property *p = properties.first(); p; p = properties.next(p)) {
132                 if (p->name() == name)
133                     return p;
134             }
135         }
136
137         Property *property = pool()->New<Property>();
138         property->parent = this;
139         property->_name = name;
140         property->isDefault = false;
141         properties.prepend(property);
142         return property;
143     } else {
144         for (Property *p = properties.first(); p; p = properties.next(p)) {
145             if (p->name() == name)
146                 return p;
147         }
148     }
149
150     return 0;
151 }
152
153 Property *QQmlScript::Object::getProperty(const QStringRef &name, bool create)
154 {
155     return getProperty(QHashedStringRef(name), create);
156 }
157
158 Property *QQmlScript::Object::getProperty(const QString &name, bool create)
159 {
160     for (Property *p = properties.first(); p; p = properties.next(p)) {
161         if (p->name() == name)
162             return p;
163     }
164
165     if (create) {
166         Property *property = pool()->New<Property>();
167         property->parent = this;
168         property->_name = QStringRef(pool()->NewString(name));
169         propertiesHashField.testAndSet(property->_name.hash());
170         property->isDefault = false;
171         properties.prepend(property);
172         return property;
173     } else {
174         return 0;
175     }
176 }
177
178 int QQmlScript::Object::aggregateDynamicSignalParameterCount() const
179 {
180     int sum = 0;
181     for (DynamicSignal *s = dynamicSignals.first(); s; s = dynamicSignals.next(s))
182         sum += s->parameterTypes.count() + 1; // +1 for return type
183     return sum;
184 }
185
186 int QQmlScript::Object::aggregateDynamicSlotParameterCount() const
187 {
188     int sum = 0;
189     for (DynamicSlot *s = dynamicSlots.first(); s; s = dynamicSlots.next(s))
190         sum += s->parameterNames.count() + 1; // +1 for return type
191     return sum;
192 }
193
194 QQmlScript::Object::DynamicProperty::DynamicProperty()
195 : isDefaultProperty(false), isReadOnly(false), type(Variant), defaultValue(0), nextProperty(0),
196   nameIndex(-1)
197 {
198 }
199
200 QQmlScript::Object::DynamicSignal::DynamicSignal()
201 : nextSignal(0), nameIndex(-1)
202 {
203 }
204
205 QQmlScript::Object::DynamicSlot::DynamicSlot()
206 : nextSlot(0), nameIndex(-1)
207 {
208 }
209
210 int QQmlScript::Object::DynamicSlot::parameterNamesLength() const
211 {
212     int rv = 0;
213     for (int ii = 0; ii < parameterNames.count(); ++ii)
214         rv += parameterNames.at(ii).length();
215     return rv;
216 }
217
218 QQmlScript::Property::Property()
219 : parent(0), type(0), index(-1), value(0), isDefault(true), isDeferred(false), 
220   isValueTypeSubProperty(false), isAlias(false), isReadOnlyDeclaration(false),
221   scriptStringScope(-1), nextMainProperty(0), nextProperty(0)
222 {
223 }
224
225 QQmlScript::Object *QQmlScript::Property::getValue(const LocationSpan &l)
226 {
227     if (!value) { value = pool()->New<Object>(); value->location = l; }
228     return value;
229 }
230
231 void QQmlScript::Property::addValue(Value *v)
232 {
233     values.append(v);
234 }
235
236 void QQmlScript::Property::addOnValue(Value *v)
237 {
238     onValues.append(v);
239 }
240
241 bool QQmlScript::Property::isEmpty() const
242 {
243     return !value && values.isEmpty() && onValues.isEmpty();
244 }
245
246 QQmlScript::Value::Value()
247 : type(Unknown), object(0), bindingReference(0), nextValue(0)
248 {
249 }
250
251 QQmlScript::Variant::Variant()
252 : t(Invalid)
253 {
254 }
255
256 QQmlScript::Variant::Variant(const Variant &o)
257 : t(o.t), d(o.d), asWritten(o.asWritten)
258 {
259 }
260
261 QQmlScript::Variant::Variant(bool v)
262 : t(Boolean), b(v)
263 {
264 }
265
266 QQmlScript::Variant::Variant(double v, const QStringRef &asWritten)
267 : t(Number), d(v), asWritten(asWritten)
268 {
269 }
270
271 QQmlScript::Variant::Variant(QQmlJS::AST::StringLiteral *v)
272 : t(String), l(v)
273 {
274 }
275
276 QQmlScript::Variant::Variant(const QStringRef &asWritten, QQmlJS::AST::Node *n)
277 : t(Script), n(n), asWritten(asWritten)
278 {
279 }
280
281 QQmlScript::Variant &QQmlScript::Variant::operator=(const Variant &o)
282 {
283     t = o.t;
284     d = o.d;
285     asWritten = o.asWritten;
286     return *this;
287 }
288
289 QQmlScript::Variant::Type QQmlScript::Variant::type() const
290 {
291     return t;
292 }
293
294 bool QQmlScript::Variant::asBoolean() const
295 {
296     return b;
297 }
298
299 QString QQmlScript::Variant::asString() const
300 {
301     if (t == String) {
302         return l->value.toString();
303     } else {
304         return asWritten.toString();
305     }
306 }
307
308 double QQmlScript::Variant::asNumber() const
309 {
310     return d;
311 }
312
313 //reverse of Lexer::singleEscape()
314 QString escapedString(const QString &string)
315 {
316     QString tmp = QLatin1String("\"");
317     for (int i = 0; i < string.length(); ++i) {
318         const QChar &c = string.at(i);
319         switch(c.unicode()) {
320         case 0x08:
321             tmp += QLatin1String("\\b");
322             break;
323         case 0x09:
324             tmp += QLatin1String("\\t");
325             break;
326         case 0x0A:
327             tmp += QLatin1String("\\n");
328             break;
329         case 0x0B:
330             tmp += QLatin1String("\\v");
331             break;
332         case 0x0C:
333             tmp += QLatin1String("\\f");
334             break;
335         case 0x0D:
336             tmp += QLatin1String("\\r");
337             break;
338         case 0x22:
339             tmp += QLatin1String("\\\"");
340             break;
341         case 0x27:
342             tmp += QLatin1String("\\\'");
343             break;
344         case 0x5C:
345             tmp += QLatin1String("\\\\");
346             break;
347         default:
348             tmp += c;
349             break;
350         }
351     }
352     tmp += QLatin1Char('\"');
353     return tmp;
354 }
355
356 QString QQmlScript::Variant::asScript() const
357 {
358     switch(type()) { 
359     default:
360     case Invalid:
361         return QString();
362     case Boolean:
363         return b?QLatin1String("true"):QLatin1String("false");
364     case Number:
365         if (asWritten.isEmpty())
366             return QString::number(d);
367         else 
368             return asWritten.toString();
369     case String:
370         return escapedString(asString());
371     case Script:
372         if (AST::IdentifierExpression *i = AST::cast<AST::IdentifierExpression *>(n)) {
373             return i->name.toString();
374         } else
375             return asWritten.toString();
376     }
377 }
378
379 QQmlJS::AST::Node *QQmlScript::Variant::asAST() const
380 {
381     if (type() == Script)
382         return n;
383     else
384         return 0;
385 }
386
387 bool QQmlScript::Variant::isStringList() const
388 {
389     if (isString())
390         return true;
391
392     if (type() != Script || !n)
393         return false;
394
395     AST::ArrayLiteral *array = AST::cast<AST::ArrayLiteral *>(n);
396     if (!array)
397         return false;
398
399     AST::ElementList *elements = array->elements;
400
401     while (elements) {
402
403         if (!AST::cast<AST::StringLiteral *>(elements->expression))
404             return false;
405
406         elements = elements->next;
407     }
408
409     return true;
410 }
411
412 QStringList QQmlScript::Variant::asStringList() const
413 {
414     QStringList rv;
415     if (isString()) {
416         rv << asString();
417         return rv;
418     }
419
420     AST::ArrayLiteral *array = AST::cast<AST::ArrayLiteral *>(n);
421     if (!array)
422         return rv;
423
424     AST::ElementList *elements = array->elements;
425     while (elements) {
426
427         AST::StringLiteral *string = AST::cast<AST::StringLiteral *>(elements->expression);
428         if (!string)
429             return QStringList();
430         rv.append(string->value.toString());
431
432         elements = elements->next;
433     }
434
435     return  rv;
436 }
437
438 //
439 // Actual parser classes
440 //
441 namespace {
442
443 class ProcessAST: protected AST::Visitor
444 {
445     struct State {
446         State() : object(0), property(0) {}
447         State(QQmlScript::Object *o) : object(o), property(0) {}
448         State(QQmlScript::Object *o, Property *p) : object(o), property(p) {}
449
450         QQmlScript::Object *object;
451         Property *property;
452     };
453
454     struct StateStack : public QStack<State>
455     {
456         void pushObject(QQmlScript::Object *obj)
457         {
458             push(State(obj));
459         }
460
461         void pushProperty(const QString &name, const LocationSpan &location)
462         {
463             const State &state = top();
464             if (state.property) {
465                 State s(state.property->getValue(location),
466                         state.property->getValue(location)->getProperty(name));
467                 s.property->location = location;
468                 push(s);
469             } else {
470                 State s(state.object, state.object->getProperty(name));
471
472                 s.property->location = location;
473                 push(s);
474             }
475         }
476
477         void pushProperty(const QStringRef &name, const LocationSpan &location)
478         {
479             const State &state = top();
480             if (state.property) {
481                 State s(state.property->getValue(location),
482                         state.property->getValue(location)->getProperty(name));
483                 s.property->location = location;
484                 push(s);
485             } else {
486                 State s(state.object, state.object->getProperty(name));
487
488                 s.property->location = location;
489                 push(s);
490             }
491         }
492     };
493
494 public:
495     ProcessAST(QQmlScript::Parser *parser);
496     virtual ~ProcessAST();
497
498     void operator()(const QString &code, AST::Node *node);
499
500     static void extractVersion(QStringRef string, int *maj, int *min);
501
502 protected:
503
504     QQmlScript::Object *defineObjectBinding(AST::UiQualifiedId *propertyName, bool onAssignment,
505                                 const QString &objectType,
506                                 AST::SourceLocation typeLocation,
507                                 LocationSpan location,
508                                 AST::UiObjectInitializer *initializer = 0);
509
510     QQmlScript::Variant getVariant(AST::Statement *stmt);
511     QQmlScript::Variant getVariant(AST::ExpressionNode *expr);
512
513     LocationSpan location(AST::SourceLocation start, AST::SourceLocation end);
514     LocationSpan location(AST::UiQualifiedId *);
515
516     using AST::Visitor::visit;
517     using AST::Visitor::endVisit;
518
519     virtual bool visit(AST::UiProgram *node);
520     virtual bool visit(AST::UiImport *node);
521     virtual bool visit(AST::UiObjectDefinition *node);
522     virtual bool visit(AST::UiPublicMember *node);
523     virtual bool visit(AST::UiObjectBinding *node);
524
525     virtual bool visit(AST::UiScriptBinding *node);
526     virtual bool visit(AST::UiArrayBinding *node);
527     virtual bool visit(AST::UiSourceElement *node);
528
529     void accept(AST::Node *node);
530
531     QString asString(AST::UiQualifiedId *node) const;
532
533     const State state() const;
534     QQmlScript::Object *currentObject() const;
535     Property *currentProperty() const;
536
537     QString textAt(const AST::SourceLocation &loc) const
538     { return _contents->mid(loc.offset, loc.length); }
539
540     QStringRef textRefAt(const AST::SourceLocation &loc) const
541     { return QStringRef(_contents, loc.offset, loc.length); }
542
543     QString textAt(const AST::SourceLocation &first,
544                    const AST::SourceLocation &last) const
545     { return _contents->mid(first.offset, last.offset + last.length - first.offset); }
546
547     QStringRef textRefAt(const AST::SourceLocation &first,
548                          const AST::SourceLocation &last) const
549     { return QStringRef(_contents, first.offset, last.offset + last.length - first.offset); }
550
551     QString asString(AST::ExpressionNode *expr)
552     {
553         if (! expr)
554             return QString();
555
556         return textAt(expr->firstSourceLocation(), expr->lastSourceLocation());
557     }
558
559     QStringRef asStringRef(AST::ExpressionNode *expr)
560     {
561         if (! expr)
562             return QStringRef();
563
564         return textRefAt(expr->firstSourceLocation(), expr->lastSourceLocation());
565     }
566
567     QString asString(AST::Statement *stmt)
568     {
569         if (! stmt)
570             return QString();
571
572         QString s = textAt(stmt->firstSourceLocation(), stmt->lastSourceLocation());
573         s += QLatin1Char('\n');
574         return s;
575     }
576
577     QStringRef asStringRef(AST::Statement *stmt)
578     {
579         if (! stmt)
580             return QStringRef();
581
582         return textRefAt(stmt->firstSourceLocation(), stmt->lastSourceLocation());
583     }
584
585 private:
586     QQmlScript::Parser *_parser;
587     StateStack _stateStack;
588     const QString *_contents;
589 };
590
591 ProcessAST::ProcessAST(QQmlScript::Parser *parser)
592     : _parser(parser)
593 {
594 }
595
596 ProcessAST::~ProcessAST()
597 {
598 }
599
600 void ProcessAST::operator()(const QString &code, AST::Node *node)
601 {
602     _contents = &code;
603     accept(node);
604 }
605
606 void ProcessAST::accept(AST::Node *node)
607 {
608     AST::Node::acceptChild(node, this);
609 }
610
611 const ProcessAST::State ProcessAST::state() const
612 {
613     if (_stateStack.isEmpty())
614         return State();
615
616     return _stateStack.back();
617 }
618
619 QQmlScript::Object *ProcessAST::currentObject() const
620 {
621     return state().object;
622 }
623
624 Property *ProcessAST::currentProperty() const
625 {
626     return state().property;
627 }
628
629 void ProcessAST::extractVersion(QStringRef string, int *maj, int *min)
630 {
631     *maj = -1; *min = -1;
632
633     if (!string.isEmpty()) {
634
635         int dot = string.indexOf(QLatin1Char('.'));
636
637         if (dot < 0) {
638             *maj = string.toString().toInt();
639             *min = 0;
640         } else {
641             const QString *s = string.string();
642             int p = string.position();
643             *maj = QStringRef(s, p, dot).toString().toInt();
644             *min = QStringRef(s, p + dot + 1, string.size() - dot - 1).toString().toInt();
645         }
646     }
647 }
648
649 QString ProcessAST::asString(AST::UiQualifiedId *node) const
650 {
651     QString s;
652
653     for (AST::UiQualifiedId *it = node; it; it = it->next) {
654         s.append(it->name.toString());
655
656         if (it->next)
657             s.append(QLatin1Char('.'));
658     }
659
660     return s;
661 }
662
663 QQmlScript::Object *
664 ProcessAST::defineObjectBinding(AST::UiQualifiedId *propertyName,
665                                 bool onAssignment,
666                                 const QString &objectType,
667                                 AST::SourceLocation typeLocation,
668                                 LocationSpan location,
669                                 AST::UiObjectInitializer *initializer)
670 {
671     int lastTypeDot = objectType.lastIndexOf(QLatin1Char('.'));
672
673     // With no preceding qualification, first char is at (-1 + 1) == 0
674     bool isType = !objectType.isEmpty() && objectType.at(lastTypeDot+1).isUpper();
675
676     int propertyCount = 0;
677     for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
678         ++propertyCount;
679         _stateStack.pushProperty(name->name,
680                                  this->location(name));
681     }
682
683     if (!onAssignment && propertyCount && currentProperty() && !currentProperty()->values.isEmpty()) {
684         QQmlError error;
685         error.setDescription(QCoreApplication::translate("QQmlParser","Property value set multiple times"));
686         error.setLine(this->location(propertyName).start.line);
687         error.setColumn(this->location(propertyName).start.column);
688         _parser->_errors << error;
689         return 0;
690     }
691
692     if (!isType) {
693
694         // Is the identifier qualified by a namespace?
695         int namespaceLength = 0;
696         if (lastTypeDot > 0) {
697             const QString qualifier(objectType.left(lastTypeDot));
698
699             for (int ii = 0; ii < _parser->_imports.count(); ++ii) {
700                 const QQmlScript::Import &import = _parser->_imports.at(ii);
701                 if (import.qualifier == qualifier) {
702                     // The qualifier is a namespace - expect a type here
703                     namespaceLength = qualifier.length() + 1;
704                     break;
705                 }
706             }
707         }
708
709         if (propertyCount || !currentObject() || namespaceLength) {
710             QQmlError error;
711             error.setDescription(QCoreApplication::translate("QQmlParser","Expected type name"));
712             error.setLine(typeLocation.startLine);
713             error.setColumn(typeLocation.startColumn + namespaceLength);
714             _parser->_errors << error;
715             return 0;
716         }
717
718         LocationSpan loc = ProcessAST::location(typeLocation, typeLocation);
719         if (propertyName)
720             loc = ProcessAST::location(propertyName);
721
722         _stateStack.pushProperty(objectType, loc);
723        accept(initializer);
724         _stateStack.pop();
725
726         return 0;
727
728     } else {
729         // Class
730
731         QQmlScript::Object *obj = _parser->_pool.New<QQmlScript::Object>();
732
733         obj->type = _parser->findOrCreateTypeId(objectType, obj);
734         obj->typeReference = _parser->_refTypes.at(obj->type);
735         obj->location = location;
736
737         if (propertyCount) {
738             Property *prop = currentProperty();
739             QQmlScript::Value *v = _parser->_pool.New<QQmlScript::Value>();
740             v->object = obj;
741             v->location = obj->location;
742             if (onAssignment)
743                 prop->addOnValue(v);
744             else
745                 prop->addValue(v);
746
747             while (propertyCount--)
748                 _stateStack.pop();
749
750         } else {
751
752             if (! _parser->tree()) {
753                 _parser->setTree(obj);
754             } else {
755                 const State state = _stateStack.top();
756                 QQmlScript::Value *v = _parser->_pool.New<QQmlScript::Value>();
757                 v->object = obj;
758                 v->location = obj->location;
759                 if (state.property) {
760                     state.property->addValue(v);
761                 } else {
762                     Property *defaultProp = state.object->getDefaultProperty();
763                     if (defaultProp->location.start.line == 0) {
764                         defaultProp->location = v->location;
765                         defaultProp->location.end = defaultProp->location.start;
766                         defaultProp->location.range.length = 0;
767                     }
768                     defaultProp->addValue(v);
769                 }
770             }
771         }
772
773         _stateStack.pushObject(obj);
774         accept(initializer);
775         _stateStack.pop();
776
777         return obj;
778     }
779 }
780
781 LocationSpan ProcessAST::location(AST::UiQualifiedId *id)
782 {
783     return location(id->identifierToken, id->identifierToken);
784 }
785
786 LocationSpan ProcessAST::location(AST::SourceLocation start, AST::SourceLocation end)
787 {
788     LocationSpan rv;
789     rv.start.line = start.startLine;
790     rv.start.column = start.startColumn;
791     rv.end.line = end.startLine;
792     rv.end.column = end.startColumn + end.length - 1;
793     rv.range.offset = start.offset;
794     rv.range.length = end.offset + end.length - start.offset;
795     return rv;
796 }
797
798 // UiProgram: UiImportListOpt UiObjectMemberList ;
799 bool ProcessAST::visit(AST::UiProgram *node)
800 {
801     accept(node->imports);
802     accept(node->members->member);
803     return false;
804 }
805
806 // UiImport: T_IMPORT T_STRING_LITERAL ;
807 bool ProcessAST::visit(AST::UiImport *node)
808 {
809     QString uri;
810     QQmlScript::Import import;
811
812     if (!node->fileName.isNull()) {
813         uri = node->fileName.toString();
814
815         if (uri.endsWith(QLatin1String(".js"))) {
816             import.type = QQmlScript::Import::Script;
817         } else {
818             import.type = QQmlScript::Import::File;
819         }
820     } else {
821         import.type = QQmlScript::Import::Library;
822         uri = asString(node->importUri);
823     }
824
825     AST::SourceLocation startLoc = node->importToken;
826     AST::SourceLocation endLoc = node->semicolonToken;
827
828     // Qualifier
829     if (!node->importId.isNull()) {
830         import.qualifier = node->importId.toString();
831         if (!import.qualifier.at(0).isUpper()) {
832             QQmlError error;
833             error.setDescription(QCoreApplication::translate("QQmlParser","Invalid import qualifier ID"));
834             error.setLine(node->importIdToken.startLine);
835             error.setColumn(node->importIdToken.startColumn);
836             _parser->_errors << error;
837             return false;
838         }
839         if (import.qualifier == QLatin1String("Qt")) {
840             QQmlError error;
841             error.setDescription(QCoreApplication::translate("QQmlParser","Reserved name \"Qt\" cannot be used as an qualifier"));
842             error.setLine(node->importIdToken.startLine);
843             error.setColumn(node->importIdToken.startColumn);
844             _parser->_errors << error;
845             return false;
846         }
847
848         // Check for script qualifier clashes
849         bool isScript = import.type == QQmlScript::Import::Script;
850         for (int ii = 0; ii < _parser->_imports.count(); ++ii) {
851             const QQmlScript::Import &other = _parser->_imports.at(ii);
852             bool otherIsScript = other.type == QQmlScript::Import::Script;
853
854             if ((isScript || otherIsScript) && import.qualifier == other.qualifier) {
855                 QQmlError error;
856                 error.setDescription(QCoreApplication::translate("QQmlParser","Script import qualifiers must be unique."));
857                 error.setLine(node->importIdToken.startLine);
858                 error.setColumn(node->importIdToken.startColumn);
859                 _parser->_errors << error;
860                 return false;
861             }
862         }
863
864     } else if (import.type == QQmlScript::Import::Script) {
865         QQmlError error;
866         error.setDescription(QCoreApplication::translate("QQmlParser","Script import requires a qualifier"));
867         error.setLine(node->fileNameToken.startLine);
868         error.setColumn(node->fileNameToken.startColumn);
869         _parser->_errors << error;
870         return false;
871     }
872
873     if (node->versionToken.isValid()) {
874         extractVersion(textRefAt(node->versionToken), &import.majorVersion, &import.minorVersion);
875     } else if (import.type == QQmlScript::Import::Library) {
876         QQmlError error;
877         error.setDescription(QCoreApplication::translate("QQmlParser","Library import requires a version"));
878         error.setLine(node->importIdToken.startLine);
879         error.setColumn(node->importIdToken.startColumn);
880         _parser->_errors << error;
881         return false;
882     }
883
884
885     import.location = location(startLoc, endLoc);
886     import.uri = uri;
887
888     _parser->_imports << import;
889
890     return false;
891 }
892
893 bool ProcessAST::visit(AST::UiPublicMember *node)
894 {
895     static const struct TypeNameToType {
896         const char *name;
897         size_t nameLength;
898         Object::DynamicProperty::Type type;
899     } propTypeNameToTypes[] = {
900         { "int", strlen("int"), Object::DynamicProperty::Int },
901         { "bool", strlen("bool"), Object::DynamicProperty::Bool },
902         { "double", strlen("double"), Object::DynamicProperty::Real },
903         { "real", strlen("real"), Object::DynamicProperty::Real },
904         { "string", strlen("string"), Object::DynamicProperty::String },
905         { "url", strlen("url"), Object::DynamicProperty::Url },
906         { "color", strlen("color"), Object::DynamicProperty::Color },
907         // Internally QTime, QDate and QDateTime are all supported.
908         // To be more consistent with JavaScript we expose only
909         // QDateTime as it matches closely with the Date JS type.
910         // We also call it "date" to match.
911         // { "time", strlen("time"), Object::DynamicProperty::Time },
912         // { "date", strlen("date"), Object::DynamicProperty::Date },
913         { "date", strlen("date"), Object::DynamicProperty::DateTime },
914         { "rect", strlen("rect"), Object::DynamicProperty::Rect },
915         { "point", strlen("point"), Object::DynamicProperty::Point },
916         { "size", strlen("size"), Object::DynamicProperty::Size },
917         { "font", strlen("font"), Object::DynamicProperty::Font },
918         { "vector2d", strlen("vector2d"), Object::DynamicProperty::Vector2D },
919         { "vector3d", strlen("vector3d"), Object::DynamicProperty::Vector3D },
920         { "vector4d", strlen("vector4d"), Object::DynamicProperty::Vector4D },
921         { "quaternion", strlen("quaternion"), Object::DynamicProperty::Quaternion },
922         { "matrix4x4", strlen("matrix4x4"), Object::DynamicProperty::Matrix4x4 },
923         { "variant", strlen("variant"), Object::DynamicProperty::Variant },
924         { "var", strlen("var"), Object::DynamicProperty::Var }
925     };
926     static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) /
927                                                 sizeof(propTypeNameToTypes[0]);
928
929     if(node->type == AST::UiPublicMember::Signal) {
930         Object::DynamicSignal *signal = _parser->_pool.New<Object::DynamicSignal>();
931         signal->name = node->name;
932
933         AST::UiParameterList *p = node->parameters;
934         int paramLength = 0;
935         while (p) { paramLength++; p = p->next; }
936         p = node->parameters;
937
938         if (paramLength) {
939             signal->parameterTypes = _parser->_pool.NewRawList<Object::DynamicProperty::Type>(paramLength);
940             signal->parameterTypeNames = _parser->_pool.NewRawList<QHashedStringRef>(paramLength);
941             signal->parameterNames = _parser->_pool.NewRawList<QHashedStringRef>(paramLength);
942         }
943
944         int index = 0;
945         while (p) {
946             const QStringRef &memberType = p->type;
947
948             if (memberType.isEmpty()) {
949                 QQmlError error;
950                 error.setDescription(QCoreApplication::translate("QQmlParser","Expected parameter type"));
951                 error.setLine(node->typeToken.startLine);
952                 error.setColumn(node->typeToken.startColumn);
953                 _parser->_errors << error;
954                 return false;
955             }
956
957             const TypeNameToType *type = 0;
958             for(int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) {
959                 const TypeNameToType *t = propTypeNameToTypes + typeIndex;
960                 if (t->nameLength == size_t(memberType.length()) &&
961                     QHashedString::compare(memberType.constData(), t->name, t->nameLength)) {
962                     type = t;
963                     break;
964                 }
965             }
966
967             if (!type) {
968                 if (memberType.at(0).isUpper()) {
969                     // Must be a QML object type.
970                     // Lazily determine type during compilation.
971                     signal->parameterTypes[index] = Object::DynamicProperty::Custom;
972                     signal->parameterTypeNames[index] = QHashedStringRef(p->type);
973                 } else {
974                     QQmlError error;
975                     QString errStr = QCoreApplication::translate("QQmlParser","Invalid signal parameter type: ");
976                     errStr.append(memberType.toString());
977                     error.setDescription(errStr);
978                     error.setLine(node->typeToken.startLine);
979                     error.setColumn(node->typeToken.startColumn);
980                     _parser->_errors << error;
981                     return false;
982                 }
983             } else {
984                 // the parameter is a known basic type
985                 signal->parameterTypes[index] = type->type;
986             }
987
988             signal->parameterNames[index] = QHashedStringRef(p->name);
989             p = p->next;
990             index++;
991         }
992
993         signal->location = location(node->typeToken, node->semicolonToken);
994         _stateStack.top().object->dynamicSignals.append(signal);
995     } else {
996         const QStringRef &memberType = node->memberType;
997         const QStringRef &name = node->name;
998
999         bool typeFound = false;
1000         Object::DynamicProperty::Type type;
1001
1002         if ((unsigned)memberType.length() == strlen("alias") && 
1003             QHashedString::compare(memberType.constData(), "alias", strlen("alias"))) {
1004             type = Object::DynamicProperty::Alias;
1005             typeFound = true;
1006         } 
1007
1008         for(int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) {
1009             const TypeNameToType *t = propTypeNameToTypes + ii;
1010             if (t->nameLength == size_t(memberType.length()) &&
1011                 QHashedString::compare(memberType.constData(), t->name, t->nameLength)) {
1012                 type = t->type;
1013                 typeFound = true;
1014             }
1015         }
1016
1017         if (!typeFound && memberType.at(0).isUpper()) {
1018             const QStringRef &typeModifier = node->typeModifier;
1019
1020             if (typeModifier.isEmpty()) {
1021                 type = Object::DynamicProperty::Custom;
1022             } else if((unsigned)typeModifier.length() == strlen("list") && 
1023                       QHashedString::compare(typeModifier.constData(), "list", strlen("list"))) {
1024                 type = Object::DynamicProperty::CustomList;
1025             } else {
1026                 QQmlError error;
1027                 error.setDescription(QCoreApplication::translate("QQmlParser","Invalid property type modifier"));
1028                 error.setLine(node->typeModifierToken.startLine);
1029                 error.setColumn(node->typeModifierToken.startColumn);
1030                 _parser->_errors << error;
1031                 return false;
1032             }
1033             typeFound = true;
1034         } else if (!node->typeModifier.isNull()) {
1035             QQmlError error;
1036             error.setDescription(QCoreApplication::translate("QQmlParser","Unexpected property type modifier"));
1037             error.setLine(node->typeModifierToken.startLine);
1038             error.setColumn(node->typeModifierToken.startColumn);
1039             _parser->_errors << error;
1040             return false;
1041         }
1042
1043         if(!typeFound) {
1044             QQmlError error;
1045             error.setDescription(QCoreApplication::translate("QQmlParser","Expected property type"));
1046             error.setLine(node->typeToken.startLine);
1047             error.setColumn(node->typeToken.startColumn);
1048             _parser->_errors << error;
1049             return false;
1050         }
1051
1052         Object::DynamicProperty *property = _parser->_pool.New<Object::DynamicProperty>();
1053         property->isDefaultProperty = node->isDefaultMember;
1054         property->isReadOnly = node->isReadonlyMember;
1055         property->type = type;
1056         property->nameLocation.line = node->identifierToken.startLine;
1057         property->nameLocation.column = node->identifierToken.startColumn;
1058         if (type >= Object::DynamicProperty::Custom) {
1059             // This forces the type to be added to the resolved types list
1060             _parser->findOrCreateTypeId(memberType.toString(), _stateStack.top().object);
1061             property->customType = memberType;
1062         }
1063
1064         property->name = QHashedStringRef(name);
1065         property->location = location(node->firstSourceLocation(),
1066                                       node->lastSourceLocation());
1067
1068         if (node->statement) { // default value
1069             property->defaultValue = _parser->_pool.New<Property>();
1070             property->defaultValue->parent = _stateStack.top().object;
1071             property->defaultValue->location =
1072                     location(node->statement->firstSourceLocation(),
1073                              node->statement->lastSourceLocation());
1074             QQmlScript::Value *value = _parser->_pool.New<QQmlScript::Value>();
1075             value->location = location(node->statement->firstSourceLocation(),
1076                                        node->statement->lastSourceLocation());
1077             value->value = getVariant(node->statement);
1078             property->defaultValue->values.append(value);
1079         }
1080
1081         _stateStack.top().object->dynamicProperties.append(property);
1082
1083         // process QML-like initializers (e.g. property Object o: Object {})
1084         accept(node->binding);
1085     }
1086
1087     return false;
1088 }
1089
1090
1091 // UiObjectMember: UiQualifiedId UiObjectInitializer ;
1092 bool ProcessAST::visit(AST::UiObjectDefinition *node)
1093 {
1094     LocationSpan l = location(node->firstSourceLocation(),
1095                               node->lastSourceLocation());
1096
1097     const QString objectType = asString(node->qualifiedTypeNameId);
1098     const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken;
1099
1100     defineObjectBinding(/*propertyName = */ 0, false, objectType,
1101                         typeLocation, l, node->initializer);
1102
1103     return false;
1104 }
1105
1106
1107 // UiObjectMember: UiQualifiedId T_COLON UiQualifiedId UiObjectInitializer ;
1108 bool ProcessAST::visit(AST::UiObjectBinding *node)
1109 {
1110     LocationSpan l = location(node->qualifiedTypeNameId->identifierToken,
1111                               node->initializer->rbraceToken);
1112
1113     const QString objectType = asString(node->qualifiedTypeNameId);
1114     const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken;
1115
1116     defineObjectBinding(node->qualifiedId, node->hasOnToken, objectType, 
1117                         typeLocation, l, node->initializer);
1118
1119     return false;
1120 }
1121
1122 QQmlScript::Variant ProcessAST::getVariant(AST::Statement *stmt)
1123 {
1124     if (stmt) {
1125         if (AST::ExpressionStatement *exprStmt = AST::cast<AST::ExpressionStatement *>(stmt))
1126             return getVariant(exprStmt->expression);
1127
1128         return QQmlScript::Variant(asStringRef(stmt), stmt);
1129     }
1130
1131     return QQmlScript::Variant();
1132 }
1133
1134 QQmlScript::Variant ProcessAST::getVariant(AST::ExpressionNode *expr)
1135 {
1136     if (AST::StringLiteral *lit = AST::cast<AST::StringLiteral *>(expr)) {
1137         return QQmlScript::Variant(lit);
1138     } else if (expr->kind == AST::Node::Kind_TrueLiteral) {
1139         return QQmlScript::Variant(true);
1140     } else if (expr->kind == AST::Node::Kind_FalseLiteral) {
1141         return QQmlScript::Variant(false);
1142     } else if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(expr)) {
1143         return QQmlScript::Variant(lit->value, asStringRef(expr));
1144     } else {
1145
1146         if (AST::UnaryMinusExpression *unaryMinus = AST::cast<AST::UnaryMinusExpression *>(expr)) {
1147            if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(unaryMinus->expression)) {
1148                return QQmlScript::Variant(-lit->value, asStringRef(expr));
1149            }
1150         }
1151
1152         return  QQmlScript::Variant(asStringRef(expr), expr);
1153     }
1154 }
1155
1156
1157 // UiObjectMember: UiQualifiedId T_COLON Statement ;
1158 bool ProcessAST::visit(AST::UiScriptBinding *node)
1159 {
1160     int propertyCount = 0;
1161     AST::UiQualifiedId *propertyName = node->qualifiedId;
1162     for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
1163         ++propertyCount;
1164         _stateStack.pushProperty(name->name,
1165                                  location(name));
1166     }
1167
1168     Property *prop = currentProperty();
1169
1170     if (!prop->values.isEmpty()) {
1171         QQmlError error;
1172         error.setDescription(QCoreApplication::translate("QQmlParser","Property value set multiple times"));
1173         error.setLine(this->location(propertyName).start.line);
1174         error.setColumn(this->location(propertyName).start.column);
1175         _parser->_errors << error;
1176         return 0;
1177     }
1178
1179     QQmlScript::Variant primitive;
1180
1181     if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(node->statement)) {
1182         primitive = getVariant(stmt->expression);
1183     } else { // do binding
1184         primitive = QQmlScript::Variant(asStringRef(node->statement), node->statement);
1185     }
1186
1187     prop->location.range.length = prop->location.range.offset + prop->location.range.length - node->qualifiedId->identifierToken.offset;
1188     prop->location.range.offset = node->qualifiedId->identifierToken.offset;
1189     QQmlScript::Value *v = _parser->_pool.New<QQmlScript::Value>();
1190     v->value = primitive;
1191     v->location = location(node->statement->firstSourceLocation(),
1192                            node->statement->lastSourceLocation());
1193
1194     prop->addValue(v);
1195
1196     while (propertyCount--)
1197         _stateStack.pop();
1198
1199     return false;
1200 }
1201
1202 // UiObjectMember: UiQualifiedId T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ;
1203 bool ProcessAST::visit(AST::UiArrayBinding *node)
1204 {
1205     int propertyCount = 0;
1206     AST::UiQualifiedId *propertyName = node->qualifiedId;
1207     for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
1208         ++propertyCount;
1209         _stateStack.pushProperty(name->name,
1210                                  location(name));
1211     }
1212
1213     Property* prop = currentProperty();
1214
1215     if (!prop->values.isEmpty()) {
1216         QQmlError error;
1217         error.setDescription(QCoreApplication::translate("QQmlParser","Property value set multiple times"));
1218         error.setLine(this->location(propertyName).start.line);
1219         error.setColumn(this->location(propertyName).start.column);
1220         _parser->_errors << error;
1221         return false;
1222     }
1223
1224     accept(node->members);
1225
1226     // For the DOM, store the position of the T_LBRACKET upto the T_RBRACKET as the range:
1227     prop->listValueRange.offset = node->lbracketToken.offset;
1228     prop->listValueRange.length = node->rbracketToken.offset + node->rbracketToken.length - node->lbracketToken.offset;
1229
1230     while (propertyCount--)
1231         _stateStack.pop();
1232
1233     return false;
1234 }
1235
1236 bool ProcessAST::visit(AST::UiSourceElement *node)
1237 {
1238     QQmlScript::Object *obj = currentObject();
1239
1240     if (AST::FunctionDeclaration *funDecl = AST::cast<AST::FunctionDeclaration *>(node->sourceElement)) {
1241
1242         Object::DynamicSlot *slot = _parser->_pool.New<Object::DynamicSlot>();
1243         slot->location = location(funDecl->identifierToken, funDecl->lastSourceLocation());
1244
1245         AST::FormalParameterList *f = funDecl->formals;
1246         while (f) {
1247             slot->parameterNames << f->name.toUtf8();
1248             f = f->next;
1249         }
1250
1251         AST::SourceLocation loc = funDecl->rparenToken;
1252         loc.offset = loc.end();
1253         loc.startColumn += 1;
1254         QString body = textAt(loc, funDecl->rbraceToken);
1255         slot->name = funDecl->name;
1256         slot->body = body;
1257         obj->dynamicSlots.append(slot);
1258
1259     } else {
1260         QQmlError error;
1261         error.setDescription(QCoreApplication::translate("QQmlParser","JavaScript declaration outside Script element"));
1262         error.setLine(node->firstSourceLocation().startLine);
1263         error.setColumn(node->firstSourceLocation().startColumn);
1264         _parser->_errors << error;
1265     }
1266     return false;
1267 }
1268
1269 } // end of anonymous namespace
1270
1271
1272 QQmlScript::Parser::Parser()
1273 : root(0), data(0)
1274 {
1275
1276 }
1277
1278 QQmlScript::Parser::~Parser()
1279 {
1280     clear();
1281 }
1282
1283 namespace QQmlScript {
1284 class ParserJsASTData
1285 {
1286 public:
1287     ParserJsASTData(const QString &filename)
1288     : filename(filename) {}
1289
1290     QString filename;
1291     Engine engine;
1292 };
1293 }
1294
1295 QByteArray QQmlScript::Parser::preparseData() const
1296 {
1297     return QByteArray();
1298 }
1299
1300 bool QQmlScript::Parser::parse(const QString &qmlcode, const QByteArray & /* preparseData */,
1301                                const QUrl &url, const QString &urlString)
1302 {
1303     clear();
1304
1305     if (urlString.isEmpty()) {
1306         _scriptFile = url.toString();
1307     } else {
1308        // Q_ASSERT(urlString == url.toString());
1309         _scriptFile = urlString;
1310     }
1311
1312     QString *code = _pool.NewString(qmlcode);
1313
1314     data = new QQmlScript::ParserJsASTData(_scriptFile);
1315
1316     Lexer lexer(&data->engine);
1317     lexer.setCode(*code, /*line = */ 1);
1318
1319     QQmlJS::Parser parser(&data->engine);
1320
1321     if (! parser.parse() || !_errors.isEmpty()) {
1322
1323         // Extract errors from the parser
1324         foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) {
1325
1326             if (m.isWarning())
1327                 continue;
1328
1329             QQmlError error;
1330             error.setUrl(url);
1331             error.setDescription(m.message);
1332             error.setLine(m.loc.startLine);
1333             error.setColumn(m.loc.startColumn);
1334             _errors << error;
1335
1336         }
1337     }
1338
1339     if (_errors.isEmpty()) {
1340         ProcessAST process(this);
1341         process(*code, parser.ast());
1342
1343         // Set the url for process errors
1344         for(int ii = 0; ii < _errors.count(); ++ii)
1345             _errors[ii].setUrl(url);
1346     }
1347
1348     return _errors.isEmpty();
1349 }
1350
1351 QList<QQmlScript::TypeReference*> QQmlScript::Parser::referencedTypes() const
1352 {
1353     return _refTypes;
1354 }
1355
1356 QQmlScript::Object *QQmlScript::Parser::tree() const
1357 {
1358     return root;
1359 }
1360
1361 QList<QQmlScript::Import> QQmlScript::Parser::imports() const
1362 {
1363     return _imports;
1364 }
1365
1366 QList<QQmlError> QQmlScript::Parser::errors() const
1367 {
1368     return _errors;
1369 }
1370
1371 static void replaceWithSpace(QString &str, int idx, int n) 
1372 {
1373     QChar *data = str.data() + idx;
1374     const QChar space(QLatin1Char(' '));
1375     for (int ii = 0; ii < n; ++ii)
1376         *data++ = space;
1377 }
1378
1379 static QQmlScript::LocationSpan
1380 locationFromLexer(const QQmlJS::Lexer &lex, int startLine, int startColumn, int startOffset)
1381 {
1382     QQmlScript::LocationSpan l;
1383
1384     l.start.line = startLine; l.start.column = startColumn;
1385     l.end.line = lex.tokenEndLine(); l.end.column = lex.tokenEndColumn();
1386     l.range.offset = startOffset;
1387     l.range.length = lex.tokenOffset() + lex.tokenLength() - startOffset;
1388
1389     return l;
1390 }
1391
1392 /*
1393 Searches for ".pragma <value>" declarations within \a script.  Currently supported pragmas
1394 are:
1395     library
1396 */
1397 QQmlScript::Object::ScriptBlock::Pragmas QQmlScript::Parser::extractPragmas(QString &script)
1398 {
1399     QQmlScript::Object::ScriptBlock::Pragmas rv = QQmlScript::Object::ScriptBlock::None;
1400
1401     const QString pragma(QLatin1String("pragma"));
1402     const QString library(QLatin1String("library"));
1403
1404     QQmlJS::Lexer l(0);
1405     l.setCode(script, 0);
1406
1407     int token = l.lex();
1408
1409     while (true) {
1410         if (token != QQmlJSGrammar::T_DOT)
1411             return rv;
1412
1413         int startOffset = l.tokenOffset();
1414         int startLine = l.tokenStartLine();
1415
1416         token = l.lex();
1417
1418         if (token != QQmlJSGrammar::T_IDENTIFIER ||
1419             l.tokenStartLine() != startLine ||
1420             script.mid(l.tokenOffset(), l.tokenLength()) != pragma)
1421             return rv;
1422
1423         token = l.lex();
1424
1425         if (token != QQmlJSGrammar::T_IDENTIFIER ||
1426             l.tokenStartLine() != startLine)
1427             return rv;
1428
1429         QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
1430         int endOffset = l.tokenLength() + l.tokenOffset();
1431
1432         token = l.lex();
1433         if (l.tokenStartLine() == startLine)
1434             return rv;
1435
1436         if (pragmaValue == library) {
1437             rv |= QQmlScript::Object::ScriptBlock::Shared;
1438             replaceWithSpace(script, startOffset, endOffset - startOffset);
1439         } else {
1440             return rv;
1441         }
1442     }
1443     return rv;
1444 }
1445
1446 #define CHECK_LINE if (l.tokenStartLine() != startLine) return rv;
1447 #define CHECK_TOKEN(t) if (token != QQmlJSGrammar:: t) return rv;
1448
1449 static const int uriTokens[] = {
1450     QQmlJSGrammar::T_IDENTIFIER, 
1451     QQmlJSGrammar::T_PROPERTY, 
1452     QQmlJSGrammar::T_SIGNAL, 
1453     QQmlJSGrammar::T_READONLY, 
1454     QQmlJSGrammar::T_ON, 
1455     QQmlJSGrammar::T_BREAK, 
1456     QQmlJSGrammar::T_CASE, 
1457     QQmlJSGrammar::T_CATCH, 
1458     QQmlJSGrammar::T_CONTINUE, 
1459     QQmlJSGrammar::T_DEFAULT, 
1460     QQmlJSGrammar::T_DELETE, 
1461     QQmlJSGrammar::T_DO, 
1462     QQmlJSGrammar::T_ELSE, 
1463     QQmlJSGrammar::T_FALSE, 
1464     QQmlJSGrammar::T_FINALLY, 
1465     QQmlJSGrammar::T_FOR, 
1466     QQmlJSGrammar::T_FUNCTION, 
1467     QQmlJSGrammar::T_IF, 
1468     QQmlJSGrammar::T_IN, 
1469     QQmlJSGrammar::T_INSTANCEOF, 
1470     QQmlJSGrammar::T_NEW, 
1471     QQmlJSGrammar::T_NULL, 
1472     QQmlJSGrammar::T_RETURN, 
1473     QQmlJSGrammar::T_SWITCH, 
1474     QQmlJSGrammar::T_THIS, 
1475     QQmlJSGrammar::T_THROW, 
1476     QQmlJSGrammar::T_TRUE, 
1477     QQmlJSGrammar::T_TRY, 
1478     QQmlJSGrammar::T_TYPEOF, 
1479     QQmlJSGrammar::T_VAR, 
1480     QQmlJSGrammar::T_VOID, 
1481     QQmlJSGrammar::T_WHILE, 
1482     QQmlJSGrammar::T_CONST, 
1483     QQmlJSGrammar::T_DEBUGGER, 
1484     QQmlJSGrammar::T_RESERVED_WORD, 
1485     QQmlJSGrammar::T_WITH, 
1486
1487     QQmlJSGrammar::EOF_SYMBOL
1488 };
1489 static inline bool isUriToken(int token)
1490 {
1491     const int *current = uriTokens;
1492     while (*current != QQmlJSGrammar::EOF_SYMBOL) {
1493         if (*current == token)
1494             return true;
1495         ++current;
1496     }
1497     return false;
1498 }
1499
1500 QQmlScript::Parser::JavaScriptMetaData QQmlScript::Parser::extractMetaData(QString &script, QQmlError *error)
1501 {
1502     Q_ASSERT(error);
1503
1504     JavaScriptMetaData rv;
1505
1506     QQmlScript::Object::ScriptBlock::Pragmas &pragmas = rv.pragmas;
1507
1508     const QString pragma(QLatin1String("pragma"));
1509     const QString js(QLatin1String(".js"));
1510     const QString library(QLatin1String("library"));
1511
1512     QQmlJS::Lexer l(0);
1513     l.setCode(script, 0);
1514
1515     int token = l.lex();
1516
1517     while (true) {
1518         if (token != QQmlJSGrammar::T_DOT)
1519             return rv;
1520
1521         int startOffset = l.tokenOffset();
1522         int startLine = l.tokenStartLine();
1523         int startColumn = l.tokenStartColumn();
1524
1525         QQmlError importError;
1526         importError.setLine(startLine + 1); // 0-based, adjust to be 1-based
1527
1528         token = l.lex();
1529
1530         CHECK_LINE;
1531
1532         if (token == QQmlJSGrammar::T_IMPORT) {
1533
1534             // .import <URI> <Version> as <Identifier>
1535             // .import <file.js> as <Identifier>
1536
1537             token = l.lex();
1538
1539             CHECK_LINE;
1540
1541             if (token == QQmlJSGrammar::T_STRING_LITERAL) {
1542
1543                 QString file = l.tokenText();
1544
1545                 if (!file.endsWith(js)) {
1546                     importError.setDescription(QCoreApplication::translate("QQmlParser","Imported file must be a script"));
1547                     importError.setColumn(l.tokenStartColumn());
1548                     *error = importError;
1549                     return rv;
1550                 }
1551
1552                 bool invalidImport = false;
1553
1554                 token = l.lex();
1555
1556                 if ((token != QQmlJSGrammar::T_AS) || (l.tokenStartLine() != startLine)) {
1557                     invalidImport = true;
1558                 } else {
1559                     token = l.lex();
1560
1561                     if ((token != QQmlJSGrammar::T_IDENTIFIER) || (l.tokenStartLine() != startLine))
1562                         invalidImport = true;
1563                 }
1564
1565
1566                 if (invalidImport) {
1567                     importError.setDescription(QCoreApplication::translate("QQmlParser","File import requires a qualifier"));
1568                     importError.setColumn(l.tokenStartColumn());
1569                     *error = importError;
1570                     return rv;
1571                 }
1572
1573                 int endOffset = l.tokenLength() + l.tokenOffset();
1574
1575                 QString importId = script.mid(l.tokenOffset(), l.tokenLength());
1576
1577                 QQmlScript::LocationSpan location =
1578                     locationFromLexer(l, startLine, startColumn, startOffset);
1579
1580                 token = l.lex();
1581
1582                 if (!importId.at(0).isUpper() || (l.tokenStartLine() == startLine)) {
1583                     importError.setDescription(QCoreApplication::translate("QQmlParser","Invalid import qualifier"));
1584                     importError.setColumn(l.tokenStartColumn());
1585                     *error = importError;
1586                     return rv;
1587                 }
1588
1589                 replaceWithSpace(script, startOffset, endOffset - startOffset);
1590
1591                 Import import;
1592                 import.type = Import::Script;
1593                 import.uri = file;
1594                 import.qualifier = importId;
1595                 import.location = location;
1596
1597                 rv.imports << import;
1598             } else {
1599                 // URI
1600                 QString uri;
1601
1602                 while (true) {
1603                     if (!isUriToken(token)) {
1604                         importError.setDescription(QCoreApplication::translate("QQmlParser","Invalid module URI"));
1605                         importError.setColumn(l.tokenStartColumn());
1606                         *error = importError;
1607                         return rv;
1608                     }
1609
1610                     uri.append(l.tokenText());
1611
1612                     token = l.lex();
1613                     CHECK_LINE;
1614                     if (token != QQmlJSGrammar::T_DOT)
1615                         break;
1616
1617                     uri.append(QLatin1Char('.'));
1618
1619                     token = l.lex();
1620                     CHECK_LINE;
1621                 }
1622
1623                 if (token != QQmlJSGrammar::T_NUMERIC_LITERAL) {
1624                     importError.setDescription(QCoreApplication::translate("QQmlParser","Module import requires a version"));
1625                     importError.setColumn(l.tokenStartColumn());
1626                     *error = importError;
1627                     return rv;
1628                 }
1629
1630                 int vmaj, vmin;
1631                 ProcessAST::extractVersion(QStringRef(&script, l.tokenOffset(), l.tokenLength()),
1632                                            &vmaj, &vmin);
1633
1634                 bool invalidImport = false;
1635
1636                 token = l.lex();
1637
1638                 if ((token != QQmlJSGrammar::T_AS) || (l.tokenStartLine() != startLine)) {
1639                     invalidImport = true;
1640                 } else {
1641                     token = l.lex();
1642
1643                     if ((token != QQmlJSGrammar::T_IDENTIFIER) || (l.tokenStartLine() != startLine))
1644                         invalidImport = true;
1645                 }
1646
1647
1648                 if (invalidImport) {
1649                     importError.setDescription(QCoreApplication::translate("QQmlParser","Module import requires a qualifier"));
1650                     importError.setColumn(l.tokenStartColumn());
1651                     *error = importError;
1652                     return rv;
1653                 }
1654
1655                 int endOffset = l.tokenLength() + l.tokenOffset();
1656
1657                 QString importId = script.mid(l.tokenOffset(), l.tokenLength());
1658
1659                 QQmlScript::LocationSpan location =
1660                     locationFromLexer(l, startLine, startColumn, startOffset);
1661
1662                 token = l.lex();
1663
1664                 if (!importId.at(0).isUpper() || (l.tokenStartLine() == startLine)) {
1665                     importError.setDescription(QCoreApplication::translate("QQmlParser","Invalid import qualifier"));
1666                     importError.setColumn(l.tokenStartColumn());
1667                     *error = importError;
1668                     return rv;
1669                 }
1670
1671                 replaceWithSpace(script, startOffset, endOffset - startOffset);
1672
1673                 Import import;
1674                 import.type = Import::Library;
1675                 import.uri = uri;
1676                 import.majorVersion = vmaj;
1677                 import.minorVersion = vmin;
1678                 import.qualifier = importId;
1679                 import.location = location;
1680
1681                 rv.imports << import;
1682             }
1683
1684         } else if (token == QQmlJSGrammar::T_IDENTIFIER &&
1685                    script.mid(l.tokenOffset(), l.tokenLength()) == pragma) {
1686
1687             token = l.lex();
1688
1689             CHECK_TOKEN(T_IDENTIFIER);
1690             CHECK_LINE;
1691
1692             QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
1693             int endOffset = l.tokenLength() + l.tokenOffset();
1694
1695             if (pragmaValue == library) {
1696                 pragmas |= QQmlScript::Object::ScriptBlock::Shared;
1697                 replaceWithSpace(script, startOffset, endOffset - startOffset);
1698             } else {
1699                 return rv;
1700             }
1701
1702             token = l.lex();
1703             if (l.tokenStartLine() == startLine)
1704                 return rv;
1705
1706         } else {
1707             return rv;
1708         }
1709     }
1710     return rv;
1711 }
1712
1713 void QQmlScript::Parser::clear()
1714 {
1715     _imports.clear();
1716     _refTypes.clear();
1717     _errors.clear();
1718
1719     if (data) {
1720         delete data;
1721         data = 0;
1722     }
1723
1724     _pool.clear();
1725 }
1726
1727 int QQmlScript::Parser::findOrCreateTypeId(const QString &name, Object *object)
1728 {
1729     for (int ii = 0; ii < _refTypes.size(); ++ii) {
1730         if (_refTypes.at(ii)->name == name)
1731             return ii;
1732     }
1733
1734     TypeReference *type = _pool.New<TypeReference>();
1735     type->name = name;
1736     type->firstUse = object;
1737     _refTypes.append(type);
1738     return _refTypes.size() - 1;
1739 }
1740
1741 void QQmlScript::Parser::setTree(QQmlScript::Object *tree)
1742 {
1743     Q_ASSERT(! root);
1744
1745     root = tree;
1746 }
1747
1748 QT_END_NAMESPACE