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