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