source: trunk/src/declarative/qml/qdeclarativescriptparser.cpp@ 900

Last change on this file since 900 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 38.2 KB
Line 
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** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "private/qdeclarativescriptparser_p.h"
43
44#include "private/qdeclarativeparser_p.h"
45#include "parser/qdeclarativejsengine_p.h"
46#include "parser/qdeclarativejsparser_p.h"
47#include "parser/qdeclarativejslexer_p.h"
48#include "parser/qdeclarativejsnodepool_p.h"
49#include "parser/qdeclarativejsastvisitor_p.h"
50#include "parser/qdeclarativejsast_p.h"
51#include "private/qdeclarativerewrite_p.h"
52
53#include <QStack>
54#include <QCoreApplication>
55#include <QtDebug>
56
57QT_BEGIN_NAMESPACE
58
59using namespace QDeclarativeJS;
60using namespace QDeclarativeParser;
61
62namespace {
63
64class ProcessAST: protected AST::Visitor
65{
66 struct State {
67 State() : object(0), property(0) {}
68 State(Object *o) : object(o), property(0) {}
69 State(Object *o, Property *p) : object(o), property(p) {}
70
71 Object *object;
72 Property *property;
73 };
74
75 struct StateStack : public QStack<State>
76 {
77 void pushObject(Object *obj)
78 {
79 push(State(obj));
80 }
81
82 void pushProperty(const QString &name, const LocationSpan &location)
83 {
84 const State &state = top();
85 if (state.property) {
86 State s(state.property->getValue(location),
87 state.property->getValue(location)->getProperty(name.toUtf8()));
88 s.property->location = location;
89 push(s);
90 } else {
91 State s(state.object,
92 state.object->getProperty(name.toUtf8()));
93
94 s.property->location = location;
95 push(s);
96 }
97 }
98 };
99
100public:
101 ProcessAST(QDeclarativeScriptParser *parser);
102 virtual ~ProcessAST();
103
104 void operator()(const QString &code, AST::Node *node);
105
106protected:
107
108 Object *defineObjectBinding(AST::UiQualifiedId *propertyName, bool onAssignment,
109 const QString &objectType,
110 AST::SourceLocation typeLocation,
111 LocationSpan location,
112 AST::UiObjectInitializer *initializer = 0);
113
114 QDeclarativeParser::Variant getVariant(AST::ExpressionNode *expr);
115
116 LocationSpan location(AST::SourceLocation start, AST::SourceLocation end);
117 LocationSpan location(AST::UiQualifiedId *);
118
119 using AST::Visitor::visit;
120 using AST::Visitor::endVisit;
121
122 virtual bool visit(AST::UiProgram *node);
123 virtual bool visit(AST::UiImport *node);
124 virtual bool visit(AST::UiObjectDefinition *node);
125 virtual bool visit(AST::UiPublicMember *node);
126 virtual bool visit(AST::UiObjectBinding *node);
127
128 virtual bool visit(AST::UiScriptBinding *node);
129 virtual bool visit(AST::UiArrayBinding *node);
130 virtual bool visit(AST::UiSourceElement *node);
131
132 void accept(AST::Node *node);
133
134 QString asString(AST::UiQualifiedId *node) const;
135
136 const State state() const;
137 Object *currentObject() const;
138 Property *currentProperty() const;
139
140 QString qualifiedNameId() const;
141
142 QString textAt(const AST::SourceLocation &loc) const
143 { return _contents.mid(loc.offset, loc.length); }
144
145
146 QString textAt(const AST::SourceLocation &first,
147 const AST::SourceLocation &last) const
148 { return _contents.mid(first.offset, last.offset + last.length - first.offset); }
149
150 QString asString(AST::ExpressionNode *expr)
151 {
152 if (! expr)
153 return QString();
154
155 return textAt(expr->firstSourceLocation(), expr->lastSourceLocation());
156 }
157
158 QString asString(AST::Statement *stmt)
159 {
160 if (! stmt)
161 return QString();
162
163 QString s = textAt(stmt->firstSourceLocation(), stmt->lastSourceLocation());
164 s += QLatin1Char('\n');
165 return s;
166 }
167
168private:
169 QDeclarativeScriptParser *_parser;
170 StateStack _stateStack;
171 QStringList _scope;
172 QString _contents;
173
174 inline bool isSignalProperty(const QByteArray &propertyName) const {
175 return (propertyName.length() >= 3 && propertyName.startsWith("on") &&
176 ('A' <= propertyName.at(2) && 'Z' >= propertyName.at(2)));
177 }
178
179};
180
181ProcessAST::ProcessAST(QDeclarativeScriptParser *parser)
182 : _parser(parser)
183{
184}
185
186ProcessAST::~ProcessAST()
187{
188}
189
190void ProcessAST::operator()(const QString &code, AST::Node *node)
191{
192 _contents = code;
193 accept(node);
194}
195
196void ProcessAST::accept(AST::Node *node)
197{
198 AST::Node::acceptChild(node, this);
199}
200
201const ProcessAST::State ProcessAST::state() const
202{
203 if (_stateStack.isEmpty())
204 return State();
205
206 return _stateStack.back();
207}
208
209Object *ProcessAST::currentObject() const
210{
211 return state().object;
212}
213
214Property *ProcessAST::currentProperty() const
215{
216 return state().property;
217}
218
219QString ProcessAST::qualifiedNameId() const
220{
221 return _scope.join(QLatin1String("/"));
222}
223
224QString ProcessAST::asString(AST::UiQualifiedId *node) const
225{
226 QString s;
227
228 for (AST::UiQualifiedId *it = node; it; it = it->next) {
229 s.append(it->name->asString());
230
231 if (it->next)
232 s.append(QLatin1Char('.'));
233 }
234
235 return s;
236}
237
238Object *
239ProcessAST::defineObjectBinding(AST::UiQualifiedId *propertyName,
240 bool onAssignment,
241 const QString &objectType,
242 AST::SourceLocation typeLocation,
243 LocationSpan location,
244 AST::UiObjectInitializer *initializer)
245{
246 int lastTypeDot = objectType.lastIndexOf(QLatin1Char('.'));
247 bool isType = !objectType.isEmpty() &&
248 (objectType.at(0).isUpper() ||
249 (lastTypeDot >= 0 && objectType.at(lastTypeDot+1).isUpper()));
250
251 int propertyCount = 0;
252 for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
253 ++propertyCount;
254 _stateStack.pushProperty(name->name->asString(),
255 this->location(name));
256 }
257
258 if (!onAssignment && propertyCount && currentProperty() && currentProperty()->values.count()) {
259 QDeclarativeError error;
260 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Property value set multiple times"));
261 error.setLine(this->location(propertyName).start.line);
262 error.setColumn(this->location(propertyName).start.column);
263 _parser->_errors << error;
264 return 0;
265 }
266
267 if (!isType) {
268
269 if(propertyCount || !currentObject()) {
270 QDeclarativeError error;
271 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Expected type name"));
272 error.setLine(typeLocation.startLine);
273 error.setColumn(typeLocation.startColumn);
274 _parser->_errors << error;
275 return 0;
276 }
277
278 LocationSpan loc = ProcessAST::location(typeLocation, typeLocation);
279 if (propertyName)
280 loc = ProcessAST::location(propertyName);
281
282 _stateStack.pushProperty(objectType, loc);
283 accept(initializer);
284 _stateStack.pop();
285
286 return 0;
287
288 } else {
289 // Class
290
291 QString resolvableObjectType = objectType;
292 if (lastTypeDot >= 0)
293 resolvableObjectType.replace(QLatin1Char('.'),QLatin1Char('/'));
294
295 Object *obj = new Object;
296
297 QDeclarativeScriptParser::TypeReference *typeRef = _parser->findOrCreateType(resolvableObjectType);
298 obj->type = typeRef->id;
299
300 typeRef->refObjects.append(obj);
301
302 // XXX this doesn't do anything (_scope never builds up)
303 _scope.append(resolvableObjectType);
304 obj->typeName = qualifiedNameId().toUtf8();
305 _scope.removeLast();
306
307 obj->location = location;
308
309 if (propertyCount) {
310
311 Property *prop = currentProperty();
312 Value *v = new Value;
313 v->object = obj;
314 v->location = obj->location;
315 if (onAssignment)
316 prop->addOnValue(v);
317 else
318 prop->addValue(v);
319
320 while (propertyCount--)
321 _stateStack.pop();
322
323 } else {
324
325 if (! _parser->tree()) {
326 _parser->setTree(obj);
327 } else {
328 const State state = _stateStack.top();
329 Value *v = new Value;
330 v->object = obj;
331 v->location = obj->location;
332 if (state.property) {
333 state.property->addValue(v);
334 } else {
335 Property *defaultProp = state.object->getDefaultProperty();
336 if (defaultProp->location.start.line == -1) {
337 defaultProp->location = v->location;
338 defaultProp->location.end = defaultProp->location.start;
339 defaultProp->location.range.length = 0;
340 }
341 defaultProp->addValue(v);
342 }
343 }
344 }
345
346 _stateStack.pushObject(obj);
347 accept(initializer);
348 _stateStack.pop();
349
350 return obj;
351 }
352}
353
354LocationSpan ProcessAST::location(AST::UiQualifiedId *id)
355{
356 return location(id->identifierToken, id->identifierToken);
357}
358
359LocationSpan ProcessAST::location(AST::SourceLocation start, AST::SourceLocation end)
360{
361 LocationSpan rv;
362 rv.start.line = start.startLine;
363 rv.start.column = start.startColumn;
364 rv.end.line = end.startLine;
365 rv.end.column = end.startColumn + end.length - 1;
366 rv.range.offset = start.offset;
367 rv.range.length = end.offset + end.length - start.offset;
368 return rv;
369}
370
371// UiProgram: UiImportListOpt UiObjectMemberList ;
372bool ProcessAST::visit(AST::UiProgram *node)
373{
374 accept(node->imports);
375 accept(node->members->member);
376 return false;
377}
378
379// UiImport: T_IMPORT T_STRING_LITERAL ;
380bool ProcessAST::visit(AST::UiImport *node)
381{
382 QString uri;
383 QDeclarativeScriptParser::Import import;
384
385 if (node->fileName) {
386 uri = node->fileName->asString();
387
388 if (uri.endsWith(QLatin1String(".js"))) {
389 import.type = QDeclarativeScriptParser::Import::Script;
390 } else {
391 import.type = QDeclarativeScriptParser::Import::File;
392 }
393 } else {
394 import.type = QDeclarativeScriptParser::Import::Library;
395 uri = asString(node->importUri);
396 }
397
398 AST::SourceLocation startLoc = node->importToken;
399 AST::SourceLocation endLoc = node->semicolonToken;
400
401 // Qualifier
402 if (node->importId) {
403 import.qualifier = node->importId->asString();
404 if (!import.qualifier.at(0).isUpper()) {
405 QDeclarativeError error;
406 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Invalid import qualifier ID"));
407 error.setLine(node->importIdToken.startLine);
408 error.setColumn(node->importIdToken.startColumn);
409 _parser->_errors << error;
410 return false;
411 }
412 if (import.qualifier == QLatin1String("Qt")) {
413 QDeclarativeError error;
414 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Reserved name \"Qt\" cannot be used as an qualifier"));
415 error.setLine(node->importIdToken.startLine);
416 error.setColumn(node->importIdToken.startColumn);
417 _parser->_errors << error;
418 return false;
419 }
420
421 // Check for script qualifier clashes
422 bool isScript = import.type == QDeclarativeScriptParser::Import::Script;
423 for (int ii = 0; ii < _parser->_imports.count(); ++ii) {
424 const QDeclarativeScriptParser::Import &other = _parser->_imports.at(ii);
425 bool otherIsScript = other.type == QDeclarativeScriptParser::Import::Script;
426
427 if ((isScript || otherIsScript) && import.qualifier == other.qualifier) {
428 QDeclarativeError error;
429 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Script import qualifiers must be unique."));
430 error.setLine(node->importIdToken.startLine);
431 error.setColumn(node->importIdToken.startColumn);
432 _parser->_errors << error;
433 return false;
434 }
435 }
436
437 } else if (import.type == QDeclarativeScriptParser::Import::Script) {
438 QDeclarativeError error;
439 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Script import requires a qualifier"));
440 error.setLine(node->fileNameToken.startLine);
441 error.setColumn(node->fileNameToken.startColumn);
442 _parser->_errors << error;
443 return false;
444 }
445
446 if (node->versionToken.isValid()) {
447 import.version = textAt(node->versionToken);
448 } else if (import.type == QDeclarativeScriptParser::Import::Library) {
449 QDeclarativeError error;
450 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Library import requires a version"));
451 error.setLine(node->importIdToken.startLine);
452 error.setColumn(node->importIdToken.startColumn);
453 _parser->_errors << error;
454 return false;
455 }
456
457
458 import.location = location(startLoc, endLoc);
459 import.uri = uri;
460
461 _parser->_imports << import;
462
463 return false;
464}
465
466bool ProcessAST::visit(AST::UiPublicMember *node)
467{
468 const struct TypeNameToType {
469 const char *name;
470 Object::DynamicProperty::Type type;
471 const char *qtName;
472 } propTypeNameToTypes[] = {
473 { "int", Object::DynamicProperty::Int, "int" },
474 { "bool", Object::DynamicProperty::Bool, "bool" },
475 { "double", Object::DynamicProperty::Real, "double" },
476 { "real", Object::DynamicProperty::Real, "qreal" },
477 { "string", Object::DynamicProperty::String, "QString" },
478 { "url", Object::DynamicProperty::Url, "QUrl" },
479 { "color", Object::DynamicProperty::Color, "QColor" },
480 // Internally QTime, QDate and QDateTime are all supported.
481 // To be more consistent with JavaScript we expose only
482 // QDateTime as it matches closely with the Date JS type.
483 // We also call it "date" to match.
484 // { "time", Object::DynamicProperty::Time, "QTime" },
485 // { "date", Object::DynamicProperty::Date, "QDate" },
486 { "date", Object::DynamicProperty::DateTime, "QDateTime" },
487 { "variant", Object::DynamicProperty::Variant, "QVariant" }
488 };
489 const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) /
490 sizeof(propTypeNameToTypes[0]);
491
492 if(node->type == AST::UiPublicMember::Signal) {
493 const QString name = node->name->asString();
494
495 Object::DynamicSignal signal;
496 signal.name = name.toUtf8();
497
498 AST::UiParameterList *p = node->parameters;
499 while (p) {
500 const QString memberType = p->type->asString();
501 const char *qtType = 0;
502 for(int ii = 0; !qtType && ii < propTypeNameToTypesCount; ++ii) {
503 if(QLatin1String(propTypeNameToTypes[ii].name) == memberType)
504 qtType = propTypeNameToTypes[ii].qtName;
505 }
506
507 if (!qtType) {
508 QDeclarativeError error;
509 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Expected parameter type"));
510 error.setLine(node->typeToken.startLine);
511 error.setColumn(node->typeToken.startColumn);
512 _parser->_errors << error;
513 return false;
514 }
515
516 signal.parameterTypes << qtType;
517 signal.parameterNames << p->name->asString().toUtf8();
518 p = p->finish();
519 }
520
521 _stateStack.top().object->dynamicSignals << signal;
522 } else {
523 const QString memberType = node->memberType->asString();
524 const QString name = node->name->asString();
525
526 bool typeFound = false;
527 Object::DynamicProperty::Type type;
528
529 if (memberType == QLatin1String("alias")) {
530 type = Object::DynamicProperty::Alias;
531 typeFound = true;
532 }
533
534 for(int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) {
535 if(QLatin1String(propTypeNameToTypes[ii].name) == memberType) {
536 type = propTypeNameToTypes[ii].type;
537 typeFound = true;
538 }
539 }
540
541 if (!typeFound && memberType.at(0).isUpper()) {
542 QString typemodifier;
543 if(node->typeModifier)
544 typemodifier = node->typeModifier->asString();
545 if (typemodifier.isEmpty()) {
546 type = Object::DynamicProperty::Custom;
547 } else if(typemodifier == QLatin1String("list")) {
548 type = Object::DynamicProperty::CustomList;
549 } else {
550 QDeclarativeError error;
551 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Invalid property type modifier"));
552 error.setLine(node->typeModifierToken.startLine);
553 error.setColumn(node->typeModifierToken.startColumn);
554 _parser->_errors << error;
555 return false;
556 }
557 typeFound = true;
558 } else if (node->typeModifier) {
559 QDeclarativeError error;
560 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Unexpected property type modifier"));
561 error.setLine(node->typeModifierToken.startLine);
562 error.setColumn(node->typeModifierToken.startColumn);
563 _parser->_errors << error;
564 return false;
565 }
566
567 if(!typeFound) {
568 QDeclarativeError error;
569 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Expected property type"));
570 error.setLine(node->typeToken.startLine);
571 error.setColumn(node->typeToken.startColumn);
572 _parser->_errors << error;
573 return false;
574 }
575
576 if (node->isReadonlyMember) {
577 QDeclarativeError error;
578 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Readonly not yet supported"));
579 error.setLine(node->readonlyToken.startLine);
580 error.setColumn(node->readonlyToken.startColumn);
581 _parser->_errors << error;
582 return false;
583
584 }
585 Object::DynamicProperty property;
586 property.isDefaultProperty = node->isDefaultMember;
587 property.type = type;
588 if (type >= Object::DynamicProperty::Custom) {
589 QDeclarativeScriptParser::TypeReference *typeRef =
590 _parser->findOrCreateType(memberType);
591 typeRef->refObjects.append(_stateStack.top().object);
592 }
593 property.customType = memberType.toUtf8();
594 property.name = name.toUtf8();
595 property.location = location(node->firstSourceLocation(),
596 node->lastSourceLocation());
597
598 if (node->expression) { // default value
599 property.defaultValue = new Property;
600 property.defaultValue->parent = _stateStack.top().object;
601 property.defaultValue->location =
602 location(node->expression->firstSourceLocation(),
603 node->expression->lastSourceLocation());
604 Value *value = new Value;
605 value->location = location(node->expression->firstSourceLocation(),
606 node->expression->lastSourceLocation());
607 value->value = getVariant(node->expression);
608 property.defaultValue->values << value;
609 }
610
611 _stateStack.top().object->dynamicProperties << property;
612
613 // process QML-like initializers (e.g. property Object o: Object {})
614 accept(node->binding);
615 }
616
617 return false;
618}
619
620
621// UiObjectMember: UiQualifiedId UiObjectInitializer ;
622bool ProcessAST::visit(AST::UiObjectDefinition *node)
623{
624 LocationSpan l = location(node->firstSourceLocation(),
625 node->lastSourceLocation());
626
627 const QString objectType = asString(node->qualifiedTypeNameId);
628 const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken;
629
630 defineObjectBinding(/*propertyName = */ 0, false, objectType,
631 typeLocation, l, node->initializer);
632
633 return false;
634}
635
636
637// UiObjectMember: UiQualifiedId T_COLON UiQualifiedId UiObjectInitializer ;
638bool ProcessAST::visit(AST::UiObjectBinding *node)
639{
640 LocationSpan l = location(node->qualifiedTypeNameId->identifierToken,
641 node->initializer->rbraceToken);
642
643 const QString objectType = asString(node->qualifiedTypeNameId);
644 const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken;
645
646 defineObjectBinding(node->qualifiedId, node->hasOnToken, objectType,
647 typeLocation, l, node->initializer);
648
649 return false;
650}
651
652QDeclarativeParser::Variant ProcessAST::getVariant(AST::ExpressionNode *expr)
653{
654 if (AST::StringLiteral *lit = AST::cast<AST::StringLiteral *>(expr)) {
655 return QDeclarativeParser::Variant(lit->value->asString());
656 } else if (expr->kind == AST::Node::Kind_TrueLiteral) {
657 return QDeclarativeParser::Variant(true);
658 } else if (expr->kind == AST::Node::Kind_FalseLiteral) {
659 return QDeclarativeParser::Variant(false);
660 } else if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(expr)) {
661 return QDeclarativeParser::Variant(lit->value, asString(expr));
662 } else {
663
664 if (AST::UnaryMinusExpression *unaryMinus = AST::cast<AST::UnaryMinusExpression *>(expr)) {
665 if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(unaryMinus->expression)) {
666 return QDeclarativeParser::Variant(-lit->value, asString(expr));
667 }
668 }
669
670 return QDeclarativeParser::Variant(asString(expr), expr);
671 }
672}
673
674
675// UiObjectMember: UiQualifiedId T_COLON Statement ;
676bool ProcessAST::visit(AST::UiScriptBinding *node)
677{
678 int propertyCount = 0;
679 AST::UiQualifiedId *propertyName = node->qualifiedId;
680 for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
681 ++propertyCount;
682 _stateStack.pushProperty(name->name->asString(),
683 location(name));
684 }
685
686 Property *prop = currentProperty();
687
688 if (prop->values.count()) {
689 QDeclarativeError error;
690 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Property value set multiple times"));
691 error.setLine(this->location(propertyName).start.line);
692 error.setColumn(this->location(propertyName).start.column);
693 _parser->_errors << error;
694 return 0;
695 }
696
697 QDeclarativeParser::Variant primitive;
698
699 if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(node->statement)) {
700 primitive = getVariant(stmt->expression);
701 } else { // do binding
702 primitive = QDeclarativeParser::Variant(asString(node->statement),
703 node->statement);
704 }
705
706 prop->location.range.length = prop->location.range.offset + prop->location.range.length - node->qualifiedId->identifierToken.offset;
707 prop->location.range.offset = node->qualifiedId->identifierToken.offset;
708 Value *v = new Value;
709 v->value = primitive;
710 v->location = location(node->statement->firstSourceLocation(),
711 node->statement->lastSourceLocation());
712
713 prop->addValue(v);
714
715 while (propertyCount--)
716 _stateStack.pop();
717
718 return true;
719}
720
721static QList<int> collectCommas(AST::UiArrayMemberList *members)
722{
723 QList<int> commas;
724
725 if (members) {
726 for (AST::UiArrayMemberList *it = members->next; it; it = it->next) {
727 commas.append(it->commaToken.offset);
728 }
729 }
730
731 return commas;
732}
733
734// UiObjectMember: UiQualifiedId T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ;
735bool ProcessAST::visit(AST::UiArrayBinding *node)
736{
737 int propertyCount = 0;
738 AST::UiQualifiedId *propertyName = node->qualifiedId;
739 for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
740 ++propertyCount;
741 _stateStack.pushProperty(name->name->asString(),
742 location(name));
743 }
744
745 Property* prop = currentProperty();
746
747 if (prop->values.count()) {
748 QDeclarativeError error;
749 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Property value set multiple times"));
750 error.setLine(this->location(propertyName).start.line);
751 error.setColumn(this->location(propertyName).start.column);
752 _parser->_errors << error;
753 return 0;
754 }
755
756 accept(node->members);
757
758 // For the DOM, store the position of the T_LBRACKET upto the T_RBRACKET as the range:
759 prop->listValueRange.offset = node->lbracketToken.offset;
760 prop->listValueRange.length = node->rbracketToken.offset + node->rbracketToken.length - node->lbracketToken.offset;
761
762 // Store the positions of the comma token too, again for the DOM to be able to retrieve it.
763 prop->listCommaPositions = collectCommas(node->members);
764
765 while (propertyCount--)
766 _stateStack.pop();
767
768 return false;
769}
770
771bool ProcessAST::visit(AST::UiSourceElement *node)
772{
773 QDeclarativeParser::Object *obj = currentObject();
774
775 if (AST::FunctionDeclaration *funDecl = AST::cast<AST::FunctionDeclaration *>(node->sourceElement)) {
776
777 Object::DynamicSlot slot;
778 slot.location = location(funDecl->firstSourceLocation(), funDecl->lastSourceLocation());
779
780 AST::FormalParameterList *f = funDecl->formals;
781 while (f) {
782 slot.parameterNames << f->name->asString().toUtf8();
783 f = f->finish();
784 }
785
786 QString body = textAt(funDecl->lbraceToken, funDecl->rbraceToken);
787 slot.name = funDecl->name->asString().toUtf8();
788 slot.body = body;
789 obj->dynamicSlots << slot;
790
791 } else {
792 QDeclarativeError error;
793 error.setDescription(QCoreApplication::translate("QDeclarativeParser","JavaScript declaration outside Script element"));
794 error.setLine(node->firstSourceLocation().startLine);
795 error.setColumn(node->firstSourceLocation().startColumn);
796 _parser->_errors << error;
797 }
798 return false;
799}
800
801} // end of anonymous namespace
802
803
804QDeclarativeScriptParser::QDeclarativeScriptParser()
805: root(0), data(0)
806{
807
808}
809
810QDeclarativeScriptParser::~QDeclarativeScriptParser()
811{
812 clear();
813}
814
815class QDeclarativeScriptParserJsASTData
816{
817public:
818 QDeclarativeScriptParserJsASTData(const QString &filename)
819 : nodePool(filename, &engine) {}
820
821 Engine engine;
822 NodePool nodePool;
823};
824
825bool QDeclarativeScriptParser::parse(const QByteArray &qmldata, const QUrl &url)
826{
827 clear();
828
829 const QString fileName = url.toString();
830 _scriptFile = fileName;
831
832 QTextStream stream(qmldata, QIODevice::ReadOnly);
833#ifndef QT_NO_TEXTCODEC
834 stream.setCodec("UTF-8");
835#endif
836 const QString code = stream.readAll();
837
838 data = new QDeclarativeScriptParserJsASTData(fileName);
839
840 Lexer lexer(&data->engine);
841 lexer.setCode(code, /*line = */ 1);
842
843 Parser parser(&data->engine);
844
845 if (! parser.parse() || !_errors.isEmpty()) {
846
847 // Extract errors from the parser
848 foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) {
849
850 if (m.isWarning())
851 continue;
852
853 QDeclarativeError error;
854 error.setUrl(url);
855 error.setDescription(m.message);
856 error.setLine(m.loc.startLine);
857 error.setColumn(m.loc.startColumn);
858 _errors << error;
859
860 }
861 }
862
863 if (_errors.isEmpty()) {
864 ProcessAST process(this);
865 process(code, parser.ast());
866
867 // Set the url for process errors
868 for(int ii = 0; ii < _errors.count(); ++ii)
869 _errors[ii].setUrl(url);
870 }
871
872 return _errors.isEmpty();
873}
874
875QList<QDeclarativeScriptParser::TypeReference*> QDeclarativeScriptParser::referencedTypes() const
876{
877 return _refTypes;
878}
879
880Object *QDeclarativeScriptParser::tree() const
881{
882 return root;
883}
884
885QList<QDeclarativeScriptParser::Import> QDeclarativeScriptParser::imports() const
886{
887 return _imports;
888}
889
890QList<QDeclarativeError> QDeclarativeScriptParser::errors() const
891{
892 return _errors;
893}
894
895static void replaceWithSpace(QString &str, int idx, int n)
896{
897 QChar *data = str.data() + idx;
898 const QChar space(QLatin1Char(' '));
899 for (int ii = 0; ii < n; ++ii)
900 *data++ = space;
901}
902
903/*
904Searches for ".pragma <value>" declarations within \a script. Currently supported pragmas
905are:
906 library
907*/
908QDeclarativeParser::Object::ScriptBlock::Pragmas QDeclarativeScriptParser::extractPragmas(QString &script)
909{
910 QDeclarativeParser::Object::ScriptBlock::Pragmas rv = QDeclarativeParser::Object::ScriptBlock::None;
911
912 const QString pragma(QLatin1String("pragma"));
913 const QString library(QLatin1String("library"));
914
915 QDeclarativeJS::Lexer l(0);
916 l.setCode(script, 0);
917
918 int token = l.lex();
919
920 while (true) {
921 if (token != QDeclarativeJSGrammar::T_DOT)
922 return rv;
923
924 int startOffset = l.tokenOffset();
925 int startLine = l.currentLineNo();
926
927 token = l.lex();
928
929 if (token != QDeclarativeJSGrammar::T_IDENTIFIER ||
930 l.currentLineNo() != startLine ||
931 script.mid(l.tokenOffset(), l.tokenLength()) != pragma)
932 return rv;
933
934 token = l.lex();
935
936 if (token != QDeclarativeJSGrammar::T_IDENTIFIER ||
937 l.currentLineNo() != startLine)
938 return rv;
939
940 QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
941 int endOffset = l.tokenLength() + l.tokenOffset();
942
943 token = l.lex();
944 if (l.currentLineNo() == startLine)
945 return rv;
946
947 if (pragmaValue == QLatin1String("library")) {
948 rv |= QDeclarativeParser::Object::ScriptBlock::Shared;
949 replaceWithSpace(script, startOffset, endOffset - startOffset);
950 } else {
951 return rv;
952 }
953 }
954 return rv;
955}
956
957#define CHECK_LINE if(l.currentLineNo() != startLine) return rv;
958#define CHECK_TOKEN(t) if (token != QDeclarativeJSGrammar:: t) return rv;
959
960static const int uriTokens[] = {
961 QDeclarativeJSGrammar::T_IDENTIFIER,
962 QDeclarativeJSGrammar::T_PROPERTY,
963 QDeclarativeJSGrammar::T_SIGNAL,
964 QDeclarativeJSGrammar::T_READONLY,
965 QDeclarativeJSGrammar::T_ON,
966 QDeclarativeJSGrammar::T_BREAK,
967 QDeclarativeJSGrammar::T_CASE,
968 QDeclarativeJSGrammar::T_CATCH,
969 QDeclarativeJSGrammar::T_CONTINUE,
970 QDeclarativeJSGrammar::T_DEFAULT,
971 QDeclarativeJSGrammar::T_DELETE,
972 QDeclarativeJSGrammar::T_DO,
973 QDeclarativeJSGrammar::T_ELSE,
974 QDeclarativeJSGrammar::T_FALSE,
975 QDeclarativeJSGrammar::T_FINALLY,
976 QDeclarativeJSGrammar::T_FOR,
977 QDeclarativeJSGrammar::T_FUNCTION,
978 QDeclarativeJSGrammar::T_IF,
979 QDeclarativeJSGrammar::T_IN,
980 QDeclarativeJSGrammar::T_INSTANCEOF,
981 QDeclarativeJSGrammar::T_NEW,
982 QDeclarativeJSGrammar::T_NULL,
983 QDeclarativeJSGrammar::T_RETURN,
984 QDeclarativeJSGrammar::T_SWITCH,
985 QDeclarativeJSGrammar::T_THIS,
986 QDeclarativeJSGrammar::T_THROW,
987 QDeclarativeJSGrammar::T_TRUE,
988 QDeclarativeJSGrammar::T_TRY,
989 QDeclarativeJSGrammar::T_TYPEOF,
990 QDeclarativeJSGrammar::T_VAR,
991 QDeclarativeJSGrammar::T_VOID,
992 QDeclarativeJSGrammar::T_WHILE,
993 QDeclarativeJSGrammar::T_CONST,
994 QDeclarativeJSGrammar::T_DEBUGGER,
995 QDeclarativeJSGrammar::T_RESERVED_WORD,
996 QDeclarativeJSGrammar::T_WITH,
997
998 QDeclarativeJSGrammar::EOF_SYMBOL
999};
1000static inline bool isUriToken(int token)
1001{
1002 const int *current = uriTokens;
1003 while (*current != QDeclarativeJSGrammar::EOF_SYMBOL) {
1004 if (*current == token)
1005 return true;
1006 ++current;
1007 }
1008 return false;
1009}
1010
1011QDeclarativeScriptParser::JavaScriptMetaData QDeclarativeScriptParser::extractMetaData(QString &script)
1012{
1013 JavaScriptMetaData rv;
1014
1015 QDeclarativeParser::Object::ScriptBlock::Pragmas &pragmas = rv.pragmas;
1016
1017 const QString pragma(QLatin1String("pragma"));
1018 const QString js(QLatin1String(".js"));
1019 const QString library(QLatin1String("library"));
1020
1021 QDeclarativeJS::Lexer l(0);
1022 l.setCode(script, 0);
1023
1024 int token = l.lex();
1025
1026 while (true) {
1027 if (token != QDeclarativeJSGrammar::T_DOT)
1028 return rv;
1029
1030 int startOffset = l.tokenOffset();
1031 int startLine = l.currentLineNo();
1032
1033 token = l.lex();
1034
1035 CHECK_LINE;
1036
1037 if (token == QDeclarativeJSGrammar::T_IMPORT) {
1038
1039 // .import <URI> <Version> as <Identifier>
1040 // .import <file.js> as <Identifier>
1041
1042 token = l.lex();
1043
1044 CHECK_LINE;
1045
1046 if (token == QDeclarativeJSGrammar::T_STRING_LITERAL) {
1047
1048 QString file(l.characterBuffer(), l.characterCount());
1049 if (!file.endsWith(js))
1050 return rv;
1051
1052 token = l.lex();
1053
1054 CHECK_TOKEN(T_AS);
1055 CHECK_LINE;
1056
1057 token = l.lex();
1058
1059 CHECK_TOKEN(T_IDENTIFIER);
1060 CHECK_LINE;
1061
1062 int endOffset = l.tokenLength() + l.tokenOffset();
1063
1064 QString importId = script.mid(l.tokenOffset(), l.tokenLength());
1065
1066 if (!importId.at(0).isUpper())
1067 return rv;
1068
1069 token = l.lex();
1070 if (l.currentLineNo() == startLine)
1071 return rv;
1072
1073 replaceWithSpace(script, startOffset, endOffset - startOffset);
1074
1075 Import import;
1076 import.type = Import::Script;
1077 import.uri = file;
1078 import.qualifier = importId;
1079
1080 rv.imports << import;
1081
1082 } else {
1083 // URI
1084 QString uri;
1085 QString version;
1086
1087 while (true) {
1088 if (!isUriToken(token))
1089 return rv;
1090
1091 uri.append(QString(l.characterBuffer(), l.characterCount()));
1092
1093 token = l.lex();
1094 CHECK_LINE;
1095 if (token != QDeclarativeJSGrammar::T_DOT)
1096 break;
1097
1098 uri.append(QLatin1Char('.'));
1099
1100 token = l.lex();
1101 CHECK_LINE;
1102 }
1103
1104 CHECK_TOKEN(T_NUMERIC_LITERAL);
1105 version = script.mid(l.tokenOffset(), l.tokenLength());
1106
1107 token = l.lex();
1108
1109 CHECK_TOKEN(T_AS);
1110 CHECK_LINE;
1111
1112 token = l.lex();
1113
1114 CHECK_TOKEN(T_IDENTIFIER);
1115 CHECK_LINE;
1116
1117 int endOffset = l.tokenLength() + l.tokenOffset();
1118
1119 QString importId = script.mid(l.tokenOffset(), l.tokenLength());
1120
1121 if (!importId.at(0).isUpper())
1122 return rv;
1123
1124 token = l.lex();
1125 if (l.currentLineNo() == startLine)
1126 return rv;
1127
1128 replaceWithSpace(script, startOffset, endOffset - startOffset);
1129
1130 Import import;
1131 import.type = Import::Library;
1132 import.uri = uri;
1133 import.version = version;
1134 import.qualifier = importId;
1135
1136 rv.imports << import;
1137 }
1138
1139 } else if (token == QDeclarativeJSGrammar::T_IDENTIFIER &&
1140 script.mid(l.tokenOffset(), l.tokenLength()) == pragma) {
1141
1142 token = l.lex();
1143
1144 CHECK_TOKEN(T_IDENTIFIER);
1145 CHECK_LINE;
1146
1147 QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
1148 int endOffset = l.tokenLength() + l.tokenOffset();
1149
1150 if (pragmaValue == QLatin1String("library")) {
1151 pragmas |= QDeclarativeParser::Object::ScriptBlock::Shared;
1152 replaceWithSpace(script, startOffset, endOffset - startOffset);
1153 } else {
1154 return rv;
1155 }
1156
1157 token = l.lex();
1158 if (l.currentLineNo() == startLine)
1159 return rv;
1160
1161 } else {
1162 return rv;
1163 }
1164 }
1165 return rv;
1166}
1167
1168void QDeclarativeScriptParser::clear()
1169{
1170 if (root) {
1171 root->release();
1172 root = 0;
1173 }
1174 _imports.clear();
1175 qDeleteAll(_refTypes);
1176 _refTypes.clear();
1177 _errors.clear();
1178
1179 if (data) {
1180 delete data;
1181 data = 0;
1182 }
1183}
1184
1185QDeclarativeScriptParser::TypeReference *QDeclarativeScriptParser::findOrCreateType(const QString &name)
1186{
1187 TypeReference *type = 0;
1188 int i = 0;
1189 for (; i < _refTypes.size(); ++i) {
1190 if (_refTypes.at(i)->name == name) {
1191 type = _refTypes.at(i);
1192 break;
1193 }
1194 }
1195 if (!type) {
1196 type = new TypeReference(i, name);
1197 _refTypes.append(type);
1198 }
1199
1200 return type;
1201}
1202
1203void QDeclarativeScriptParser::setTree(Object *tree)
1204{
1205 Q_ASSERT(! root);
1206
1207 root = tree;
1208}
1209
1210QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.