a237f9a2f80b2f7e96adf5a6171a2d6cec16135b
[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 == -1) {
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->parameterNames = _parser->_pool.NewRawList<QHashedStringRef>(paramLength);
941         }
942
943         int index = 0;
944         while (p) {
945             const QStringRef &memberType = p->type;
946
947             const TypeNameToType *type = 0;
948             for(int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) {
949                 const TypeNameToType *t = propTypeNameToTypes + typeIndex;
950                 if (t->nameLength == size_t(memberType.length()) &&
951                     QHashedString::compare(memberType.constData(), t->name, t->nameLength)) {
952                     type = t;
953                     break;
954                 }
955             }
956
957             if (!type) {
958                 QQmlError error;
959                 error.setDescription(QCoreApplication::translate("QQmlParser","Expected parameter type"));
960                 error.setLine(node->typeToken.startLine);
961                 error.setColumn(node->typeToken.startColumn);
962                 _parser->_errors << error;
963                 return false;
964             }
965             
966             signal->parameterTypes[index] = type->type;
967             signal->parameterNames[index] = QHashedStringRef(p->name);
968             p = p->next;
969             index++;
970         }
971
972         signal->location = location(node->typeToken, node->semicolonToken);
973         _stateStack.top().object->dynamicSignals.append(signal);
974     } else {
975         const QStringRef &memberType = node->memberType;
976         const QStringRef &name = node->name;
977
978         bool typeFound = false;
979         Object::DynamicProperty::Type type;
980
981         if ((unsigned)memberType.length() == strlen("alias") && 
982             QHashedString::compare(memberType.constData(), "alias", strlen("alias"))) {
983             type = Object::DynamicProperty::Alias;
984             typeFound = true;
985         } 
986
987         for(int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) {
988             const TypeNameToType *t = propTypeNameToTypes + ii;
989             if (t->nameLength == size_t(memberType.length()) &&
990                 QHashedString::compare(memberType.constData(), t->name, t->nameLength)) {
991                 type = t->type;
992                 typeFound = true;
993             }
994         }
995
996         if (!typeFound && memberType.at(0).isUpper()) {
997             const QStringRef &typeModifier = node->typeModifier;
998
999             if (typeModifier.isEmpty()) {
1000                 type = Object::DynamicProperty::Custom;
1001             } else if((unsigned)typeModifier.length() == strlen("list") && 
1002                       QHashedString::compare(typeModifier.constData(), "list", strlen("list"))) {
1003                 type = Object::DynamicProperty::CustomList;
1004             } else {
1005                 QQmlError error;
1006                 error.setDescription(QCoreApplication::translate("QQmlParser","Invalid property type modifier"));
1007                 error.setLine(node->typeModifierToken.startLine);
1008                 error.setColumn(node->typeModifierToken.startColumn);
1009                 _parser->_errors << error;
1010                 return false;
1011             }
1012             typeFound = true;
1013         } else if (!node->typeModifier.isNull()) {
1014             QQmlError error;
1015             error.setDescription(QCoreApplication::translate("QQmlParser","Unexpected property type modifier"));
1016             error.setLine(node->typeModifierToken.startLine);
1017             error.setColumn(node->typeModifierToken.startColumn);
1018             _parser->_errors << error;
1019             return false;
1020         }
1021
1022         if(!typeFound) {
1023             QQmlError error;
1024             error.setDescription(QCoreApplication::translate("QQmlParser","Expected property type"));
1025             error.setLine(node->typeToken.startLine);
1026             error.setColumn(node->typeToken.startColumn);
1027             _parser->_errors << error;
1028             return false;
1029         }
1030
1031         Object::DynamicProperty *property = _parser->_pool.New<Object::DynamicProperty>();
1032         property->isDefaultProperty = node->isDefaultMember;
1033         property->isReadOnly = node->isReadonlyMember;
1034         property->type = type;
1035         property->nameLocation.line = node->identifierToken.startLine;
1036         property->nameLocation.column = node->identifierToken.startColumn;
1037         if (type >= Object::DynamicProperty::Custom) {
1038             // This forces the type to be added to the resolved types list
1039             _parser->findOrCreateTypeId(memberType.toString(), _stateStack.top().object);
1040             property->customType = memberType;
1041         }
1042
1043         property->name = QHashedStringRef(name);
1044         property->location = location(node->firstSourceLocation(),
1045                                       node->lastSourceLocation());
1046
1047         if (node->statement) { // default value
1048             property->defaultValue = _parser->_pool.New<Property>();
1049             property->defaultValue->parent = _stateStack.top().object;
1050             property->defaultValue->location =
1051                     location(node->statement->firstSourceLocation(),
1052                              node->statement->lastSourceLocation());
1053             QQmlScript::Value *value = _parser->_pool.New<QQmlScript::Value>();
1054             value->location = location(node->statement->firstSourceLocation(),
1055                                        node->statement->lastSourceLocation());
1056             value->value = getVariant(node->statement);
1057             property->defaultValue->values.append(value);
1058         }
1059
1060         _stateStack.top().object->dynamicProperties.append(property);
1061
1062         // process QML-like initializers (e.g. property Object o: Object {})
1063         accept(node->binding);
1064     }
1065
1066     return false;
1067 }
1068
1069
1070 // UiObjectMember: UiQualifiedId UiObjectInitializer ;
1071 bool ProcessAST::visit(AST::UiObjectDefinition *node)
1072 {
1073     LocationSpan l = location(node->firstSourceLocation(),
1074                               node->lastSourceLocation());
1075
1076     const QString objectType = asString(node->qualifiedTypeNameId);
1077     const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken;
1078
1079     defineObjectBinding(/*propertyName = */ 0, false, objectType,
1080                         typeLocation, l, node->initializer);
1081
1082     return false;
1083 }
1084
1085
1086 // UiObjectMember: UiQualifiedId T_COLON UiQualifiedId UiObjectInitializer ;
1087 bool ProcessAST::visit(AST::UiObjectBinding *node)
1088 {
1089     LocationSpan l = location(node->qualifiedTypeNameId->identifierToken,
1090                               node->initializer->rbraceToken);
1091
1092     const QString objectType = asString(node->qualifiedTypeNameId);
1093     const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken;
1094
1095     defineObjectBinding(node->qualifiedId, node->hasOnToken, objectType, 
1096                         typeLocation, l, node->initializer);
1097
1098     return false;
1099 }
1100
1101 QQmlScript::Variant ProcessAST::getVariant(AST::Statement *stmt)
1102 {
1103     if (stmt) {
1104         if (AST::ExpressionStatement *exprStmt = AST::cast<AST::ExpressionStatement *>(stmt))
1105             return getVariant(exprStmt->expression);
1106
1107         return QQmlScript::Variant(asStringRef(stmt), stmt);
1108     }
1109
1110     return QQmlScript::Variant();
1111 }
1112
1113 QQmlScript::Variant ProcessAST::getVariant(AST::ExpressionNode *expr)
1114 {
1115     if (AST::StringLiteral *lit = AST::cast<AST::StringLiteral *>(expr)) {
1116         return QQmlScript::Variant(lit);
1117     } else if (expr->kind == AST::Node::Kind_TrueLiteral) {
1118         return QQmlScript::Variant(true);
1119     } else if (expr->kind == AST::Node::Kind_FalseLiteral) {
1120         return QQmlScript::Variant(false);
1121     } else if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(expr)) {
1122         return QQmlScript::Variant(lit->value, asStringRef(expr));
1123     } else {
1124
1125         if (AST::UnaryMinusExpression *unaryMinus = AST::cast<AST::UnaryMinusExpression *>(expr)) {
1126            if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(unaryMinus->expression)) {
1127                return QQmlScript::Variant(-lit->value, asStringRef(expr));
1128            }
1129         }
1130
1131         return  QQmlScript::Variant(asStringRef(expr), expr);
1132     }
1133 }
1134
1135
1136 // UiObjectMember: UiQualifiedId T_COLON Statement ;
1137 bool ProcessAST::visit(AST::UiScriptBinding *node)
1138 {
1139     int propertyCount = 0;
1140     AST::UiQualifiedId *propertyName = node->qualifiedId;
1141     for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
1142         ++propertyCount;
1143         _stateStack.pushProperty(name->name,
1144                                  location(name));
1145     }
1146
1147     Property *prop = currentProperty();
1148
1149     if (!prop->values.isEmpty()) {
1150         QQmlError error;
1151         error.setDescription(QCoreApplication::translate("QQmlParser","Property value set multiple times"));
1152         error.setLine(this->location(propertyName).start.line);
1153         error.setColumn(this->location(propertyName).start.column);
1154         _parser->_errors << error;
1155         return 0;
1156     }
1157
1158     QQmlScript::Variant primitive;
1159
1160     if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(node->statement)) {
1161         primitive = getVariant(stmt->expression);
1162     } else { // do binding
1163         primitive = QQmlScript::Variant(asStringRef(node->statement), node->statement);
1164     }
1165
1166     prop->location.range.length = prop->location.range.offset + prop->location.range.length - node->qualifiedId->identifierToken.offset;
1167     prop->location.range.offset = node->qualifiedId->identifierToken.offset;
1168     QQmlScript::Value *v = _parser->_pool.New<QQmlScript::Value>();
1169     v->value = primitive;
1170     v->location = location(node->statement->firstSourceLocation(),
1171                            node->statement->lastSourceLocation());
1172
1173     prop->addValue(v);
1174
1175     while (propertyCount--)
1176         _stateStack.pop();
1177
1178     return false;
1179 }
1180
1181 // UiObjectMember: UiQualifiedId T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ;
1182 bool ProcessAST::visit(AST::UiArrayBinding *node)
1183 {
1184     int propertyCount = 0;
1185     AST::UiQualifiedId *propertyName = node->qualifiedId;
1186     for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
1187         ++propertyCount;
1188         _stateStack.pushProperty(name->name,
1189                                  location(name));
1190     }
1191
1192     Property* prop = currentProperty();
1193
1194     if (!prop->values.isEmpty()) {
1195         QQmlError error;
1196         error.setDescription(QCoreApplication::translate("QQmlParser","Property value set multiple times"));
1197         error.setLine(this->location(propertyName).start.line);
1198         error.setColumn(this->location(propertyName).start.column);
1199         _parser->_errors << error;
1200         return false;
1201     }
1202
1203     accept(node->members);
1204
1205     // For the DOM, store the position of the T_LBRACKET upto the T_RBRACKET as the range:
1206     prop->listValueRange.offset = node->lbracketToken.offset;
1207     prop->listValueRange.length = node->rbracketToken.offset + node->rbracketToken.length - node->lbracketToken.offset;
1208
1209     while (propertyCount--)
1210         _stateStack.pop();
1211
1212     return false;
1213 }
1214
1215 bool ProcessAST::visit(AST::UiSourceElement *node)
1216 {
1217     QQmlScript::Object *obj = currentObject();
1218
1219     if (AST::FunctionDeclaration *funDecl = AST::cast<AST::FunctionDeclaration *>(node->sourceElement)) {
1220
1221         Object::DynamicSlot *slot = _parser->_pool.New<Object::DynamicSlot>();
1222         slot->location = location(funDecl->identifierToken, funDecl->lastSourceLocation());
1223
1224         AST::FormalParameterList *f = funDecl->formals;
1225         while (f) {
1226             slot->parameterNames << f->name.toUtf8();
1227             f = f->next;
1228         }
1229
1230         AST::SourceLocation loc = funDecl->rparenToken;
1231         loc.offset = loc.end();
1232         loc.startColumn += 1;
1233         QString body = textAt(loc, funDecl->rbraceToken);
1234         slot->name = funDecl->name;
1235         slot->body = body;
1236         obj->dynamicSlots.append(slot);
1237
1238     } else {
1239         QQmlError error;
1240         error.setDescription(QCoreApplication::translate("QQmlParser","JavaScript declaration outside Script element"));
1241         error.setLine(node->firstSourceLocation().startLine);
1242         error.setColumn(node->firstSourceLocation().startColumn);
1243         _parser->_errors << error;
1244     }
1245     return false;
1246 }
1247
1248 } // end of anonymous namespace
1249
1250
1251 QQmlScript::Parser::Parser()
1252 : root(0), data(0)
1253 {
1254
1255 }
1256
1257 QQmlScript::Parser::~Parser()
1258 {
1259     clear();
1260 }
1261
1262 namespace QQmlScript {
1263 class ParserJsASTData
1264 {
1265 public:
1266     ParserJsASTData(const QString &filename)
1267     : filename(filename) {}
1268
1269     QString filename;
1270     Engine engine;
1271 };
1272 }
1273
1274 QByteArray QQmlScript::Parser::preparseData() const
1275 {
1276     return QByteArray();
1277 }
1278
1279 bool QQmlScript::Parser::parse(const QString &qmlcode, const QByteArray & /* preparseData */,
1280                                const QUrl &url, const QString &urlString)
1281 {
1282     clear();
1283
1284     if (urlString.isEmpty()) {
1285         _scriptFile = url.toString();
1286     } else {
1287        // Q_ASSERT(urlString == url.toString());
1288         _scriptFile = urlString;
1289     }
1290
1291     QString *code = _pool.NewString(qmlcode);
1292
1293     data = new QQmlScript::ParserJsASTData(_scriptFile);
1294
1295     Lexer lexer(&data->engine);
1296     lexer.setCode(*code, /*line = */ 1);
1297
1298     QQmlJS::Parser parser(&data->engine);
1299
1300     if (! parser.parse() || !_errors.isEmpty()) {
1301
1302         // Extract errors from the parser
1303         foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) {
1304
1305             if (m.isWarning())
1306                 continue;
1307
1308             QQmlError error;
1309             error.setUrl(url);
1310             error.setDescription(m.message);
1311             error.setLine(m.loc.startLine);
1312             error.setColumn(m.loc.startColumn);
1313             _errors << error;
1314
1315         }
1316     }
1317
1318     if (_errors.isEmpty()) {
1319         ProcessAST process(this);
1320         process(*code, parser.ast());
1321
1322         // Set the url for process errors
1323         for(int ii = 0; ii < _errors.count(); ++ii)
1324             _errors[ii].setUrl(url);
1325     }
1326
1327     return _errors.isEmpty();
1328 }
1329
1330 QList<QQmlScript::TypeReference*> QQmlScript::Parser::referencedTypes() const
1331 {
1332     return _refTypes;
1333 }
1334
1335 QQmlScript::Object *QQmlScript::Parser::tree() const
1336 {
1337     return root;
1338 }
1339
1340 QList<QQmlScript::Import> QQmlScript::Parser::imports() const
1341 {
1342     return _imports;
1343 }
1344
1345 QList<QQmlError> QQmlScript::Parser::errors() const
1346 {
1347     return _errors;
1348 }
1349
1350 static void replaceWithSpace(QString &str, int idx, int n) 
1351 {
1352     QChar *data = str.data() + idx;
1353     const QChar space(QLatin1Char(' '));
1354     for (int ii = 0; ii < n; ++ii)
1355         *data++ = space;
1356 }
1357
1358 static QQmlScript::LocationSpan
1359 locationFromLexer(const QQmlJS::Lexer &lex, int startLine, int startColumn, int startOffset)
1360 {
1361     QQmlScript::LocationSpan l;
1362
1363     l.start.line = startLine; l.start.column = startColumn;
1364     l.end.line = lex.tokenEndLine(); l.end.column = lex.tokenEndColumn();
1365     l.range.offset = startOffset;
1366     l.range.length = lex.tokenOffset() + lex.tokenLength() - startOffset;
1367
1368     return l;
1369 }
1370
1371 /*
1372 Searches for ".pragma <value>" declarations within \a script.  Currently supported pragmas
1373 are:
1374     library
1375 */
1376 QQmlScript::Object::ScriptBlock::Pragmas QQmlScript::Parser::extractPragmas(QString &script)
1377 {
1378     QQmlScript::Object::ScriptBlock::Pragmas rv = QQmlScript::Object::ScriptBlock::None;
1379
1380     const QString pragma(QLatin1String("pragma"));
1381     const QString library(QLatin1String("library"));
1382
1383     QQmlJS::Lexer l(0);
1384     l.setCode(script, 0);
1385
1386     int token = l.lex();
1387
1388     while (true) {
1389         if (token != QQmlJSGrammar::T_DOT)
1390             return rv;
1391
1392         int startOffset = l.tokenOffset();
1393         int startLine = l.tokenStartLine();
1394
1395         token = l.lex();
1396
1397         if (token != QQmlJSGrammar::T_IDENTIFIER ||
1398             l.tokenStartLine() != startLine ||
1399             script.mid(l.tokenOffset(), l.tokenLength()) != pragma)
1400             return rv;
1401
1402         token = l.lex();
1403
1404         if (token != QQmlJSGrammar::T_IDENTIFIER ||
1405             l.tokenStartLine() != startLine)
1406             return rv;
1407
1408         QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
1409         int endOffset = l.tokenLength() + l.tokenOffset();
1410
1411         token = l.lex();
1412         if (l.tokenStartLine() == startLine)
1413             return rv;
1414
1415         if (pragmaValue == library) {
1416             rv |= QQmlScript::Object::ScriptBlock::Shared;
1417             replaceWithSpace(script, startOffset, endOffset - startOffset);
1418         } else {
1419             return rv;
1420         }
1421     }
1422     return rv;
1423 }
1424
1425 #define CHECK_LINE if (l.tokenStartLine() != startLine) return rv;
1426 #define CHECK_TOKEN(t) if (token != QQmlJSGrammar:: t) return rv;
1427
1428 static const int uriTokens[] = {
1429     QQmlJSGrammar::T_IDENTIFIER, 
1430     QQmlJSGrammar::T_PROPERTY, 
1431     QQmlJSGrammar::T_SIGNAL, 
1432     QQmlJSGrammar::T_READONLY, 
1433     QQmlJSGrammar::T_ON, 
1434     QQmlJSGrammar::T_BREAK, 
1435     QQmlJSGrammar::T_CASE, 
1436     QQmlJSGrammar::T_CATCH, 
1437     QQmlJSGrammar::T_CONTINUE, 
1438     QQmlJSGrammar::T_DEFAULT, 
1439     QQmlJSGrammar::T_DELETE, 
1440     QQmlJSGrammar::T_DO, 
1441     QQmlJSGrammar::T_ELSE, 
1442     QQmlJSGrammar::T_FALSE, 
1443     QQmlJSGrammar::T_FINALLY, 
1444     QQmlJSGrammar::T_FOR, 
1445     QQmlJSGrammar::T_FUNCTION, 
1446     QQmlJSGrammar::T_IF, 
1447     QQmlJSGrammar::T_IN, 
1448     QQmlJSGrammar::T_INSTANCEOF, 
1449     QQmlJSGrammar::T_NEW, 
1450     QQmlJSGrammar::T_NULL, 
1451     QQmlJSGrammar::T_RETURN, 
1452     QQmlJSGrammar::T_SWITCH, 
1453     QQmlJSGrammar::T_THIS, 
1454     QQmlJSGrammar::T_THROW, 
1455     QQmlJSGrammar::T_TRUE, 
1456     QQmlJSGrammar::T_TRY, 
1457     QQmlJSGrammar::T_TYPEOF, 
1458     QQmlJSGrammar::T_VAR, 
1459     QQmlJSGrammar::T_VOID, 
1460     QQmlJSGrammar::T_WHILE, 
1461     QQmlJSGrammar::T_CONST, 
1462     QQmlJSGrammar::T_DEBUGGER, 
1463     QQmlJSGrammar::T_RESERVED_WORD, 
1464     QQmlJSGrammar::T_WITH, 
1465
1466     QQmlJSGrammar::EOF_SYMBOL
1467 };
1468 static inline bool isUriToken(int token)
1469 {
1470     const int *current = uriTokens;
1471     while (*current != QQmlJSGrammar::EOF_SYMBOL) {
1472         if (*current == token)
1473             return true;
1474         ++current;
1475     }
1476     return false;
1477 }
1478
1479 QQmlScript::Parser::JavaScriptMetaData QQmlScript::Parser::extractMetaData(QString &script, QQmlError *error)
1480 {
1481     Q_ASSERT(error);
1482
1483     JavaScriptMetaData rv;
1484
1485     QQmlScript::Object::ScriptBlock::Pragmas &pragmas = rv.pragmas;
1486
1487     const QString pragma(QLatin1String("pragma"));
1488     const QString js(QLatin1String(".js"));
1489     const QString library(QLatin1String("library"));
1490
1491     QQmlJS::Lexer l(0);
1492     l.setCode(script, 0);
1493
1494     int token = l.lex();
1495
1496     while (true) {
1497         if (token != QQmlJSGrammar::T_DOT)
1498             return rv;
1499
1500         int startOffset = l.tokenOffset();
1501         int startLine = l.tokenStartLine();
1502         int startColumn = l.tokenStartColumn();
1503
1504         QQmlError importError;
1505         importError.setLine(startLine);
1506         importError.setColumn(startColumn);
1507
1508         token = l.lex();
1509
1510         CHECK_LINE;
1511
1512         if (token == QQmlJSGrammar::T_IMPORT) {
1513
1514             // .import <URI> <Version> as <Identifier>
1515             // .import <file.js> as <Identifier>
1516
1517             token = l.lex();
1518
1519             CHECK_LINE;
1520
1521             if (token == QQmlJSGrammar::T_STRING_LITERAL) {
1522
1523                 QString file = l.tokenText();
1524
1525                 if (!file.endsWith(js)) {
1526                     importError.setDescription(QCoreApplication::translate("QQmlParser","Imported file must be a script"));
1527                     *error = importError;
1528                     return rv;
1529                 }
1530
1531                 bool invalidImport = false;
1532
1533                 token = l.lex();
1534
1535                 if ((token != QQmlJSGrammar::T_AS) || (l.tokenStartLine() != startLine)) {
1536                     invalidImport = true;
1537                 } else {
1538                     token = l.lex();
1539
1540                     if ((token != QQmlJSGrammar::T_IDENTIFIER) || (l.tokenStartLine() != startLine))
1541                         invalidImport = true;
1542                 }
1543
1544
1545                 if (invalidImport) {
1546                     importError.setDescription(QCoreApplication::translate("QQmlParser","File import requires a qualifier"));
1547                     *error = importError;
1548                     return rv;
1549                 }
1550
1551                 int endOffset = l.tokenLength() + l.tokenOffset();
1552
1553                 QString importId = script.mid(l.tokenOffset(), l.tokenLength());
1554
1555                 QQmlScript::LocationSpan location =
1556                     locationFromLexer(l, startLine, startColumn, startOffset);
1557
1558                 token = l.lex();
1559
1560                 if (!importId.at(0).isUpper() || (l.tokenStartLine() == startLine)) {
1561                     importError.setDescription(QCoreApplication::translate("QQmlParser","Invalid import qualifier"));
1562                     *error = importError;
1563                     return rv;
1564                 }
1565
1566                 replaceWithSpace(script, startOffset, endOffset - startOffset);
1567
1568                 Import import;
1569                 import.type = Import::Script;
1570                 import.uri = file;
1571                 import.qualifier = importId;
1572                 import.location = location;
1573
1574                 rv.imports << import;
1575             } else {
1576                 // URI
1577                 QString uri;
1578
1579                 while (true) {
1580                     if (!isUriToken(token)) {
1581                         importError.setDescription(QCoreApplication::translate("QQmlParser","Invalid module URI"));
1582                         *error = importError;
1583                         return rv;
1584                     }
1585
1586                     uri.append(l.tokenText());
1587
1588                     token = l.lex();
1589                     CHECK_LINE;
1590                     if (token != QQmlJSGrammar::T_DOT)
1591                         break;
1592
1593                     uri.append(QLatin1Char('.'));
1594
1595                     token = l.lex();
1596                     CHECK_LINE;
1597                 }
1598
1599                 if (token != QQmlJSGrammar::T_NUMERIC_LITERAL) {
1600                     importError.setDescription(QCoreApplication::translate("QQmlParser","Module import requires a version"));
1601                     *error = importError;
1602                     return rv;
1603                 }
1604
1605                 int vmaj, vmin;
1606                 ProcessAST::extractVersion(QStringRef(&script, l.tokenOffset(), l.tokenLength()),
1607                                            &vmaj, &vmin);
1608
1609                 bool invalidImport = false;
1610
1611                 token = l.lex();
1612
1613                 if ((token != QQmlJSGrammar::T_AS) || (l.tokenStartLine() != startLine)) {
1614                     invalidImport = true;
1615                 } else {
1616                     token = l.lex();
1617
1618                     if ((token != QQmlJSGrammar::T_IDENTIFIER) || (l.tokenStartLine() != startLine))
1619                         invalidImport = true;
1620                 }
1621
1622
1623                 if (invalidImport) {
1624                     importError.setDescription(QCoreApplication::translate("QQmlParser","Module import requires a qualifier"));
1625                     *error = importError;
1626                     return rv;
1627                 }
1628
1629                 int endOffset = l.tokenLength() + l.tokenOffset();
1630
1631                 QString importId = script.mid(l.tokenOffset(), l.tokenLength());
1632
1633                 QQmlScript::LocationSpan location =
1634                     locationFromLexer(l, startLine, startColumn, startOffset);
1635
1636                 token = l.lex();
1637
1638                 if (!importId.at(0).isUpper() || (l.tokenStartLine() == startLine)) {
1639                     importError.setDescription(QCoreApplication::translate("QQmlParser","Invalid import qualifier"));
1640                     *error = importError;
1641                     return rv;
1642                 }
1643
1644                 replaceWithSpace(script, startOffset, endOffset - startOffset);
1645
1646                 Import import;
1647                 import.type = Import::Library;
1648                 import.uri = uri;
1649                 import.majorVersion = vmaj;
1650                 import.minorVersion = vmin;
1651                 import.qualifier = importId;
1652                 import.location = location;
1653
1654                 rv.imports << import;
1655             }
1656
1657         } else if (token == QQmlJSGrammar::T_IDENTIFIER &&
1658                    script.mid(l.tokenOffset(), l.tokenLength()) == pragma) {
1659
1660             token = l.lex();
1661
1662             CHECK_TOKEN(T_IDENTIFIER);
1663             CHECK_LINE;
1664
1665             QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
1666             int endOffset = l.tokenLength() + l.tokenOffset();
1667
1668             if (pragmaValue == library) {
1669                 pragmas |= QQmlScript::Object::ScriptBlock::Shared;
1670                 replaceWithSpace(script, startOffset, endOffset - startOffset);
1671             } else {
1672                 return rv;
1673             }
1674
1675             token = l.lex();
1676             if (l.tokenStartLine() == startLine)
1677                 return rv;
1678
1679         } else {
1680             return rv;
1681         }
1682     }
1683     return rv;
1684 }
1685
1686 void QQmlScript::Parser::clear()
1687 {
1688     _imports.clear();
1689     _refTypes.clear();
1690     _errors.clear();
1691
1692     if (data) {
1693         delete data;
1694         data = 0;
1695     }
1696
1697     _pool.clear();
1698 }
1699
1700 int QQmlScript::Parser::findOrCreateTypeId(const QString &name, Object *object)
1701 {
1702     for (int ii = 0; ii < _refTypes.size(); ++ii) {
1703         if (_refTypes.at(ii)->name == name)
1704             return ii;
1705     }
1706
1707     TypeReference *type = _pool.New<TypeReference>();
1708     type->name = name;
1709     type->firstUse = object;
1710     _refTypes.append(type);
1711     return _refTypes.size() - 1;
1712 }
1713
1714 void QQmlScript::Parser::setTree(QQmlScript::Object *tree)
1715 {
1716     Q_ASSERT(! root);
1717
1718     root = tree;
1719 }
1720
1721 QT_END_NAMESPACE