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), typeReference(0), idIndex(-1), metatype(0), synthCache(0),
67 defaultProperty(0), parserStatusCast(-1), componentCompileState(0), nextAliasingObject(0),
72 QQmlScript::Object::~Object()
74 if (synthCache) synthCache->release();
77 void Object::setBindingBit(int b)
79 while (bindingBitmask.size() < 4 * (1 + b / 32))
80 bindingBitmask.append(char(0));
82 quint32 *bits = (quint32 *)bindingBitmask.data();
83 bits[b / 32] |= (1 << (b % 32));
86 QQmlScript::Property *Object::getDefaultProperty()
88 if (!defaultProperty) {
89 defaultProperty = pool()->New<Property>();
90 defaultProperty->parent = this;
92 return defaultProperty;
95 void QQmlScript::Object::addValueProperty(Property *p)
97 valueProperties.append(p);
100 void QQmlScript::Object::addSignalProperty(Property *p)
102 signalProperties.append(p);
105 void QQmlScript::Object::addAttachedProperty(Property *p)
107 attachedProperties.append(p);
110 void QQmlScript::Object::addGroupedProperty(Property *p)
112 groupedProperties.append(p);
115 void QQmlScript::Object::addValueTypeProperty(Property *p)
117 valueTypeProperties.append(p);
120 void QQmlScript::Object::addScriptStringProperty(Property *p)
122 scriptStringProperties.append(p);
125 // This lookup is optimized for missing, and having to create a new property.
126 Property *QQmlScript::Object::getProperty(const QHashedStringRef &name, bool create)
129 quint32 h = name.hash();
130 if (propertiesHashField.testAndSet(h)) {
131 for (Property *p = properties.first(); p; p = properties.next(p)) {
132 if (p->name() == name)
137 Property *property = pool()->New<Property>();
138 property->parent = this;
139 property->_name = name;
140 property->isDefault = false;
141 properties.prepend(property);
144 for (Property *p = properties.first(); p; p = properties.next(p)) {
145 if (p->name() == name)
153 Property *QQmlScript::Object::getProperty(const QStringRef &name, bool create)
155 return getProperty(QHashedStringRef(name), create);
158 Property *QQmlScript::Object::getProperty(const QString &name, bool create)
160 for (Property *p = properties.first(); p; p = properties.next(p)) {
161 if (p->name() == name)
166 Property *property = pool()->New<Property>();
167 property->parent = this;
168 property->_name = QStringRef(pool()->NewString(name));
169 propertiesHashField.testAndSet(property->_name.hash());
170 property->isDefault = false;
171 properties.prepend(property);
178 int QQmlScript::Object::aggregateDynamicSignalParameterCount() const
181 for (DynamicSignal *s = dynamicSignals.first(); s; s = dynamicSignals.next(s))
182 sum += s->parameterTypes.count() + 1; // +1 for return type
186 int QQmlScript::Object::aggregateDynamicSlotParameterCount() const
189 for (DynamicSlot *s = dynamicSlots.first(); s; s = dynamicSlots.next(s))
190 sum += s->parameterNames.count() + 1; // +1 for return type
194 QQmlScript::Object::DynamicProperty::DynamicProperty()
195 : isDefaultProperty(false), isReadOnly(false), type(Variant), defaultValue(0), nextProperty(0),
200 QQmlScript::Object::DynamicSignal::DynamicSignal()
201 : nextSignal(0), nameIndex(-1)
205 QQmlScript::Object::DynamicSlot::DynamicSlot()
206 : nextSlot(0), nameIndex(-1)
210 int QQmlScript::Object::DynamicSlot::parameterNamesLength() const
213 for (int ii = 0; ii < parameterNames.count(); ++ii)
214 rv += parameterNames.at(ii).length();
218 QQmlScript::Property::Property()
219 : parent(0), type(0), index(-1), value(0), isDefault(true), isDeferred(false),
220 isValueTypeSubProperty(false), isAlias(false), isReadOnlyDeclaration(false),
221 scriptStringScope(-1), nextMainProperty(0), nextProperty(0)
225 QQmlScript::Object *QQmlScript::Property::getValue(const LocationSpan &l)
227 if (!value) { value = pool()->New<Object>(); value->location = l; }
231 void QQmlScript::Property::addValue(Value *v)
236 void QQmlScript::Property::addOnValue(Value *v)
241 bool QQmlScript::Property::isEmpty() const
243 return !value && values.isEmpty() && onValues.isEmpty();
246 QQmlScript::Value::Value()
247 : type(Unknown), object(0), bindingReference(0), nextValue(0)
251 QQmlScript::Variant::Variant()
256 QQmlScript::Variant::Variant(const Variant &o)
257 : t(o.t), d(o.d), asWritten(o.asWritten)
261 QQmlScript::Variant::Variant(bool v)
266 QQmlScript::Variant::Variant(double v, const QStringRef &asWritten)
267 : t(Number), d(v), asWritten(asWritten)
271 QQmlScript::Variant::Variant(QQmlJS::AST::StringLiteral *v)
276 QQmlScript::Variant::Variant(const QStringRef &asWritten, QQmlJS::AST::Node *n)
277 : t(Script), n(n), asWritten(asWritten)
281 QQmlScript::Variant &QQmlScript::Variant::operator=(const Variant &o)
285 asWritten = o.asWritten;
289 QQmlScript::Variant::Type QQmlScript::Variant::type() const
294 bool QQmlScript::Variant::asBoolean() const
299 QString QQmlScript::Variant::asString() const
302 return l->value.toString();
304 return asWritten.toString();
308 double QQmlScript::Variant::asNumber() const
313 //reverse of Lexer::singleEscape()
314 QString escapedString(const QString &string)
316 QString tmp = QLatin1String("\"");
317 for (int i = 0; i < string.length(); ++i) {
318 const QChar &c = string.at(i);
319 switch(c.unicode()) {
321 tmp += QLatin1String("\\b");
324 tmp += QLatin1String("\\t");
327 tmp += QLatin1String("\\n");
330 tmp += QLatin1String("\\v");
333 tmp += QLatin1String("\\f");
336 tmp += QLatin1String("\\r");
339 tmp += QLatin1String("\\\"");
342 tmp += QLatin1String("\\\'");
345 tmp += QLatin1String("\\\\");
352 tmp += QLatin1Char('\"');
356 QString QQmlScript::Variant::asScript() const
363 return b?QLatin1String("true"):QLatin1String("false");
365 if (asWritten.isEmpty())
366 return QString::number(d);
368 return asWritten.toString();
370 return escapedString(asString());
372 if (AST::IdentifierExpression *i = AST::cast<AST::IdentifierExpression *>(n)) {
373 return i->name.toString();
375 return asWritten.toString();
379 QQmlJS::AST::Node *QQmlScript::Variant::asAST() const
381 if (type() == Script)
387 bool QQmlScript::Variant::isStringList() const
392 if (type() != Script || !n)
395 AST::ArrayLiteral *array = AST::cast<AST::ArrayLiteral *>(n);
399 AST::ElementList *elements = array->elements;
403 if (!AST::cast<AST::StringLiteral *>(elements->expression))
406 elements = elements->next;
412 QStringList QQmlScript::Variant::asStringList() const
420 AST::ArrayLiteral *array = AST::cast<AST::ArrayLiteral *>(n);
424 AST::ElementList *elements = array->elements;
427 AST::StringLiteral *string = AST::cast<AST::StringLiteral *>(elements->expression);
429 return QStringList();
430 rv.append(string->value.toString());
432 elements = elements->next;
439 // Actual parser classes
443 class ProcessAST: protected AST::Visitor
446 State() : object(0), property(0) {}
447 State(QQmlScript::Object *o) : object(o), property(0) {}
448 State(QQmlScript::Object *o, Property *p) : object(o), property(p) {}
450 QQmlScript::Object *object;
454 struct StateStack : public QStack<State>
456 void pushObject(QQmlScript::Object *obj)
461 void pushProperty(const QString &name, const LocationSpan &location)
463 const State &state = top();
464 if (state.property) {
465 State s(state.property->getValue(location),
466 state.property->getValue(location)->getProperty(name));
467 s.property->location = location;
470 State s(state.object, state.object->getProperty(name));
472 s.property->location = location;
477 void pushProperty(const QStringRef &name, const LocationSpan &location)
479 const State &state = top();
480 if (state.property) {
481 State s(state.property->getValue(location),
482 state.property->getValue(location)->getProperty(name));
483 s.property->location = location;
486 State s(state.object, state.object->getProperty(name));
488 s.property->location = location;
495 ProcessAST(QQmlScript::Parser *parser);
496 virtual ~ProcessAST();
498 void operator()(const QString &code, AST::Node *node);
500 static void extractVersion(QStringRef string, int *maj, int *min);
504 QQmlScript::Object *defineObjectBinding(AST::UiQualifiedId *propertyName, bool onAssignment,
505 const QString &objectType,
506 AST::SourceLocation typeLocation,
507 LocationSpan location,
508 AST::UiObjectInitializer *initializer = 0);
510 QQmlScript::Variant getVariant(AST::Statement *stmt);
511 QQmlScript::Variant getVariant(AST::ExpressionNode *expr);
513 LocationSpan location(AST::SourceLocation start, AST::SourceLocation end);
514 LocationSpan location(AST::UiQualifiedId *);
516 using AST::Visitor::visit;
517 using AST::Visitor::endVisit;
519 virtual bool visit(AST::UiProgram *node);
520 virtual bool visit(AST::UiImport *node);
521 virtual bool visit(AST::UiObjectDefinition *node);
522 virtual bool visit(AST::UiPublicMember *node);
523 virtual bool visit(AST::UiObjectBinding *node);
525 virtual bool visit(AST::UiScriptBinding *node);
526 virtual bool visit(AST::UiArrayBinding *node);
527 virtual bool visit(AST::UiSourceElement *node);
529 void accept(AST::Node *node);
531 QString asString(AST::UiQualifiedId *node) const;
533 const State state() const;
534 QQmlScript::Object *currentObject() const;
535 Property *currentProperty() const;
537 QString textAt(const AST::SourceLocation &loc) const
538 { return _contents->mid(loc.offset, loc.length); }
540 QStringRef textRefAt(const AST::SourceLocation &loc) const
541 { return QStringRef(_contents, loc.offset, loc.length); }
543 QString textAt(const AST::SourceLocation &first,
544 const AST::SourceLocation &last) const
545 { return _contents->mid(first.offset, last.offset + last.length - first.offset); }
547 QStringRef textRefAt(const AST::SourceLocation &first,
548 const AST::SourceLocation &last) const
549 { return QStringRef(_contents, first.offset, last.offset + last.length - first.offset); }
551 QString asString(AST::ExpressionNode *expr)
556 return textAt(expr->firstSourceLocation(), expr->lastSourceLocation());
559 QStringRef asStringRef(AST::ExpressionNode *expr)
564 return textRefAt(expr->firstSourceLocation(), expr->lastSourceLocation());
567 QString asString(AST::Statement *stmt)
572 QString s = textAt(stmt->firstSourceLocation(), stmt->lastSourceLocation());
573 s += QLatin1Char('\n');
577 QStringRef asStringRef(AST::Statement *stmt)
582 return textRefAt(stmt->firstSourceLocation(), stmt->lastSourceLocation());
586 QQmlScript::Parser *_parser;
587 StateStack _stateStack;
588 const QString *_contents;
591 ProcessAST::ProcessAST(QQmlScript::Parser *parser)
596 ProcessAST::~ProcessAST()
600 void ProcessAST::operator()(const QString &code, AST::Node *node)
606 void ProcessAST::accept(AST::Node *node)
608 AST::Node::acceptChild(node, this);
611 const ProcessAST::State ProcessAST::state() const
613 if (_stateStack.isEmpty())
616 return _stateStack.back();
619 QQmlScript::Object *ProcessAST::currentObject() const
621 return state().object;
624 Property *ProcessAST::currentProperty() const
626 return state().property;
629 void ProcessAST::extractVersion(QStringRef string, int *maj, int *min)
631 *maj = -1; *min = -1;
633 if (!string.isEmpty()) {
635 int dot = string.indexOf(QLatin1Char('.'));
638 *maj = string.toString().toInt();
641 const QString *s = string.string();
642 int p = string.position();
643 *maj = QStringRef(s, p, dot).toString().toInt();
644 *min = QStringRef(s, p + dot + 1, string.size() - dot - 1).toString().toInt();
649 QString ProcessAST::asString(AST::UiQualifiedId *node) const
653 for (AST::UiQualifiedId *it = node; it; it = it->next) {
654 s.append(it->name.toString());
657 s.append(QLatin1Char('.'));
664 ProcessAST::defineObjectBinding(AST::UiQualifiedId *propertyName,
666 const QString &objectType,
667 AST::SourceLocation typeLocation,
668 LocationSpan location,
669 AST::UiObjectInitializer *initializer)
671 int lastTypeDot = objectType.lastIndexOf(QLatin1Char('.'));
673 // With no preceding qualification, first char is at (-1 + 1) == 0
674 bool isType = !objectType.isEmpty() && objectType.at(lastTypeDot+1).isUpper();
676 int propertyCount = 0;
677 for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
679 _stateStack.pushProperty(name->name,
680 this->location(name));
683 if (!onAssignment && propertyCount && currentProperty() && !currentProperty()->values.isEmpty()) {
685 error.setDescription(QCoreApplication::translate("QQmlParser","Property value set multiple times"));
686 error.setLine(this->location(propertyName).start.line);
687 error.setColumn(this->location(propertyName).start.column);
688 _parser->_errors << error;
694 // Is the identifier qualified by a namespace?
695 int namespaceLength = 0;
696 if (lastTypeDot > 0) {
697 const QString qualifier(objectType.left(lastTypeDot));
699 for (int ii = 0; ii < _parser->_imports.count(); ++ii) {
700 const QQmlScript::Import &import = _parser->_imports.at(ii);
701 if (import.qualifier == qualifier) {
702 // The qualifier is a namespace - expect a type here
703 namespaceLength = qualifier.length() + 1;
709 if (propertyCount || !currentObject() || namespaceLength) {
711 error.setDescription(QCoreApplication::translate("QQmlParser","Expected type name"));
712 error.setLine(typeLocation.startLine);
713 error.setColumn(typeLocation.startColumn + namespaceLength);
714 _parser->_errors << error;
718 LocationSpan loc = ProcessAST::location(typeLocation, typeLocation);
720 loc = ProcessAST::location(propertyName);
722 _stateStack.pushProperty(objectType, loc);
731 QQmlScript::Object *obj = _parser->_pool.New<QQmlScript::Object>();
733 obj->type = _parser->findOrCreateTypeId(objectType, obj);
734 obj->typeReference = _parser->_refTypes.at(obj->type);
735 obj->location = location;
738 Property *prop = currentProperty();
739 QQmlScript::Value *v = _parser->_pool.New<QQmlScript::Value>();
741 v->location = obj->location;
747 while (propertyCount--)
752 if (! _parser->tree()) {
753 _parser->setTree(obj);
755 const State state = _stateStack.top();
756 QQmlScript::Value *v = _parser->_pool.New<QQmlScript::Value>();
758 v->location = obj->location;
759 if (state.property) {
760 state.property->addValue(v);
762 Property *defaultProp = state.object->getDefaultProperty();
763 if (defaultProp->location.start.line == 0) {
764 defaultProp->location = v->location;
765 defaultProp->location.end = defaultProp->location.start;
766 defaultProp->location.range.length = 0;
768 defaultProp->addValue(v);
773 _stateStack.pushObject(obj);
781 LocationSpan ProcessAST::location(AST::UiQualifiedId *id)
783 return location(id->identifierToken, id->identifierToken);
786 LocationSpan ProcessAST::location(AST::SourceLocation start, AST::SourceLocation end)
789 rv.start.line = start.startLine;
790 rv.start.column = start.startColumn;
791 rv.end.line = end.startLine;
792 rv.end.column = end.startColumn + end.length - 1;
793 rv.range.offset = start.offset;
794 rv.range.length = end.offset + end.length - start.offset;
798 // UiProgram: UiImportListOpt UiObjectMemberList ;
799 bool ProcessAST::visit(AST::UiProgram *node)
801 accept(node->imports);
802 accept(node->members->member);
806 // UiImport: T_IMPORT T_STRING_LITERAL ;
807 bool ProcessAST::visit(AST::UiImport *node)
810 QQmlScript::Import import;
812 if (!node->fileName.isNull()) {
813 uri = node->fileName.toString();
815 if (uri.endsWith(QLatin1String(".js"))) {
816 import.type = QQmlScript::Import::Script;
818 import.type = QQmlScript::Import::File;
821 import.type = QQmlScript::Import::Library;
822 uri = asString(node->importUri);
825 AST::SourceLocation startLoc = node->importToken;
826 AST::SourceLocation endLoc = node->semicolonToken;
829 if (!node->importId.isNull()) {
830 import.qualifier = node->importId.toString();
831 if (!import.qualifier.at(0).isUpper()) {
833 error.setDescription(QCoreApplication::translate("QQmlParser","Invalid import qualifier ID"));
834 error.setLine(node->importIdToken.startLine);
835 error.setColumn(node->importIdToken.startColumn);
836 _parser->_errors << error;
839 if (import.qualifier == QLatin1String("Qt")) {
841 error.setDescription(QCoreApplication::translate("QQmlParser","Reserved name \"Qt\" cannot be used as an qualifier"));
842 error.setLine(node->importIdToken.startLine);
843 error.setColumn(node->importIdToken.startColumn);
844 _parser->_errors << error;
848 // Check for script qualifier clashes
849 bool isScript = import.type == QQmlScript::Import::Script;
850 for (int ii = 0; ii < _parser->_imports.count(); ++ii) {
851 const QQmlScript::Import &other = _parser->_imports.at(ii);
852 bool otherIsScript = other.type == QQmlScript::Import::Script;
854 if ((isScript || otherIsScript) && import.qualifier == other.qualifier) {
856 error.setDescription(QCoreApplication::translate("QQmlParser","Script import qualifiers must be unique."));
857 error.setLine(node->importIdToken.startLine);
858 error.setColumn(node->importIdToken.startColumn);
859 _parser->_errors << error;
864 } else if (import.type == QQmlScript::Import::Script) {
866 error.setDescription(QCoreApplication::translate("QQmlParser","Script import requires a qualifier"));
867 error.setLine(node->fileNameToken.startLine);
868 error.setColumn(node->fileNameToken.startColumn);
869 _parser->_errors << error;
873 if (node->versionToken.isValid()) {
874 extractVersion(textRefAt(node->versionToken), &import.majorVersion, &import.minorVersion);
875 } else if (import.type == QQmlScript::Import::Library) {
877 error.setDescription(QCoreApplication::translate("QQmlParser","Library import requires a version"));
878 error.setLine(node->importIdToken.startLine);
879 error.setColumn(node->importIdToken.startColumn);
880 _parser->_errors << error;
885 import.location = location(startLoc, endLoc);
888 _parser->_imports << import;
893 bool ProcessAST::visit(AST::UiPublicMember *node)
895 static const struct TypeNameToType {
898 Object::DynamicProperty::Type type;
899 } propTypeNameToTypes[] = {
900 { "int", strlen("int"), Object::DynamicProperty::Int },
901 { "bool", strlen("bool"), Object::DynamicProperty::Bool },
902 { "double", strlen("double"), Object::DynamicProperty::Real },
903 { "real", strlen("real"), Object::DynamicProperty::Real },
904 { "string", strlen("string"), Object::DynamicProperty::String },
905 { "url", strlen("url"), Object::DynamicProperty::Url },
906 { "color", strlen("color"), Object::DynamicProperty::Color },
907 // Internally QTime, QDate and QDateTime are all supported.
908 // To be more consistent with JavaScript we expose only
909 // QDateTime as it matches closely with the Date JS type.
910 // We also call it "date" to match.
911 // { "time", strlen("time"), Object::DynamicProperty::Time },
912 // { "date", strlen("date"), Object::DynamicProperty::Date },
913 { "date", strlen("date"), Object::DynamicProperty::DateTime },
914 { "rect", strlen("rect"), Object::DynamicProperty::Rect },
915 { "point", strlen("point"), Object::DynamicProperty::Point },
916 { "size", strlen("size"), Object::DynamicProperty::Size },
917 { "font", strlen("font"), Object::DynamicProperty::Font },
918 { "vector2d", strlen("vector2d"), Object::DynamicProperty::Vector2D },
919 { "vector3d", strlen("vector3d"), Object::DynamicProperty::Vector3D },
920 { "vector4d", strlen("vector4d"), Object::DynamicProperty::Vector4D },
921 { "quaternion", strlen("quaternion"), Object::DynamicProperty::Quaternion },
922 { "matrix4x4", strlen("matrix4x4"), Object::DynamicProperty::Matrix4x4 },
923 { "variant", strlen("variant"), Object::DynamicProperty::Variant },
924 { "var", strlen("var"), Object::DynamicProperty::Var }
926 static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) /
927 sizeof(propTypeNameToTypes[0]);
929 if(node->type == AST::UiPublicMember::Signal) {
930 Object::DynamicSignal *signal = _parser->_pool.New<Object::DynamicSignal>();
931 signal->name = node->name;
933 AST::UiParameterList *p = node->parameters;
935 while (p) { paramLength++; p = p->next; }
936 p = node->parameters;
939 signal->parameterTypes = _parser->_pool.NewRawList<Object::DynamicProperty::Type>(paramLength);
940 signal->parameterTypeNames = _parser->_pool.NewRawList<QHashedStringRef>(paramLength);
941 signal->parameterNames = _parser->_pool.NewRawList<QHashedStringRef>(paramLength);
946 const QStringRef &memberType = p->type;
948 if (memberType.isEmpty()) {
950 error.setDescription(QCoreApplication::translate("QQmlParser","Expected parameter type"));
951 error.setLine(node->typeToken.startLine);
952 error.setColumn(node->typeToken.startColumn);
953 _parser->_errors << error;
957 const TypeNameToType *type = 0;
958 for(int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) {
959 const TypeNameToType *t = propTypeNameToTypes + typeIndex;
960 if (t->nameLength == size_t(memberType.length()) &&
961 QHashedString::compare(memberType.constData(), t->name, t->nameLength)) {
968 if (memberType.at(0).isUpper()) {
969 // Must be a QML object type.
970 // Lazily determine type during compilation.
971 signal->parameterTypes[index] = Object::DynamicProperty::Custom;
972 signal->parameterTypeNames[index] = QHashedStringRef(p->type);
975 QString errStr = QCoreApplication::translate("QQmlParser","Invalid signal parameter type: ");
976 errStr.append(memberType.toString());
977 error.setDescription(errStr);
978 error.setLine(node->typeToken.startLine);
979 error.setColumn(node->typeToken.startColumn);
980 _parser->_errors << error;
984 // the parameter is a known basic type
985 signal->parameterTypes[index] = type->type;
988 signal->parameterNames[index] = QHashedStringRef(p->name);
993 signal->location = location(node->typeToken, node->semicolonToken);
994 _stateStack.top().object->dynamicSignals.append(signal);
996 const QStringRef &memberType = node->memberType;
997 const QStringRef &name = node->name;
999 bool typeFound = false;
1000 Object::DynamicProperty::Type type;
1002 if ((unsigned)memberType.length() == strlen("alias") &&
1003 QHashedString::compare(memberType.constData(), "alias", strlen("alias"))) {
1004 type = Object::DynamicProperty::Alias;
1008 for(int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) {
1009 const TypeNameToType *t = propTypeNameToTypes + ii;
1010 if (t->nameLength == size_t(memberType.length()) &&
1011 QHashedString::compare(memberType.constData(), t->name, t->nameLength)) {
1017 if (!typeFound && memberType.at(0).isUpper()) {
1018 const QStringRef &typeModifier = node->typeModifier;
1020 if (typeModifier.isEmpty()) {
1021 type = Object::DynamicProperty::Custom;
1022 } else if((unsigned)typeModifier.length() == strlen("list") &&
1023 QHashedString::compare(typeModifier.constData(), "list", strlen("list"))) {
1024 type = Object::DynamicProperty::CustomList;
1027 error.setDescription(QCoreApplication::translate("QQmlParser","Invalid property type modifier"));
1028 error.setLine(node->typeModifierToken.startLine);
1029 error.setColumn(node->typeModifierToken.startColumn);
1030 _parser->_errors << error;
1034 } else if (!node->typeModifier.isNull()) {
1036 error.setDescription(QCoreApplication::translate("QQmlParser","Unexpected property type modifier"));
1037 error.setLine(node->typeModifierToken.startLine);
1038 error.setColumn(node->typeModifierToken.startColumn);
1039 _parser->_errors << error;
1045 error.setDescription(QCoreApplication::translate("QQmlParser","Expected property type"));
1046 error.setLine(node->typeToken.startLine);
1047 error.setColumn(node->typeToken.startColumn);
1048 _parser->_errors << error;
1052 Object::DynamicProperty *property = _parser->_pool.New<Object::DynamicProperty>();
1053 property->isDefaultProperty = node->isDefaultMember;
1054 property->isReadOnly = node->isReadonlyMember;
1055 property->type = type;
1056 property->nameLocation.line = node->identifierToken.startLine;
1057 property->nameLocation.column = node->identifierToken.startColumn;
1058 if (type >= Object::DynamicProperty::Custom) {
1059 // This forces the type to be added to the resolved types list
1060 _parser->findOrCreateTypeId(memberType.toString(), _stateStack.top().object);
1061 property->customType = memberType;
1064 property->name = QHashedStringRef(name);
1065 property->location = location(node->firstSourceLocation(),
1066 node->lastSourceLocation());
1068 if (node->statement) { // default value
1069 property->defaultValue = _parser->_pool.New<Property>();
1070 property->defaultValue->parent = _stateStack.top().object;
1071 property->defaultValue->location =
1072 location(node->statement->firstSourceLocation(),
1073 node->statement->lastSourceLocation());
1074 QQmlScript::Value *value = _parser->_pool.New<QQmlScript::Value>();
1075 value->location = location(node->statement->firstSourceLocation(),
1076 node->statement->lastSourceLocation());
1077 value->value = getVariant(node->statement);
1078 property->defaultValue->values.append(value);
1081 _stateStack.top().object->dynamicProperties.append(property);
1083 // process QML-like initializers (e.g. property Object o: Object {})
1084 accept(node->binding);
1091 // UiObjectMember: UiQualifiedId UiObjectInitializer ;
1092 bool ProcessAST::visit(AST::UiObjectDefinition *node)
1094 LocationSpan l = location(node->firstSourceLocation(),
1095 node->lastSourceLocation());
1097 const QString objectType = asString(node->qualifiedTypeNameId);
1098 const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken;
1100 defineObjectBinding(/*propertyName = */ 0, false, objectType,
1101 typeLocation, l, node->initializer);
1107 // UiObjectMember: UiQualifiedId T_COLON UiQualifiedId UiObjectInitializer ;
1108 bool ProcessAST::visit(AST::UiObjectBinding *node)
1110 LocationSpan l = location(node->qualifiedTypeNameId->identifierToken,
1111 node->initializer->rbraceToken);
1113 const QString objectType = asString(node->qualifiedTypeNameId);
1114 const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken;
1116 defineObjectBinding(node->qualifiedId, node->hasOnToken, objectType,
1117 typeLocation, l, node->initializer);
1122 QQmlScript::Variant ProcessAST::getVariant(AST::Statement *stmt)
1125 if (AST::ExpressionStatement *exprStmt = AST::cast<AST::ExpressionStatement *>(stmt))
1126 return getVariant(exprStmt->expression);
1128 return QQmlScript::Variant(asStringRef(stmt), stmt);
1131 return QQmlScript::Variant();
1134 QQmlScript::Variant ProcessAST::getVariant(AST::ExpressionNode *expr)
1136 if (AST::StringLiteral *lit = AST::cast<AST::StringLiteral *>(expr)) {
1137 return QQmlScript::Variant(lit);
1138 } else if (expr->kind == AST::Node::Kind_TrueLiteral) {
1139 return QQmlScript::Variant(true);
1140 } else if (expr->kind == AST::Node::Kind_FalseLiteral) {
1141 return QQmlScript::Variant(false);
1142 } else if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(expr)) {
1143 return QQmlScript::Variant(lit->value, asStringRef(expr));
1146 if (AST::UnaryMinusExpression *unaryMinus = AST::cast<AST::UnaryMinusExpression *>(expr)) {
1147 if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(unaryMinus->expression)) {
1148 return QQmlScript::Variant(-lit->value, asStringRef(expr));
1152 return QQmlScript::Variant(asStringRef(expr), expr);
1157 // UiObjectMember: UiQualifiedId T_COLON Statement ;
1158 bool ProcessAST::visit(AST::UiScriptBinding *node)
1160 int propertyCount = 0;
1161 AST::UiQualifiedId *propertyName = node->qualifiedId;
1162 for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
1164 _stateStack.pushProperty(name->name,
1168 Property *prop = currentProperty();
1170 if (!prop->values.isEmpty()) {
1172 error.setDescription(QCoreApplication::translate("QQmlParser","Property value set multiple times"));
1173 error.setLine(this->location(propertyName).start.line);
1174 error.setColumn(this->location(propertyName).start.column);
1175 _parser->_errors << error;
1179 QQmlScript::Variant primitive;
1181 if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(node->statement)) {
1182 primitive = getVariant(stmt->expression);
1183 } else { // do binding
1184 primitive = QQmlScript::Variant(asStringRef(node->statement), node->statement);
1187 prop->location.range.length = prop->location.range.offset + prop->location.range.length - node->qualifiedId->identifierToken.offset;
1188 prop->location.range.offset = node->qualifiedId->identifierToken.offset;
1189 QQmlScript::Value *v = _parser->_pool.New<QQmlScript::Value>();
1190 v->value = primitive;
1191 v->location = location(node->statement->firstSourceLocation(),
1192 node->statement->lastSourceLocation());
1196 while (propertyCount--)
1202 // UiObjectMember: UiQualifiedId T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ;
1203 bool ProcessAST::visit(AST::UiArrayBinding *node)
1205 int propertyCount = 0;
1206 AST::UiQualifiedId *propertyName = node->qualifiedId;
1207 for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
1209 _stateStack.pushProperty(name->name,
1213 Property* prop = currentProperty();
1215 if (!prop->values.isEmpty()) {
1217 error.setDescription(QCoreApplication::translate("QQmlParser","Property value set multiple times"));
1218 error.setLine(this->location(propertyName).start.line);
1219 error.setColumn(this->location(propertyName).start.column);
1220 _parser->_errors << error;
1224 accept(node->members);
1226 // For the DOM, store the position of the T_LBRACKET upto the T_RBRACKET as the range:
1227 prop->listValueRange.offset = node->lbracketToken.offset;
1228 prop->listValueRange.length = node->rbracketToken.offset + node->rbracketToken.length - node->lbracketToken.offset;
1230 while (propertyCount--)
1236 bool ProcessAST::visit(AST::UiSourceElement *node)
1238 QQmlScript::Object *obj = currentObject();
1240 if (AST::FunctionDeclaration *funDecl = AST::cast<AST::FunctionDeclaration *>(node->sourceElement)) {
1242 Object::DynamicSlot *slot = _parser->_pool.New<Object::DynamicSlot>();
1243 slot->location = location(funDecl->identifierToken, funDecl->lastSourceLocation());
1245 AST::FormalParameterList *f = funDecl->formals;
1247 slot->parameterNames << f->name.toUtf8();
1251 AST::SourceLocation loc = funDecl->rparenToken;
1252 loc.offset = loc.end();
1253 loc.startColumn += 1;
1254 QString body = textAt(loc, funDecl->rbraceToken);
1255 slot->name = funDecl->name;
1257 obj->dynamicSlots.append(slot);
1261 error.setDescription(QCoreApplication::translate("QQmlParser","JavaScript declaration outside Script element"));
1262 error.setLine(node->firstSourceLocation().startLine);
1263 error.setColumn(node->firstSourceLocation().startColumn);
1264 _parser->_errors << error;
1269 } // end of anonymous namespace
1272 QQmlScript::Parser::Parser()
1278 QQmlScript::Parser::~Parser()
1283 namespace QQmlScript {
1284 class ParserJsASTData
1287 ParserJsASTData(const QString &filename)
1288 : filename(filename) {}
1295 QByteArray QQmlScript::Parser::preparseData() const
1297 return QByteArray();
1300 bool QQmlScript::Parser::parse(const QString &qmlcode, const QByteArray & /* preparseData */,
1301 const QUrl &url, const QString &urlString)
1305 if (urlString.isEmpty()) {
1306 _scriptFile = url.toString();
1308 // Q_ASSERT(urlString == url.toString());
1309 _scriptFile = urlString;
1312 QString *code = _pool.NewString(qmlcode);
1314 data = new QQmlScript::ParserJsASTData(_scriptFile);
1316 Lexer lexer(&data->engine);
1317 lexer.setCode(*code, /*line = */ 1);
1319 QQmlJS::Parser parser(&data->engine);
1321 if (! parser.parse() || !_errors.isEmpty()) {
1323 // Extract errors from the parser
1324 foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) {
1331 error.setDescription(m.message);
1332 error.setLine(m.loc.startLine);
1333 error.setColumn(m.loc.startColumn);
1339 if (_errors.isEmpty()) {
1340 ProcessAST process(this);
1341 process(*code, parser.ast());
1343 // Set the url for process errors
1344 for(int ii = 0; ii < _errors.count(); ++ii)
1345 _errors[ii].setUrl(url);
1348 return _errors.isEmpty();
1351 QList<QQmlScript::TypeReference*> QQmlScript::Parser::referencedTypes() const
1356 QQmlScript::Object *QQmlScript::Parser::tree() const
1361 QList<QQmlScript::Import> QQmlScript::Parser::imports() const
1366 QList<QQmlError> QQmlScript::Parser::errors() const
1371 static void replaceWithSpace(QString &str, int idx, int n)
1373 QChar *data = str.data() + idx;
1374 const QChar space(QLatin1Char(' '));
1375 for (int ii = 0; ii < n; ++ii)
1379 static QQmlScript::LocationSpan
1380 locationFromLexer(const QQmlJS::Lexer &lex, int startLine, int startColumn, int startOffset)
1382 QQmlScript::LocationSpan l;
1384 l.start.line = startLine; l.start.column = startColumn;
1385 l.end.line = lex.tokenEndLine(); l.end.column = lex.tokenEndColumn();
1386 l.range.offset = startOffset;
1387 l.range.length = lex.tokenOffset() + lex.tokenLength() - startOffset;
1393 Searches for ".pragma <value>" declarations within \a script. Currently supported pragmas
1397 QQmlScript::Object::ScriptBlock::Pragmas QQmlScript::Parser::extractPragmas(QString &script)
1399 QQmlScript::Object::ScriptBlock::Pragmas rv = QQmlScript::Object::ScriptBlock::None;
1401 const QString pragma(QLatin1String("pragma"));
1402 const QString library(QLatin1String("library"));
1405 l.setCode(script, 0);
1407 int token = l.lex();
1410 if (token != QQmlJSGrammar::T_DOT)
1413 int startOffset = l.tokenOffset();
1414 int startLine = l.tokenStartLine();
1418 if (token != QQmlJSGrammar::T_IDENTIFIER ||
1419 l.tokenStartLine() != startLine ||
1420 script.mid(l.tokenOffset(), l.tokenLength()) != pragma)
1425 if (token != QQmlJSGrammar::T_IDENTIFIER ||
1426 l.tokenStartLine() != startLine)
1429 QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
1430 int endOffset = l.tokenLength() + l.tokenOffset();
1433 if (l.tokenStartLine() == startLine)
1436 if (pragmaValue == library) {
1437 rv |= QQmlScript::Object::ScriptBlock::Shared;
1438 replaceWithSpace(script, startOffset, endOffset - startOffset);
1446 #define CHECK_LINE if (l.tokenStartLine() != startLine) return rv;
1447 #define CHECK_TOKEN(t) if (token != QQmlJSGrammar:: t) return rv;
1449 static const int uriTokens[] = {
1450 QQmlJSGrammar::T_IDENTIFIER,
1451 QQmlJSGrammar::T_PROPERTY,
1452 QQmlJSGrammar::T_SIGNAL,
1453 QQmlJSGrammar::T_READONLY,
1454 QQmlJSGrammar::T_ON,
1455 QQmlJSGrammar::T_BREAK,
1456 QQmlJSGrammar::T_CASE,
1457 QQmlJSGrammar::T_CATCH,
1458 QQmlJSGrammar::T_CONTINUE,
1459 QQmlJSGrammar::T_DEFAULT,
1460 QQmlJSGrammar::T_DELETE,
1461 QQmlJSGrammar::T_DO,
1462 QQmlJSGrammar::T_ELSE,
1463 QQmlJSGrammar::T_FALSE,
1464 QQmlJSGrammar::T_FINALLY,
1465 QQmlJSGrammar::T_FOR,
1466 QQmlJSGrammar::T_FUNCTION,
1467 QQmlJSGrammar::T_IF,
1468 QQmlJSGrammar::T_IN,
1469 QQmlJSGrammar::T_INSTANCEOF,
1470 QQmlJSGrammar::T_NEW,
1471 QQmlJSGrammar::T_NULL,
1472 QQmlJSGrammar::T_RETURN,
1473 QQmlJSGrammar::T_SWITCH,
1474 QQmlJSGrammar::T_THIS,
1475 QQmlJSGrammar::T_THROW,
1476 QQmlJSGrammar::T_TRUE,
1477 QQmlJSGrammar::T_TRY,
1478 QQmlJSGrammar::T_TYPEOF,
1479 QQmlJSGrammar::T_VAR,
1480 QQmlJSGrammar::T_VOID,
1481 QQmlJSGrammar::T_WHILE,
1482 QQmlJSGrammar::T_CONST,
1483 QQmlJSGrammar::T_DEBUGGER,
1484 QQmlJSGrammar::T_RESERVED_WORD,
1485 QQmlJSGrammar::T_WITH,
1487 QQmlJSGrammar::EOF_SYMBOL
1489 static inline bool isUriToken(int token)
1491 const int *current = uriTokens;
1492 while (*current != QQmlJSGrammar::EOF_SYMBOL) {
1493 if (*current == token)
1500 QQmlScript::Parser::JavaScriptMetaData QQmlScript::Parser::extractMetaData(QString &script, QQmlError *error)
1504 JavaScriptMetaData rv;
1506 QQmlScript::Object::ScriptBlock::Pragmas &pragmas = rv.pragmas;
1508 const QString pragma(QLatin1String("pragma"));
1509 const QString js(QLatin1String(".js"));
1510 const QString library(QLatin1String("library"));
1513 l.setCode(script, 0);
1515 int token = l.lex();
1518 if (token != QQmlJSGrammar::T_DOT)
1521 int startOffset = l.tokenOffset();
1522 int startLine = l.tokenStartLine();
1523 int startColumn = l.tokenStartColumn();
1525 QQmlError importError;
1526 importError.setLine(startLine + 1); // 0-based, adjust to be 1-based
1532 if (token == QQmlJSGrammar::T_IMPORT) {
1534 // .import <URI> <Version> as <Identifier>
1535 // .import <file.js> as <Identifier>
1541 if (token == QQmlJSGrammar::T_STRING_LITERAL) {
1543 QString file = l.tokenText();
1545 if (!file.endsWith(js)) {
1546 importError.setDescription(QCoreApplication::translate("QQmlParser","Imported file must be a script"));
1547 importError.setColumn(l.tokenStartColumn());
1548 *error = importError;
1552 bool invalidImport = false;
1556 if ((token != QQmlJSGrammar::T_AS) || (l.tokenStartLine() != startLine)) {
1557 invalidImport = true;
1561 if ((token != QQmlJSGrammar::T_IDENTIFIER) || (l.tokenStartLine() != startLine))
1562 invalidImport = true;
1566 if (invalidImport) {
1567 importError.setDescription(QCoreApplication::translate("QQmlParser","File import requires a qualifier"));
1568 importError.setColumn(l.tokenStartColumn());
1569 *error = importError;
1573 int endOffset = l.tokenLength() + l.tokenOffset();
1575 QString importId = script.mid(l.tokenOffset(), l.tokenLength());
1577 QQmlScript::LocationSpan location =
1578 locationFromLexer(l, startLine, startColumn, startOffset);
1582 if (!importId.at(0).isUpper() || (l.tokenStartLine() == startLine)) {
1583 importError.setDescription(QCoreApplication::translate("QQmlParser","Invalid import qualifier"));
1584 importError.setColumn(l.tokenStartColumn());
1585 *error = importError;
1589 replaceWithSpace(script, startOffset, endOffset - startOffset);
1592 import.type = Import::Script;
1594 import.qualifier = importId;
1595 import.location = location;
1597 rv.imports << import;
1603 if (!isUriToken(token)) {
1604 importError.setDescription(QCoreApplication::translate("QQmlParser","Invalid module URI"));
1605 importError.setColumn(l.tokenStartColumn());
1606 *error = importError;
1610 uri.append(l.tokenText());
1614 if (token != QQmlJSGrammar::T_DOT)
1617 uri.append(QLatin1Char('.'));
1623 if (token != QQmlJSGrammar::T_NUMERIC_LITERAL) {
1624 importError.setDescription(QCoreApplication::translate("QQmlParser","Module import requires a version"));
1625 importError.setColumn(l.tokenStartColumn());
1626 *error = importError;
1631 ProcessAST::extractVersion(QStringRef(&script, l.tokenOffset(), l.tokenLength()),
1634 bool invalidImport = false;
1638 if ((token != QQmlJSGrammar::T_AS) || (l.tokenStartLine() != startLine)) {
1639 invalidImport = true;
1643 if ((token != QQmlJSGrammar::T_IDENTIFIER) || (l.tokenStartLine() != startLine))
1644 invalidImport = true;
1648 if (invalidImport) {
1649 importError.setDescription(QCoreApplication::translate("QQmlParser","Module import requires a qualifier"));
1650 importError.setColumn(l.tokenStartColumn());
1651 *error = importError;
1655 int endOffset = l.tokenLength() + l.tokenOffset();
1657 QString importId = script.mid(l.tokenOffset(), l.tokenLength());
1659 QQmlScript::LocationSpan location =
1660 locationFromLexer(l, startLine, startColumn, startOffset);
1664 if (!importId.at(0).isUpper() || (l.tokenStartLine() == startLine)) {
1665 importError.setDescription(QCoreApplication::translate("QQmlParser","Invalid import qualifier"));
1666 importError.setColumn(l.tokenStartColumn());
1667 *error = importError;
1671 replaceWithSpace(script, startOffset, endOffset - startOffset);
1674 import.type = Import::Library;
1676 import.majorVersion = vmaj;
1677 import.minorVersion = vmin;
1678 import.qualifier = importId;
1679 import.location = location;
1681 rv.imports << import;
1684 } else if (token == QQmlJSGrammar::T_IDENTIFIER &&
1685 script.mid(l.tokenOffset(), l.tokenLength()) == pragma) {
1689 CHECK_TOKEN(T_IDENTIFIER);
1692 QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
1693 int endOffset = l.tokenLength() + l.tokenOffset();
1695 if (pragmaValue == library) {
1696 pragmas |= QQmlScript::Object::ScriptBlock::Shared;
1697 replaceWithSpace(script, startOffset, endOffset - startOffset);
1703 if (l.tokenStartLine() == startLine)
1713 void QQmlScript::Parser::clear()
1727 int QQmlScript::Parser::findOrCreateTypeId(const QString &name, Object *object)
1729 for (int ii = 0; ii < _refTypes.size(); ++ii) {
1730 if (_refTypes.at(ii)->name == name)
1734 TypeReference *type = _pool.New<TypeReference>();
1736 type->firstUse = object;
1737 _refTypes.append(type);
1738 return _refTypes.size() - 1;
1741 void QQmlScript::Parser::setTree(QQmlScript::Object *tree)