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