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