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