1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qqmlscript_p.h"
44 #include "parser/qqmljsengine_p.h"
45 #include "parser/qqmljsparser_p.h"
46 #include "parser/qqmljslexer_p.h"
47 #include "parser/qqmljsmemorypool_p.h"
48 #include "parser/qqmljsastvisitor_p.h"
49 #include "parser/qqmljsast_p.h"
50 #include <private/qqmlrewrite_p.h>
53 #include <QStringList>
54 #include <QCoreApplication>
59 using namespace QQmlJS;
60 using namespace QQmlScript;
65 QQmlScript::Object::Object()
66 : type(-1), idIndex(-1), metatype(0), synthCache(0), defaultProperty(0), parserStatusCast(-1),
67 componentCompileState(0), nextAliasingObject(0), nextIdObject(0)
71 QQmlScript::Object::~Object()
73 if (synthCache) synthCache->release();
76 void Object::setBindingBit(int b)
78 while (bindingBitmask.size() < 4 * (1 + b / 32))
79 bindingBitmask.append(char(0));
81 quint32 *bits = (quint32 *)bindingBitmask.data();
82 bits[b / 32] |= (1 << (b % 32));
85 const QMetaObject *Object::metaObject() const
87 if (!metadata.isEmpty() && metatype)
93 QQmlScript::Property *Object::getDefaultProperty()
95 if (!defaultProperty) {
96 defaultProperty = pool()->New<Property>();
97 defaultProperty->parent = this;
99 return defaultProperty;
102 void QQmlScript::Object::addValueProperty(Property *p)
104 valueProperties.append(p);
107 void QQmlScript::Object::addSignalProperty(Property *p)
109 signalProperties.append(p);
112 void QQmlScript::Object::addAttachedProperty(Property *p)
114 attachedProperties.append(p);
117 void QQmlScript::Object::addGroupedProperty(Property *p)
119 groupedProperties.append(p);
122 void QQmlScript::Object::addValueTypeProperty(Property *p)
124 valueTypeProperties.append(p);
127 void QQmlScript::Object::addScriptStringProperty(Property *p)
129 scriptStringProperties.append(p);
132 // This lookup is optimized for missing, and having to create a new property.
133 Property *QQmlScript::Object::getProperty(const QHashedStringRef &name, bool create)
136 quint32 h = name.hash();
137 if (propertiesHashField.testAndSet(h)) {
138 for (Property *p = properties.first(); p; p = properties.next(p)) {
139 if (p->name() == name)
144 Property *property = pool()->New<Property>();
145 property->parent = this;
146 property->_name = name;
147 property->isDefault = false;
148 properties.prepend(property);
151 for (Property *p = properties.first(); p; p = properties.next(p)) {
152 if (p->name() == name)
160 Property *QQmlScript::Object::getProperty(const QStringRef &name, bool create)
162 return getProperty(QHashedStringRef(name), create);
165 Property *QQmlScript::Object::getProperty(const QString &name, bool create)
167 for (Property *p = properties.first(); p; p = properties.next(p)) {
168 if (p->name() == name)
173 Property *property = pool()->New<Property>();
174 property->parent = this;
175 property->_name = QStringRef(pool()->NewString(name));
176 propertiesHashField.testAndSet(property->_name.hash());
177 property->isDefault = false;
178 properties.prepend(property);
185 QQmlScript::Object::DynamicProperty::DynamicProperty()
186 : isDefaultProperty(false), isReadOnly(false), type(Variant), defaultValue(0), nextProperty(0),
187 resolvedCustomTypeName(0)
191 QQmlScript::Object::DynamicSignal::DynamicSignal()
196 // Returns length in utf8 bytes
197 int QQmlScript::Object::DynamicSignal::parameterTypesLength() const
200 for (int ii = 0; ii < parameterTypes.count(); ++ii)
201 rv += parameterTypes.at(ii).length();
205 // Returns length in utf8 bytes
206 int QQmlScript::Object::DynamicSignal::parameterNamesLength() const
209 for (int ii = 0; ii < parameterNames.count(); ++ii)
210 rv += parameterNames.at(ii).utf8length();
214 QQmlScript::Object::DynamicSlot::DynamicSlot()
219 int QQmlScript::Object::DynamicSlot::parameterNamesLength() const
222 for (int ii = 0; ii < parameterNames.count(); ++ii)
223 rv += parameterNames.at(ii).length();
227 QQmlScript::Property::Property()
228 : parent(0), type(0), index(-1), value(0), isDefault(true), isDeferred(false),
229 isValueTypeSubProperty(false), isAlias(false), isReadOnlyDeclaration(false),
230 scriptStringScope(-1), nextMainProperty(0), nextProperty(0)
234 QQmlScript::Object *QQmlScript::Property::getValue(const LocationSpan &l)
236 if (!value) { value = pool()->New<Object>(); value->location = l; }
240 void QQmlScript::Property::addValue(Value *v)
245 void QQmlScript::Property::addOnValue(Value *v)
250 bool QQmlScript::Property::isEmpty() const
252 return !value && values.isEmpty() && onValues.isEmpty();
255 QQmlScript::Value::Value()
256 : type(Unknown), object(0), bindingReference(0), nextValue(0)
260 QQmlScript::Variant::Variant()
265 QQmlScript::Variant::Variant(const Variant &o)
266 : t(o.t), d(o.d), asWritten(o.asWritten)
270 QQmlScript::Variant::Variant(bool v)
275 QQmlScript::Variant::Variant(double v, const QStringRef &asWritten)
276 : t(Number), d(v), asWritten(asWritten)
280 QQmlScript::Variant::Variant(QQmlJS::AST::StringLiteral *v)
285 QQmlScript::Variant::Variant(const QStringRef &asWritten, QQmlJS::AST::Node *n)
286 : t(Script), n(n), asWritten(asWritten)
290 QQmlScript::Variant &QQmlScript::Variant::operator=(const Variant &o)
294 asWritten = o.asWritten;
298 QQmlScript::Variant::Type QQmlScript::Variant::type() const
303 bool QQmlScript::Variant::asBoolean() const
308 QString QQmlScript::Variant::asString() const
311 return l->value.toString();
313 return asWritten.toString();
317 double QQmlScript::Variant::asNumber() const
322 //reverse of Lexer::singleEscape()
323 QString escapedString(const QString &string)
325 QString tmp = QLatin1String("\"");
326 for (int i = 0; i < string.length(); ++i) {
327 const QChar &c = string.at(i);
328 switch(c.unicode()) {
330 tmp += QLatin1String("\\b");
333 tmp += QLatin1String("\\t");
336 tmp += QLatin1String("\\n");
339 tmp += QLatin1String("\\v");
342 tmp += QLatin1String("\\f");
345 tmp += QLatin1String("\\r");
348 tmp += QLatin1String("\\\"");
351 tmp += QLatin1String("\\\'");
354 tmp += QLatin1String("\\\\");
361 tmp += QLatin1Char('\"');
365 QString QQmlScript::Variant::asScript() const
372 return b?QLatin1String("true"):QLatin1String("false");
374 if (asWritten.isEmpty())
375 return QString::number(d);
377 return asWritten.toString();
379 return escapedString(asString());
381 if (AST::IdentifierExpression *i = AST::cast<AST::IdentifierExpression *>(n)) {
382 return i->name.toString();
384 return asWritten.toString();
388 QQmlJS::AST::Node *QQmlScript::Variant::asAST() const
390 if (type() == Script)
396 bool QQmlScript::Variant::isStringList() const
401 if (type() != Script || !n)
404 AST::ArrayLiteral *array = AST::cast<AST::ArrayLiteral *>(n);
408 AST::ElementList *elements = array->elements;
412 if (!AST::cast<AST::StringLiteral *>(elements->expression))
415 elements = elements->next;
421 QStringList QQmlScript::Variant::asStringList() const
429 AST::ArrayLiteral *array = AST::cast<AST::ArrayLiteral *>(n);
433 AST::ElementList *elements = array->elements;
436 AST::StringLiteral *string = AST::cast<AST::StringLiteral *>(elements->expression);
438 return QStringList();
439 rv.append(string->value.toString());
441 elements = elements->next;
448 // Actual parser classes
450 void QQmlScript::Import::extractVersion(int *maj, int *min) const
452 *maj = -1; *min = -1;
454 if (!version.isEmpty()) {
455 int dot = version.indexOf(QLatin1Char('.'));
457 *maj = version.toInt();
460 *maj = version.left(dot).toInt();
461 *min = version.mid(dot+1).toInt();
468 class ProcessAST: protected AST::Visitor
471 State() : object(0), property(0) {}
472 State(QQmlScript::Object *o) : object(o), property(0) {}
473 State(QQmlScript::Object *o, Property *p) : object(o), property(p) {}
475 QQmlScript::Object *object;
479 struct StateStack : public QStack<State>
481 void pushObject(QQmlScript::Object *obj)
486 void pushProperty(const QString &name, const LocationSpan &location)
488 const State &state = top();
489 if (state.property) {
490 State s(state.property->getValue(location),
491 state.property->getValue(location)->getProperty(name));
492 s.property->location = location;
495 State s(state.object, state.object->getProperty(name));
497 s.property->location = location;
502 void pushProperty(const QStringRef &name, const LocationSpan &location)
504 const State &state = top();
505 if (state.property) {
506 State s(state.property->getValue(location),
507 state.property->getValue(location)->getProperty(name));
508 s.property->location = location;
511 State s(state.object, state.object->getProperty(name));
513 s.property->location = location;
520 ProcessAST(QQmlScript::Parser *parser);
521 virtual ~ProcessAST();
523 void operator()(const QString &code, AST::Node *node);
527 QQmlScript::Object *defineObjectBinding(AST::UiQualifiedId *propertyName, bool onAssignment,
528 const QString &objectType,
529 AST::SourceLocation typeLocation,
530 LocationSpan location,
531 AST::UiObjectInitializer *initializer = 0);
533 QQmlScript::Variant getVariant(AST::Statement *stmt);
534 QQmlScript::Variant getVariant(AST::ExpressionNode *expr);
536 LocationSpan location(AST::SourceLocation start, AST::SourceLocation end);
537 LocationSpan location(AST::UiQualifiedId *);
539 using AST::Visitor::visit;
540 using AST::Visitor::endVisit;
542 virtual bool visit(AST::UiProgram *node);
543 virtual bool visit(AST::UiImport *node);
544 virtual bool visit(AST::UiObjectDefinition *node);
545 virtual bool visit(AST::UiPublicMember *node);
546 virtual bool visit(AST::UiObjectBinding *node);
548 virtual bool visit(AST::UiScriptBinding *node);
549 virtual bool visit(AST::UiArrayBinding *node);
550 virtual bool visit(AST::UiSourceElement *node);
552 void accept(AST::Node *node);
554 QString asString(AST::UiQualifiedId *node) const;
556 const State state() const;
557 QQmlScript::Object *currentObject() const;
558 Property *currentProperty() const;
560 QString qualifiedNameId() const;
562 QString textAt(const AST::SourceLocation &loc) const
563 { return _contents->mid(loc.offset, loc.length); }
565 QStringRef textRefAt(const AST::SourceLocation &loc) const
566 { return QStringRef(_contents, loc.offset, loc.length); }
568 QString textAt(const AST::SourceLocation &first,
569 const AST::SourceLocation &last) const
570 { return _contents->mid(first.offset, last.offset + last.length - first.offset); }
572 QStringRef textRefAt(const AST::SourceLocation &first,
573 const AST::SourceLocation &last) const
574 { return QStringRef(_contents, first.offset, last.offset + last.length - first.offset); }
576 QString asString(AST::ExpressionNode *expr)
581 return textAt(expr->firstSourceLocation(), expr->lastSourceLocation());
584 QStringRef asStringRef(AST::ExpressionNode *expr)
589 return textRefAt(expr->firstSourceLocation(), expr->lastSourceLocation());
592 QString asString(AST::Statement *stmt)
597 QString s = textAt(stmt->firstSourceLocation(), stmt->lastSourceLocation());
598 s += QLatin1Char('\n');
602 QStringRef asStringRef(AST::Statement *stmt)
607 return textRefAt(stmt->firstSourceLocation(), stmt->lastSourceLocation());
611 QQmlScript::Parser *_parser;
612 StateStack _stateStack;
614 const QString *_contents;
617 ProcessAST::ProcessAST(QQmlScript::Parser *parser)
622 ProcessAST::~ProcessAST()
626 void ProcessAST::operator()(const QString &code, AST::Node *node)
632 void ProcessAST::accept(AST::Node *node)
634 AST::Node::acceptChild(node, this);
637 const ProcessAST::State ProcessAST::state() const
639 if (_stateStack.isEmpty())
642 return _stateStack.back();
645 QQmlScript::Object *ProcessAST::currentObject() const
647 return state().object;
650 Property *ProcessAST::currentProperty() const
652 return state().property;
655 QString ProcessAST::qualifiedNameId() const
657 return _scope.join(QLatin1String("/"));
660 QString ProcessAST::asString(AST::UiQualifiedId *node) const
664 for (AST::UiQualifiedId *it = node; it; it = it->next) {
665 s.append(it->name.toString());
668 s.append(QLatin1Char('.'));
675 ProcessAST::defineObjectBinding(AST::UiQualifiedId *propertyName,
677 const QString &objectType,
678 AST::SourceLocation typeLocation,
679 LocationSpan location,
680 AST::UiObjectInitializer *initializer)
682 int lastTypeDot = objectType.lastIndexOf(QLatin1Char('.'));
684 // With no preceding qualification, first char is at (-1 + 1) == 0
685 bool isType = !objectType.isEmpty() && objectType.at(lastTypeDot+1).isUpper();
687 int propertyCount = 0;
688 for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
690 _stateStack.pushProperty(name->name,
691 this->location(name));
694 if (!onAssignment && propertyCount && currentProperty() && !currentProperty()->values.isEmpty()) {
696 error.setDescription(QCoreApplication::translate("QQmlParser","Property value set multiple times"));
697 error.setLine(this->location(propertyName).start.line);
698 error.setColumn(this->location(propertyName).start.column);
699 _parser->_errors << error;
705 // Is the identifier qualified by a namespace?
706 int namespaceLength = 0;
707 if (lastTypeDot > 0) {
708 const QString qualifier(objectType.left(lastTypeDot));
710 for (int ii = 0; ii < _parser->_imports.count(); ++ii) {
711 const QQmlScript::Import &import = _parser->_imports.at(ii);
712 if (import.qualifier == qualifier) {
713 // The qualifier is a namespace - expect a type here
714 namespaceLength = qualifier.length() + 1;
720 if (propertyCount || !currentObject() || namespaceLength) {
722 error.setDescription(QCoreApplication::translate("QQmlParser","Expected type name"));
723 error.setLine(typeLocation.startLine);
724 error.setColumn(typeLocation.startColumn + namespaceLength);
725 _parser->_errors << error;
729 LocationSpan loc = ProcessAST::location(typeLocation, typeLocation);
731 loc = ProcessAST::location(propertyName);
733 _stateStack.pushProperty(objectType, loc);
742 QString resolvableObjectType = objectType;
743 if (lastTypeDot >= 0)
744 resolvableObjectType.replace(QLatin1Char('.'),QLatin1Char('/'));
746 QQmlScript::Object *obj = _parser->_pool.New<QQmlScript::Object>();
748 QQmlScript::TypeReference *typeRef = _parser->findOrCreateType(resolvableObjectType);
749 obj->type = typeRef->id;
751 typeRef->refObjects.append(obj);
753 // XXX this doesn't do anything (_scope never builds up)
754 _scope.append(resolvableObjectType);
755 obj->typeName = qualifiedNameId();
758 obj->location = location;
761 Property *prop = currentProperty();
762 QQmlScript::Value *v = _parser->_pool.New<QQmlScript::Value>();
764 v->location = obj->location;
770 while (propertyCount--)
775 if (! _parser->tree()) {
776 _parser->setTree(obj);
778 const State state = _stateStack.top();
779 QQmlScript::Value *v = _parser->_pool.New<QQmlScript::Value>();
781 v->location = obj->location;
782 if (state.property) {
783 state.property->addValue(v);
785 Property *defaultProp = state.object->getDefaultProperty();
786 if (defaultProp->location.start.line == -1) {
787 defaultProp->location = v->location;
788 defaultProp->location.end = defaultProp->location.start;
789 defaultProp->location.range.length = 0;
791 defaultProp->addValue(v);
796 _stateStack.pushObject(obj);
804 LocationSpan ProcessAST::location(AST::UiQualifiedId *id)
806 return location(id->identifierToken, id->identifierToken);
809 LocationSpan ProcessAST::location(AST::SourceLocation start, AST::SourceLocation end)
812 rv.start.line = start.startLine;
813 rv.start.column = start.startColumn;
814 rv.end.line = end.startLine;
815 rv.end.column = end.startColumn + end.length - 1;
816 rv.range.offset = start.offset;
817 rv.range.length = end.offset + end.length - start.offset;
821 // UiProgram: UiImportListOpt UiObjectMemberList ;
822 bool ProcessAST::visit(AST::UiProgram *node)
824 accept(node->imports);
825 accept(node->members->member);
829 // UiImport: T_IMPORT T_STRING_LITERAL ;
830 bool ProcessAST::visit(AST::UiImport *node)
833 QQmlScript::Import import;
835 if (!node->fileName.isNull()) {
836 uri = node->fileName.toString();
838 if (uri.endsWith(QLatin1String(".js"))) {
839 import.type = QQmlScript::Import::Script;
841 import.type = QQmlScript::Import::File;
844 import.type = QQmlScript::Import::Library;
845 uri = asString(node->importUri);
848 AST::SourceLocation startLoc = node->importToken;
849 AST::SourceLocation endLoc = node->semicolonToken;
852 if (!node->importId.isNull()) {
853 import.qualifier = node->importId.toString();
854 if (!import.qualifier.at(0).isUpper()) {
856 error.setDescription(QCoreApplication::translate("QQmlParser","Invalid import qualifier ID"));
857 error.setLine(node->importIdToken.startLine);
858 error.setColumn(node->importIdToken.startColumn);
859 _parser->_errors << error;
862 if (import.qualifier == QLatin1String("Qt")) {
864 error.setDescription(QCoreApplication::translate("QQmlParser","Reserved name \"Qt\" cannot be used as an qualifier"));
865 error.setLine(node->importIdToken.startLine);
866 error.setColumn(node->importIdToken.startColumn);
867 _parser->_errors << error;
871 // Check for script qualifier clashes
872 bool isScript = import.type == QQmlScript::Import::Script;
873 for (int ii = 0; ii < _parser->_imports.count(); ++ii) {
874 const QQmlScript::Import &other = _parser->_imports.at(ii);
875 bool otherIsScript = other.type == QQmlScript::Import::Script;
877 if ((isScript || otherIsScript) && import.qualifier == other.qualifier) {
879 error.setDescription(QCoreApplication::translate("QQmlParser","Script import qualifiers must be unique."));
880 error.setLine(node->importIdToken.startLine);
881 error.setColumn(node->importIdToken.startColumn);
882 _parser->_errors << error;
887 } else if (import.type == QQmlScript::Import::Script) {
889 error.setDescription(QCoreApplication::translate("QQmlParser","Script import requires a qualifier"));
890 error.setLine(node->fileNameToken.startLine);
891 error.setColumn(node->fileNameToken.startColumn);
892 _parser->_errors << error;
896 if (node->versionToken.isValid()) {
897 import.version = textAt(node->versionToken);
898 } else if (import.type == QQmlScript::Import::Library) {
900 error.setDescription(QCoreApplication::translate("QQmlParser","Library import requires a version"));
901 error.setLine(node->importIdToken.startLine);
902 error.setColumn(node->importIdToken.startColumn);
903 _parser->_errors << error;
908 import.location = location(startLoc, endLoc);
911 _parser->_imports << import;
916 bool ProcessAST::visit(AST::UiPublicMember *node)
918 static const struct TypeNameToType {
921 Object::DynamicProperty::Type type;
924 } propTypeNameToTypes[] = {
925 { "int", strlen("int"), Object::DynamicProperty::Int, "int", strlen("int") },
926 { "bool", strlen("bool"), Object::DynamicProperty::Bool, "bool", strlen("bool") },
927 { "double", strlen("double"), Object::DynamicProperty::Real, "double", strlen("double") },
928 { "real", strlen("real"), Object::DynamicProperty::Real, "double", strlen("double") },
929 { "string", strlen("string"), Object::DynamicProperty::String, "QString", strlen("QString") },
930 { "url", strlen("url"), Object::DynamicProperty::Url, "QUrl", strlen("QUrl") },
931 { "color", strlen("color"), Object::DynamicProperty::Color, "QColor", strlen("QColor") },
932 // Internally QTime, QDate and QDateTime are all supported.
933 // To be more consistent with JavaScript we expose only
934 // QDateTime as it matches closely with the Date JS type.
935 // We also call it "date" to match.
936 // { "time", strlen("time"), Object::DynamicProperty::Time, "QTime", strlen("QTime") },
937 // { "date", strlen("date"), Object::DynamicProperty::Date, "QDate", strlen("QDate") },
938 { "date", strlen("date"), Object::DynamicProperty::DateTime, "QDateTime", strlen("QDateTime") },
939 { "variant", strlen("variant"), Object::DynamicProperty::Variant, "QVariant", strlen("QVariant") },
940 { "var", strlen("var"), Object::DynamicProperty::Var, "QVariant", strlen("QVariant") }
942 static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) /
943 sizeof(propTypeNameToTypes[0]);
945 if(node->type == AST::UiPublicMember::Signal) {
946 Object::DynamicSignal *signal = _parser->_pool.New<Object::DynamicSignal>();
947 signal->name = node->name;
949 AST::UiParameterList *p = node->parameters;
951 while (p) { paramLength++; p = p->next; }
952 p = node->parameters;
955 signal->parameterTypes = _parser->_pool.NewRawList<QHashedCStringRef>(paramLength);
956 signal->parameterNames = _parser->_pool.NewRawList<QHashedStringRef>(paramLength);
961 const QStringRef &memberType = p->type;
963 const TypeNameToType *type = 0;
964 for(int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) {
965 const TypeNameToType *t = propTypeNameToTypes + typeIndex;
966 if (t->nameLength == memberType.length() &&
967 QHashedString::compare(memberType.constData(), t->name, t->nameLength)) {
975 error.setDescription(QCoreApplication::translate("QQmlParser","Expected parameter type"));
976 error.setLine(node->typeToken.startLine);
977 error.setColumn(node->typeToken.startColumn);
978 _parser->_errors << error;
982 signal->parameterTypes[index] = QHashedCStringRef(type->qtName, type->qtNameLength);
983 signal->parameterNames[index] = QHashedStringRef(p->name);
988 signal->location = location(node->typeToken, node->semicolonToken);
989 _stateStack.top().object->dynamicSignals.append(signal);
991 const QStringRef &memberType = node->memberType;
992 const QStringRef &name = node->name;
994 bool typeFound = false;
995 Object::DynamicProperty::Type type;
997 if ((unsigned)memberType.length() == strlen("alias") &&
998 QHashedString::compare(memberType.constData(), "alias", strlen("alias"))) {
999 type = Object::DynamicProperty::Alias;
1003 for(int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) {
1004 const TypeNameToType *t = propTypeNameToTypes + ii;
1005 if (t->nameLength == memberType.length() &&
1006 QHashedString::compare(memberType.constData(), t->name, t->nameLength)) {
1012 if (!typeFound && memberType.at(0).isUpper()) {
1013 const QStringRef &typeModifier = node->typeModifier;
1015 if (typeModifier.isEmpty()) {
1016 type = Object::DynamicProperty::Custom;
1017 } else if((unsigned)typeModifier.length() == strlen("list") &&
1018 QHashedString::compare(typeModifier.constData(), "list", strlen("list"))) {
1019 type = Object::DynamicProperty::CustomList;
1022 error.setDescription(QCoreApplication::translate("QQmlParser","Invalid property type modifier"));
1023 error.setLine(node->typeModifierToken.startLine);
1024 error.setColumn(node->typeModifierToken.startColumn);
1025 _parser->_errors << error;
1029 } else if (!node->typeModifier.isNull()) {
1031 error.setDescription(QCoreApplication::translate("QQmlParser","Unexpected property type modifier"));
1032 error.setLine(node->typeModifierToken.startLine);
1033 error.setColumn(node->typeModifierToken.startColumn);
1034 _parser->_errors << error;
1040 error.setDescription(QCoreApplication::translate("QQmlParser","Expected property type"));
1041 error.setLine(node->typeToken.startLine);
1042 error.setColumn(node->typeToken.startColumn);
1043 _parser->_errors << error;
1047 Object::DynamicProperty *property = _parser->_pool.New<Object::DynamicProperty>();
1048 property->isDefaultProperty = node->isDefaultMember;
1049 property->isReadOnly = node->isReadonlyMember;
1050 property->type = type;
1051 property->nameLocation.line = node->identifierToken.startLine;
1052 property->nameLocation.column = node->identifierToken.startColumn;
1053 if (type >= Object::DynamicProperty::Custom) {
1054 QQmlScript::TypeReference *typeRef =
1055 _parser->findOrCreateType(memberType.toString());
1056 typeRef->refObjects.append(_stateStack.top().object);
1057 property->customType = memberType;
1060 property->name = QHashedStringRef(name);
1061 property->location = location(node->firstSourceLocation(),
1062 node->lastSourceLocation());
1064 if (node->statement) { // default value
1065 property->defaultValue = _parser->_pool.New<Property>();
1066 property->defaultValue->parent = _stateStack.top().object;
1067 property->defaultValue->location =
1068 location(node->statement->firstSourceLocation(),
1069 node->statement->lastSourceLocation());
1070 QQmlScript::Value *value = _parser->_pool.New<QQmlScript::Value>();
1071 value->location = location(node->statement->firstSourceLocation(),
1072 node->statement->lastSourceLocation());
1073 value->value = getVariant(node->statement);
1074 property->defaultValue->values.append(value);
1077 _stateStack.top().object->dynamicProperties.append(property);
1079 // process QML-like initializers (e.g. property Object o: Object {})
1080 accept(node->binding);
1087 // UiObjectMember: UiQualifiedId UiObjectInitializer ;
1088 bool ProcessAST::visit(AST::UiObjectDefinition *node)
1090 LocationSpan l = location(node->firstSourceLocation(),
1091 node->lastSourceLocation());
1093 const QString objectType = asString(node->qualifiedTypeNameId);
1094 const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken;
1096 defineObjectBinding(/*propertyName = */ 0, false, objectType,
1097 typeLocation, l, node->initializer);
1103 // UiObjectMember: UiQualifiedId T_COLON UiQualifiedId UiObjectInitializer ;
1104 bool ProcessAST::visit(AST::UiObjectBinding *node)
1106 LocationSpan l = location(node->qualifiedTypeNameId->identifierToken,
1107 node->initializer->rbraceToken);
1109 const QString objectType = asString(node->qualifiedTypeNameId);
1110 const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken;
1112 defineObjectBinding(node->qualifiedId, node->hasOnToken, objectType,
1113 typeLocation, l, node->initializer);
1118 QQmlScript::Variant ProcessAST::getVariant(AST::Statement *stmt)
1121 if (AST::ExpressionStatement *exprStmt = AST::cast<AST::ExpressionStatement *>(stmt))
1122 return getVariant(exprStmt->expression);
1124 return QQmlScript::Variant(asStringRef(stmt), stmt);
1127 return QQmlScript::Variant();
1130 QQmlScript::Variant ProcessAST::getVariant(AST::ExpressionNode *expr)
1132 if (AST::StringLiteral *lit = AST::cast<AST::StringLiteral *>(expr)) {
1133 return QQmlScript::Variant(lit);
1134 } else if (expr->kind == AST::Node::Kind_TrueLiteral) {
1135 return QQmlScript::Variant(true);
1136 } else if (expr->kind == AST::Node::Kind_FalseLiteral) {
1137 return QQmlScript::Variant(false);
1138 } else if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(expr)) {
1139 return QQmlScript::Variant(lit->value, asStringRef(expr));
1142 if (AST::UnaryMinusExpression *unaryMinus = AST::cast<AST::UnaryMinusExpression *>(expr)) {
1143 if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(unaryMinus->expression)) {
1144 return QQmlScript::Variant(-lit->value, asStringRef(expr));
1148 return QQmlScript::Variant(asStringRef(expr), expr);
1153 // UiObjectMember: UiQualifiedId T_COLON Statement ;
1154 bool ProcessAST::visit(AST::UiScriptBinding *node)
1156 int propertyCount = 0;
1157 AST::UiQualifiedId *propertyName = node->qualifiedId;
1158 for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
1160 _stateStack.pushProperty(name->name,
1164 Property *prop = currentProperty();
1166 if (!prop->values.isEmpty()) {
1168 error.setDescription(QCoreApplication::translate("QQmlParser","Property value set multiple times"));
1169 error.setLine(this->location(propertyName).start.line);
1170 error.setColumn(this->location(propertyName).start.column);
1171 _parser->_errors << error;
1175 QQmlScript::Variant primitive;
1177 if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(node->statement)) {
1178 primitive = getVariant(stmt->expression);
1179 } else { // do binding
1180 primitive = QQmlScript::Variant(asStringRef(node->statement), node->statement);
1183 prop->location.range.length = prop->location.range.offset + prop->location.range.length - node->qualifiedId->identifierToken.offset;
1184 prop->location.range.offset = node->qualifiedId->identifierToken.offset;
1185 QQmlScript::Value *v = _parser->_pool.New<QQmlScript::Value>();
1186 v->value = primitive;
1187 v->location = location(node->statement->firstSourceLocation(),
1188 node->statement->lastSourceLocation());
1192 while (propertyCount--)
1198 // UiObjectMember: UiQualifiedId T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ;
1199 bool ProcessAST::visit(AST::UiArrayBinding *node)
1201 int propertyCount = 0;
1202 AST::UiQualifiedId *propertyName = node->qualifiedId;
1203 for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
1205 _stateStack.pushProperty(name->name,
1209 Property* prop = currentProperty();
1211 if (!prop->values.isEmpty()) {
1213 error.setDescription(QCoreApplication::translate("QQmlParser","Property value set multiple times"));
1214 error.setLine(this->location(propertyName).start.line);
1215 error.setColumn(this->location(propertyName).start.column);
1216 _parser->_errors << error;
1220 accept(node->members);
1222 // For the DOM, store the position of the T_LBRACKET upto the T_RBRACKET as the range:
1223 prop->listValueRange.offset = node->lbracketToken.offset;
1224 prop->listValueRange.length = node->rbracketToken.offset + node->rbracketToken.length - node->lbracketToken.offset;
1226 while (propertyCount--)
1232 bool ProcessAST::visit(AST::UiSourceElement *node)
1234 QQmlScript::Object *obj = currentObject();
1236 if (AST::FunctionDeclaration *funDecl = AST::cast<AST::FunctionDeclaration *>(node->sourceElement)) {
1238 Object::DynamicSlot *slot = _parser->_pool.New<Object::DynamicSlot>();
1239 slot->location = location(funDecl->identifierToken, funDecl->lastSourceLocation());
1241 AST::FormalParameterList *f = funDecl->formals;
1243 slot->parameterNames << f->name.toUtf8();
1247 AST::SourceLocation loc = funDecl->rparenToken;
1248 loc.offset = loc.end();
1249 loc.startColumn += 1;
1250 QString body = textAt(loc, funDecl->rbraceToken);
1251 slot->name = funDecl->name;
1253 obj->dynamicSlots.append(slot);
1257 error.setDescription(QCoreApplication::translate("QQmlParser","JavaScript declaration outside Script element"));
1258 error.setLine(node->firstSourceLocation().startLine);
1259 error.setColumn(node->firstSourceLocation().startColumn);
1260 _parser->_errors << error;
1265 } // end of anonymous namespace
1268 QQmlScript::Parser::Parser()
1274 QQmlScript::Parser::~Parser()
1279 namespace QQmlScript {
1280 class ParserJsASTData
1283 ParserJsASTData(const QString &filename)
1284 : filename(filename) {}
1291 bool QQmlScript::Parser::parse(const QByteArray &qmldata, const QUrl &url,
1292 const QString &urlString)
1296 if (urlString.isEmpty()) {
1297 _scriptFile = url.toString();
1299 // Q_ASSERT(urlString == url.toString());
1300 _scriptFile = urlString;
1303 QTextStream stream(qmldata, QIODevice::ReadOnly);
1304 #ifndef QT_NO_TEXTCODEC
1305 stream.setCodec("UTF-8");
1307 QString *code = _pool.NewString(stream.readAll());
1309 data = new QQmlScript::ParserJsASTData(_scriptFile);
1311 Lexer lexer(&data->engine);
1312 lexer.setCode(*code, /*line = */ 1);
1314 QQmlJS::Parser parser(&data->engine);
1316 if (! parser.parse() || !_errors.isEmpty()) {
1318 // Extract errors from the parser
1319 foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) {
1326 error.setDescription(m.message);
1327 error.setLine(m.loc.startLine);
1328 error.setColumn(m.loc.startColumn);
1334 if (_errors.isEmpty()) {
1335 ProcessAST process(this);
1336 process(*code, parser.ast());
1338 // Set the url for process errors
1339 for(int ii = 0; ii < _errors.count(); ++ii)
1340 _errors[ii].setUrl(url);
1343 return _errors.isEmpty();
1346 QList<QQmlScript::TypeReference*> QQmlScript::Parser::referencedTypes() const
1351 QQmlScript::Object *QQmlScript::Parser::tree() const
1356 QList<QQmlScript::Import> QQmlScript::Parser::imports() const
1361 QList<QQmlError> QQmlScript::Parser::errors() const
1366 static void replaceWithSpace(QString &str, int idx, int n)
1368 QChar *data = str.data() + idx;
1369 const QChar space(QLatin1Char(' '));
1370 for (int ii = 0; ii < n; ++ii)
1374 static QQmlScript::LocationSpan
1375 locationFromLexer(const QQmlJS::Lexer &lex, int startLine, int startColumn, int startOffset)
1377 QQmlScript::LocationSpan l;
1379 l.start.line = startLine; l.start.column = startColumn;
1380 l.end.line = lex.tokenEndLine(); l.end.column = lex.tokenEndColumn();
1381 l.range.offset = startOffset;
1382 l.range.length = lex.tokenOffset() + lex.tokenLength() - startOffset;
1388 Searches for ".pragma <value>" declarations within \a script. Currently supported pragmas
1392 QQmlScript::Object::ScriptBlock::Pragmas QQmlScript::Parser::extractPragmas(QString &script)
1394 QQmlScript::Object::ScriptBlock::Pragmas rv = QQmlScript::Object::ScriptBlock::None;
1396 const QString pragma(QLatin1String("pragma"));
1397 const QString library(QLatin1String("library"));
1400 l.setCode(script, 0);
1402 int token = l.lex();
1405 if (token != QQmlJSGrammar::T_DOT)
1408 int startOffset = l.tokenOffset();
1409 int startLine = l.tokenStartLine();
1413 if (token != QQmlJSGrammar::T_IDENTIFIER ||
1414 l.tokenStartLine() != startLine ||
1415 script.mid(l.tokenOffset(), l.tokenLength()) != pragma)
1420 if (token != QQmlJSGrammar::T_IDENTIFIER ||
1421 l.tokenStartLine() != startLine)
1424 QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
1425 int endOffset = l.tokenLength() + l.tokenOffset();
1428 if (l.tokenStartLine() == startLine)
1431 if (pragmaValue == library) {
1432 rv |= QQmlScript::Object::ScriptBlock::Shared;
1433 replaceWithSpace(script, startOffset, endOffset - startOffset);
1441 #define CHECK_LINE if (l.tokenStartLine() != startLine) return rv;
1442 #define CHECK_TOKEN(t) if (token != QQmlJSGrammar:: t) return rv;
1444 static const int uriTokens[] = {
1445 QQmlJSGrammar::T_IDENTIFIER,
1446 QQmlJSGrammar::T_PROPERTY,
1447 QQmlJSGrammar::T_SIGNAL,
1448 QQmlJSGrammar::T_READONLY,
1449 QQmlJSGrammar::T_ON,
1450 QQmlJSGrammar::T_BREAK,
1451 QQmlJSGrammar::T_CASE,
1452 QQmlJSGrammar::T_CATCH,
1453 QQmlJSGrammar::T_CONTINUE,
1454 QQmlJSGrammar::T_DEFAULT,
1455 QQmlJSGrammar::T_DELETE,
1456 QQmlJSGrammar::T_DO,
1457 QQmlJSGrammar::T_ELSE,
1458 QQmlJSGrammar::T_FALSE,
1459 QQmlJSGrammar::T_FINALLY,
1460 QQmlJSGrammar::T_FOR,
1461 QQmlJSGrammar::T_FUNCTION,
1462 QQmlJSGrammar::T_IF,
1463 QQmlJSGrammar::T_IN,
1464 QQmlJSGrammar::T_INSTANCEOF,
1465 QQmlJSGrammar::T_NEW,
1466 QQmlJSGrammar::T_NULL,
1467 QQmlJSGrammar::T_RETURN,
1468 QQmlJSGrammar::T_SWITCH,
1469 QQmlJSGrammar::T_THIS,
1470 QQmlJSGrammar::T_THROW,
1471 QQmlJSGrammar::T_TRUE,
1472 QQmlJSGrammar::T_TRY,
1473 QQmlJSGrammar::T_TYPEOF,
1474 QQmlJSGrammar::T_VAR,
1475 QQmlJSGrammar::T_VOID,
1476 QQmlJSGrammar::T_WHILE,
1477 QQmlJSGrammar::T_CONST,
1478 QQmlJSGrammar::T_DEBUGGER,
1479 QQmlJSGrammar::T_RESERVED_WORD,
1480 QQmlJSGrammar::T_WITH,
1482 QQmlJSGrammar::EOF_SYMBOL
1484 static inline bool isUriToken(int token)
1486 const int *current = uriTokens;
1487 while (*current != QQmlJSGrammar::EOF_SYMBOL) {
1488 if (*current == token)
1495 QQmlScript::Parser::JavaScriptMetaData QQmlScript::Parser::extractMetaData(QString &script)
1497 JavaScriptMetaData rv;
1499 QQmlScript::Object::ScriptBlock::Pragmas &pragmas = rv.pragmas;
1501 const QString pragma(QLatin1String("pragma"));
1502 const QString js(QLatin1String(".js"));
1503 const QString library(QLatin1String("library"));
1506 l.setCode(script, 0);
1508 int token = l.lex();
1511 if (token != QQmlJSGrammar::T_DOT)
1514 int startOffset = l.tokenOffset();
1515 int startLine = l.tokenStartLine();
1516 int startColumn = l.tokenStartColumn();
1522 if (token == QQmlJSGrammar::T_IMPORT) {
1524 // .import <URI> <Version> as <Identifier>
1525 // .import <file.js> as <Identifier>
1531 if (token == QQmlJSGrammar::T_STRING_LITERAL) {
1533 QString file = l.tokenText();
1535 if (!file.endsWith(js))
1545 CHECK_TOKEN(T_IDENTIFIER);
1548 int endOffset = l.tokenLength() + l.tokenOffset();
1550 QString importId = script.mid(l.tokenOffset(), l.tokenLength());
1552 if (!importId.at(0).isUpper())
1555 QQmlScript::LocationSpan location =
1556 locationFromLexer(l, startLine, startColumn, startOffset);
1559 if (l.tokenStartLine() == startLine)
1562 replaceWithSpace(script, startOffset, endOffset - startOffset);
1565 import.type = Import::Script;
1567 import.qualifier = importId;
1568 import.location = location;
1570 rv.imports << import;
1577 if (!isUriToken(token))
1580 uri.append(l.tokenText());
1584 if (token != QQmlJSGrammar::T_DOT)
1587 uri.append(QLatin1Char('.'));
1593 CHECK_TOKEN(T_NUMERIC_LITERAL);
1594 version = script.mid(l.tokenOffset(), l.tokenLength());
1603 CHECK_TOKEN(T_IDENTIFIER);
1606 int endOffset = l.tokenLength() + l.tokenOffset();
1608 QString importId = script.mid(l.tokenOffset(), l.tokenLength());
1610 if (!importId.at(0).isUpper())
1613 QQmlScript::LocationSpan location =
1614 locationFromLexer(l, startLine, startColumn, startOffset);
1617 if (l.tokenStartLine() == startLine)
1620 replaceWithSpace(script, startOffset, endOffset - startOffset);
1623 import.type = Import::Library;
1625 import.version = version;
1626 import.qualifier = importId;
1627 import.location = location;
1629 rv.imports << import;
1632 } else if (token == QQmlJSGrammar::T_IDENTIFIER &&
1633 script.mid(l.tokenOffset(), l.tokenLength()) == pragma) {
1637 CHECK_TOKEN(T_IDENTIFIER);
1640 QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
1641 int endOffset = l.tokenLength() + l.tokenOffset();
1643 if (pragmaValue == library) {
1644 pragmas |= QQmlScript::Object::ScriptBlock::Shared;
1645 replaceWithSpace(script, startOffset, endOffset - startOffset);
1651 if (l.tokenStartLine() == startLine)
1661 void QQmlScript::Parser::clear()
1664 qDeleteAll(_refTypes);
1676 QQmlScript::TypeReference *QQmlScript::Parser::findOrCreateType(const QString &name)
1678 TypeReference *type = 0;
1680 for (; i < _refTypes.size(); ++i) {
1681 if (_refTypes.at(i)->name == name) {
1682 type = _refTypes.at(i);
1687 type = new TypeReference(i, name);
1688 _refTypes.append(type);
1694 void QQmlScript::Parser::setTree(QQmlScript::Object *tree)