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 QtDeclarative 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 "qdeclarativescript_p.h"
44 #include "parser/qdeclarativejsengine_p.h"
45 #include "parser/qdeclarativejsparser_p.h"
46 #include "parser/qdeclarativejslexer_p.h"
47 #include "parser/qdeclarativejsmemorypool_p.h"
48 #include "parser/qdeclarativejsastvisitor_p.h"
49 #include "parser/qdeclarativejsast_p.h"
50 #include <private/qdeclarativerewrite_p.h>
53 #include <QCoreApplication>
58 using namespace QDeclarativeJS;
59 using namespace QDeclarativeScript;
64 QDeclarativeScript::Object::Object()
65 : type(-1), idIndex(-1), metatype(0), synthCache(0), defaultProperty(0), parserStatusCast(-1),
66 componentCompileState(0), nextAliasingObject(0), nextIdObject(0)
70 QDeclarativeScript::Object::~Object()
72 if (synthCache) synthCache->release();
75 void Object::setBindingBit(int b)
77 while (bindingBitmask.size() < 4 * (1 + b / 32))
78 bindingBitmask.append(char(0));
80 quint32 *bits = (quint32 *)bindingBitmask.data();
81 bits[b / 32] |= (1 << (b % 32));
84 const QMetaObject *Object::metaObject() const
86 if (!metadata.isEmpty() && metatype)
92 QDeclarativeScript::Property *Object::getDefaultProperty()
94 if (!defaultProperty) {
95 defaultProperty = pool()->New<Property>();
96 defaultProperty->parent = this;
98 return defaultProperty;
101 void QDeclarativeScript::Object::addValueProperty(Property *p)
103 valueProperties.append(p);
106 void QDeclarativeScript::Object::addSignalProperty(Property *p)
108 signalProperties.append(p);
111 void QDeclarativeScript::Object::addAttachedProperty(Property *p)
113 attachedProperties.append(p);
116 void QDeclarativeScript::Object::addGroupedProperty(Property *p)
118 groupedProperties.append(p);
121 void QDeclarativeScript::Object::addValueTypeProperty(Property *p)
123 valueTypeProperties.append(p);
126 void QDeclarativeScript::Object::addScriptStringProperty(Property *p)
128 scriptStringProperties.append(p);
131 // This lookup is optimized for missing, and having to create a new property.
132 Property *QDeclarativeScript::Object::getProperty(const QHashedStringRef &name, bool create)
135 quint32 h = name.hash();
136 if (propertiesHashField.testAndSet(h)) {
137 for (Property *p = properties.first(); p; p = properties.next(p)) {
138 if (p->name() == name)
143 Property *property = pool()->New<Property>();
144 property->parent = this;
145 property->_name = name;
146 property->isDefault = false;
147 properties.prepend(property);
150 for (Property *p = properties.first(); p; p = properties.next(p)) {
151 if (p->name() == name)
159 Property *QDeclarativeScript::Object::getProperty(const QStringRef &name, bool create)
161 return getProperty(QHashedStringRef(name), create);
164 Property *QDeclarativeScript::Object::getProperty(const QString &name, bool create)
166 for (Property *p = properties.first(); p; p = properties.next(p)) {
167 if (p->name() == name)
172 Property *property = pool()->New<Property>();
173 property->parent = this;
174 property->_name = QStringRef(pool()->NewString(name));
175 propertiesHashField.testAndSet(property->_name.hash());
176 property->isDefault = false;
177 properties.prepend(property);
184 QDeclarativeScript::Object::DynamicProperty::DynamicProperty()
185 : isDefaultProperty(false), isReadOnly(false), type(Variant), defaultValue(0), nextProperty(0),
186 resolvedCustomTypeName(0)
190 QDeclarativeScript::Object::DynamicSignal::DynamicSignal()
195 // Returns length in utf8 bytes
196 int QDeclarativeScript::Object::DynamicSignal::parameterTypesLength() const
199 for (int ii = 0; ii < parameterTypes.count(); ++ii)
200 rv += parameterTypes.at(ii).length();
204 // Returns length in utf8 bytes
205 int QDeclarativeScript::Object::DynamicSignal::parameterNamesLength() const
208 for (int ii = 0; ii < parameterNames.count(); ++ii)
209 rv += parameterNames.at(ii).utf8length();
213 QDeclarativeScript::Object::DynamicSlot::DynamicSlot()
218 int QDeclarativeScript::Object::DynamicSlot::parameterNamesLength() const
221 for (int ii = 0; ii < parameterNames.count(); ++ii)
222 rv += parameterNames.at(ii).length();
226 QDeclarativeScript::Property::Property()
227 : parent(0), type(0), index(-1), value(0), isDefault(true), isDeferred(false),
228 isValueTypeSubProperty(false), isAlias(false), isReadOnlyDeclaration(false),
229 scriptStringScope(-1), nextMainProperty(0), nextProperty(0)
233 QDeclarativeScript::Object *QDeclarativeScript::Property::getValue(const LocationSpan &l)
235 if (!value) { value = pool()->New<Object>(); value->location = l; }
239 void QDeclarativeScript::Property::addValue(Value *v)
244 void QDeclarativeScript::Property::addOnValue(Value *v)
249 bool QDeclarativeScript::Property::isEmpty() const
251 return !value && values.isEmpty() && onValues.isEmpty();
254 QDeclarativeScript::Value::Value()
255 : type(Unknown), object(0), bindingReference(0), nextValue(0)
259 QDeclarativeScript::Variant::Variant()
264 QDeclarativeScript::Variant::Variant(const Variant &o)
265 : t(o.t), d(o.d), asWritten(o.asWritten)
269 QDeclarativeScript::Variant::Variant(bool v)
274 QDeclarativeScript::Variant::Variant(double v, const QStringRef &asWritten)
275 : t(Number), d(v), asWritten(asWritten)
279 QDeclarativeScript::Variant::Variant(QDeclarativeJS::AST::StringLiteral *v)
284 QDeclarativeScript::Variant::Variant(const QStringRef &asWritten, QDeclarativeJS::AST::Node *n)
285 : t(Script), n(n), asWritten(asWritten)
289 QDeclarativeScript::Variant &QDeclarativeScript::Variant::operator=(const Variant &o)
293 asWritten = o.asWritten;
297 QDeclarativeScript::Variant::Type QDeclarativeScript::Variant::type() const
302 bool QDeclarativeScript::Variant::asBoolean() const
307 QString QDeclarativeScript::Variant::asString() const
310 return l->value.toString();
312 return asWritten.toString();
316 double QDeclarativeScript::Variant::asNumber() const
321 //reverse of Lexer::singleEscape()
322 QString escapedString(const QString &string)
324 QString tmp = QLatin1String("\"");
325 for (int i = 0; i < string.length(); ++i) {
326 const QChar &c = string.at(i);
327 switch(c.unicode()) {
329 tmp += QLatin1String("\\b");
332 tmp += QLatin1String("\\t");
335 tmp += QLatin1String("\\n");
338 tmp += QLatin1String("\\v");
341 tmp += QLatin1String("\\f");
344 tmp += QLatin1String("\\r");
347 tmp += QLatin1String("\\\"");
350 tmp += QLatin1String("\\\'");
353 tmp += QLatin1String("\\\\");
360 tmp += QLatin1Char('\"');
364 QString QDeclarativeScript::Variant::asScript() const
371 return b?QLatin1String("true"):QLatin1String("false");
373 if (asWritten.isEmpty())
374 return QString::number(d);
376 return asWritten.toString();
378 return escapedString(asString());
380 if (AST::IdentifierExpression *i = AST::cast<AST::IdentifierExpression *>(n)) {
381 return i->name.toString();
383 return asWritten.toString();
387 QDeclarativeJS::AST::Node *QDeclarativeScript::Variant::asAST() const
389 if (type() == Script)
395 bool QDeclarativeScript::Variant::isStringList() const
400 if (type() != Script || !n)
403 AST::ArrayLiteral *array = AST::cast<AST::ArrayLiteral *>(n);
407 AST::ElementList *elements = array->elements;
411 if (!AST::cast<AST::StringLiteral *>(elements->expression))
414 elements = elements->next;
420 QStringList QDeclarativeScript::Variant::asStringList() const
428 AST::ArrayLiteral *array = AST::cast<AST::ArrayLiteral *>(n);
432 AST::ElementList *elements = array->elements;
435 AST::StringLiteral *string = AST::cast<AST::StringLiteral *>(elements->expression);
437 return QStringList();
438 rv.append(string->value.toString());
440 elements = elements->next;
447 // Actual parser classes
449 void QDeclarativeScript::Import::extractVersion(int *maj, int *min) const
451 *maj = -1; *min = -1;
453 if (!version.isEmpty()) {
454 int dot = version.indexOf(QLatin1Char('.'));
456 *maj = version.toInt();
459 *maj = version.left(dot).toInt();
460 *min = version.mid(dot+1).toInt();
467 class ProcessAST: protected AST::Visitor
470 State() : object(0), property(0) {}
471 State(QDeclarativeScript::Object *o) : object(o), property(0) {}
472 State(QDeclarativeScript::Object *o, Property *p) : object(o), property(p) {}
474 QDeclarativeScript::Object *object;
478 struct StateStack : public QStack<State>
480 void pushObject(QDeclarativeScript::Object *obj)
485 void pushProperty(const QString &name, const LocationSpan &location)
487 const State &state = top();
488 if (state.property) {
489 State s(state.property->getValue(location),
490 state.property->getValue(location)->getProperty(name));
491 s.property->location = location;
494 State s(state.object, state.object->getProperty(name));
496 s.property->location = location;
501 void pushProperty(const QStringRef &name, const LocationSpan &location)
503 const State &state = top();
504 if (state.property) {
505 State s(state.property->getValue(location),
506 state.property->getValue(location)->getProperty(name));
507 s.property->location = location;
510 State s(state.object, state.object->getProperty(name));
512 s.property->location = location;
519 ProcessAST(QDeclarativeScript::Parser *parser);
520 virtual ~ProcessAST();
522 void operator()(const QString &code, AST::Node *node);
526 QDeclarativeScript::Object *defineObjectBinding(AST::UiQualifiedId *propertyName, bool onAssignment,
527 const QString &objectType,
528 AST::SourceLocation typeLocation,
529 LocationSpan location,
530 AST::UiObjectInitializer *initializer = 0);
532 QDeclarativeScript::Variant getVariant(AST::Statement *stmt);
533 QDeclarativeScript::Variant getVariant(AST::ExpressionNode *expr);
535 LocationSpan location(AST::SourceLocation start, AST::SourceLocation end);
536 LocationSpan location(AST::UiQualifiedId *);
538 using AST::Visitor::visit;
539 using AST::Visitor::endVisit;
541 virtual bool visit(AST::UiProgram *node);
542 virtual bool visit(AST::UiImport *node);
543 virtual bool visit(AST::UiObjectDefinition *node);
544 virtual bool visit(AST::UiPublicMember *node);
545 virtual bool visit(AST::UiObjectBinding *node);
547 virtual bool visit(AST::UiScriptBinding *node);
548 virtual bool visit(AST::UiArrayBinding *node);
549 virtual bool visit(AST::UiSourceElement *node);
551 void accept(AST::Node *node);
553 QString asString(AST::UiQualifiedId *node) const;
555 const State state() const;
556 QDeclarativeScript::Object *currentObject() const;
557 Property *currentProperty() const;
559 QString qualifiedNameId() const;
561 QString textAt(const AST::SourceLocation &loc) const
562 { return _contents->mid(loc.offset, loc.length); }
564 QStringRef textRefAt(const AST::SourceLocation &loc) const
565 { return QStringRef(_contents, loc.offset, loc.length); }
567 QString textAt(const AST::SourceLocation &first,
568 const AST::SourceLocation &last) const
569 { return _contents->mid(first.offset, last.offset + last.length - first.offset); }
571 QStringRef textRefAt(const AST::SourceLocation &first,
572 const AST::SourceLocation &last) const
573 { return QStringRef(_contents, first.offset, last.offset + last.length - first.offset); }
575 QString asString(AST::ExpressionNode *expr)
580 return textAt(expr->firstSourceLocation(), expr->lastSourceLocation());
583 QStringRef asStringRef(AST::ExpressionNode *expr)
588 return textRefAt(expr->firstSourceLocation(), expr->lastSourceLocation());
591 QString asString(AST::Statement *stmt)
596 QString s = textAt(stmt->firstSourceLocation(), stmt->lastSourceLocation());
597 s += QLatin1Char('\n');
601 QStringRef asStringRef(AST::Statement *stmt)
606 return textRefAt(stmt->firstSourceLocation(), stmt->lastSourceLocation());
610 QDeclarativeScript::Parser *_parser;
611 StateStack _stateStack;
613 const QString *_contents;
616 ProcessAST::ProcessAST(QDeclarativeScript::Parser *parser)
621 ProcessAST::~ProcessAST()
625 void ProcessAST::operator()(const QString &code, AST::Node *node)
631 void ProcessAST::accept(AST::Node *node)
633 AST::Node::acceptChild(node, this);
636 const ProcessAST::State ProcessAST::state() const
638 if (_stateStack.isEmpty())
641 return _stateStack.back();
644 QDeclarativeScript::Object *ProcessAST::currentObject() const
646 return state().object;
649 Property *ProcessAST::currentProperty() const
651 return state().property;
654 QString ProcessAST::qualifiedNameId() const
656 return _scope.join(QLatin1String("/"));
659 QString ProcessAST::asString(AST::UiQualifiedId *node) const
663 for (AST::UiQualifiedId *it = node; it; it = it->next) {
664 s.append(it->name.toString());
667 s.append(QLatin1Char('.'));
673 QDeclarativeScript::Object *
674 ProcessAST::defineObjectBinding(AST::UiQualifiedId *propertyName,
676 const QString &objectType,
677 AST::SourceLocation typeLocation,
678 LocationSpan location,
679 AST::UiObjectInitializer *initializer)
681 int lastTypeDot = objectType.lastIndexOf(QLatin1Char('.'));
683 // With no preceding qualification, first char is at (-1 + 1) == 0
684 bool isType = !objectType.isEmpty() && objectType.at(lastTypeDot+1).isUpper();
686 int propertyCount = 0;
687 for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
689 _stateStack.pushProperty(name->name,
690 this->location(name));
693 if (!onAssignment && propertyCount && currentProperty() && !currentProperty()->values.isEmpty()) {
694 QDeclarativeError error;
695 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Property value set multiple times"));
696 error.setLine(this->location(propertyName).start.line);
697 error.setColumn(this->location(propertyName).start.column);
698 _parser->_errors << error;
704 // Is the identifier qualified by a namespace?
705 int namespaceLength = 0;
706 if (lastTypeDot > 0) {
707 const QString qualifier(objectType.left(lastTypeDot));
709 for (int ii = 0; ii < _parser->_imports.count(); ++ii) {
710 const QDeclarativeScript::Import &import = _parser->_imports.at(ii);
711 if (import.qualifier == qualifier) {
712 // The qualifier is a namespace - expect a type here
713 namespaceLength = qualifier.length() + 1;
719 if (propertyCount || !currentObject() || namespaceLength) {
720 QDeclarativeError error;
721 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Expected type name"));
722 error.setLine(typeLocation.startLine);
723 error.setColumn(typeLocation.startColumn + namespaceLength);
724 _parser->_errors << error;
728 LocationSpan loc = ProcessAST::location(typeLocation, typeLocation);
730 loc = ProcessAST::location(propertyName);
732 _stateStack.pushProperty(objectType, loc);
741 QString resolvableObjectType = objectType;
742 if (lastTypeDot >= 0)
743 resolvableObjectType.replace(QLatin1Char('.'),QLatin1Char('/'));
745 QDeclarativeScript::Object *obj = _parser->_pool.New<QDeclarativeScript::Object>();
747 QDeclarativeScript::TypeReference *typeRef = _parser->findOrCreateType(resolvableObjectType);
748 obj->type = typeRef->id;
750 typeRef->refObjects.append(obj);
752 // XXX this doesn't do anything (_scope never builds up)
753 _scope.append(resolvableObjectType);
754 obj->typeName = qualifiedNameId();
757 obj->location = location;
760 Property *prop = currentProperty();
761 QDeclarativeScript::Value *v = _parser->_pool.New<QDeclarativeScript::Value>();
763 v->location = obj->location;
769 while (propertyCount--)
774 if (! _parser->tree()) {
775 _parser->setTree(obj);
777 const State state = _stateStack.top();
778 QDeclarativeScript::Value *v = _parser->_pool.New<QDeclarativeScript::Value>();
780 v->location = obj->location;
781 if (state.property) {
782 state.property->addValue(v);
784 Property *defaultProp = state.object->getDefaultProperty();
785 if (defaultProp->location.start.line == -1) {
786 defaultProp->location = v->location;
787 defaultProp->location.end = defaultProp->location.start;
788 defaultProp->location.range.length = 0;
790 defaultProp->addValue(v);
795 _stateStack.pushObject(obj);
803 LocationSpan ProcessAST::location(AST::UiQualifiedId *id)
805 return location(id->identifierToken, id->identifierToken);
808 LocationSpan ProcessAST::location(AST::SourceLocation start, AST::SourceLocation end)
811 rv.start.line = start.startLine;
812 rv.start.column = start.startColumn;
813 rv.end.line = end.startLine;
814 rv.end.column = end.startColumn + end.length - 1;
815 rv.range.offset = start.offset;
816 rv.range.length = end.offset + end.length - start.offset;
820 // UiProgram: UiImportListOpt UiObjectMemberList ;
821 bool ProcessAST::visit(AST::UiProgram *node)
823 accept(node->imports);
824 accept(node->members->member);
828 // UiImport: T_IMPORT T_STRING_LITERAL ;
829 bool ProcessAST::visit(AST::UiImport *node)
832 QDeclarativeScript::Import import;
834 if (!node->fileName.isNull()) {
835 uri = node->fileName.toString();
837 if (uri.endsWith(QLatin1String(".js"))) {
838 import.type = QDeclarativeScript::Import::Script;
840 import.type = QDeclarativeScript::Import::File;
843 import.type = QDeclarativeScript::Import::Library;
844 uri = asString(node->importUri);
847 AST::SourceLocation startLoc = node->importToken;
848 AST::SourceLocation endLoc = node->semicolonToken;
851 if (!node->importId.isNull()) {
852 import.qualifier = node->importId.toString();
853 if (!import.qualifier.at(0).isUpper()) {
854 QDeclarativeError error;
855 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Invalid import qualifier ID"));
856 error.setLine(node->importIdToken.startLine);
857 error.setColumn(node->importIdToken.startColumn);
858 _parser->_errors << error;
861 if (import.qualifier == QLatin1String("Qt")) {
862 QDeclarativeError error;
863 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Reserved name \"Qt\" cannot be used as an qualifier"));
864 error.setLine(node->importIdToken.startLine);
865 error.setColumn(node->importIdToken.startColumn);
866 _parser->_errors << error;
870 // Check for script qualifier clashes
871 bool isScript = import.type == QDeclarativeScript::Import::Script;
872 for (int ii = 0; ii < _parser->_imports.count(); ++ii) {
873 const QDeclarativeScript::Import &other = _parser->_imports.at(ii);
874 bool otherIsScript = other.type == QDeclarativeScript::Import::Script;
876 if ((isScript || otherIsScript) && import.qualifier == other.qualifier) {
877 QDeclarativeError error;
878 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Script import qualifiers must be unique."));
879 error.setLine(node->importIdToken.startLine);
880 error.setColumn(node->importIdToken.startColumn);
881 _parser->_errors << error;
886 } else if (import.type == QDeclarativeScript::Import::Script) {
887 QDeclarativeError error;
888 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Script import requires a qualifier"));
889 error.setLine(node->fileNameToken.startLine);
890 error.setColumn(node->fileNameToken.startColumn);
891 _parser->_errors << error;
895 if (node->versionToken.isValid()) {
896 import.version = textAt(node->versionToken);
897 } else if (import.type == QDeclarativeScript::Import::Library) {
898 QDeclarativeError error;
899 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Library import requires a version"));
900 error.setLine(node->importIdToken.startLine);
901 error.setColumn(node->importIdToken.startColumn);
902 _parser->_errors << error;
907 import.location = location(startLoc, endLoc);
910 _parser->_imports << import;
915 bool ProcessAST::visit(AST::UiPublicMember *node)
917 static const struct TypeNameToType {
920 Object::DynamicProperty::Type type;
923 } propTypeNameToTypes[] = {
924 { "int", strlen("int"), Object::DynamicProperty::Int, "int", strlen("int") },
925 { "bool", strlen("bool"), Object::DynamicProperty::Bool, "bool", strlen("bool") },
926 { "double", strlen("double"), Object::DynamicProperty::Real, "double", strlen("double") },
927 { "real", strlen("real"), Object::DynamicProperty::Real, "qreal", strlen("qreal") },
928 { "string", strlen("string"), Object::DynamicProperty::String, "QString", strlen("QString") },
929 { "url", strlen("url"), Object::DynamicProperty::Url, "QUrl", strlen("QUrl") },
930 { "color", strlen("color"), Object::DynamicProperty::Color, "QColor", strlen("QColor") },
931 // Internally QTime, QDate and QDateTime are all supported.
932 // To be more consistent with JavaScript we expose only
933 // QDateTime as it matches closely with the Date JS type.
934 // We also call it "date" to match.
935 // { "time", strlen("time"), Object::DynamicProperty::Time, "QTime", strlen("QTime") },
936 // { "date", strlen("date"), Object::DynamicProperty::Date, "QDate", strlen("QDate") },
937 { "date", strlen("date"), Object::DynamicProperty::DateTime, "QDateTime", strlen("QDateTime") },
938 { "variant", strlen("variant"), Object::DynamicProperty::Variant, "QVariant", strlen("QVariant") },
939 { "var", strlen("var"), Object::DynamicProperty::Var, "QVariant", strlen("QVariant") }
941 static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) /
942 sizeof(propTypeNameToTypes[0]);
944 if(node->type == AST::UiPublicMember::Signal) {
945 Object::DynamicSignal *signal = _parser->_pool.New<Object::DynamicSignal>();
946 signal->name = node->name;
948 AST::UiParameterList *p = node->parameters;
950 while (p) { paramLength++; p = p->next; }
951 p = node->parameters;
954 signal->parameterTypes = _parser->_pool.NewRawList<QHashedCStringRef>(paramLength);
955 signal->parameterNames = _parser->_pool.NewRawList<QHashedStringRef>(paramLength);
960 const QStringRef &memberType = p->type;
962 const TypeNameToType *type = 0;
963 for(int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) {
964 const TypeNameToType *t = propTypeNameToTypes + typeIndex;
965 if (t->nameLength == memberType.length() &&
966 QHashedString::compare(memberType.constData(), t->name, t->nameLength)) {
973 QDeclarativeError error;
974 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Expected parameter type"));
975 error.setLine(node->typeToken.startLine);
976 error.setColumn(node->typeToken.startColumn);
977 _parser->_errors << error;
981 signal->parameterTypes[index] = QHashedCStringRef(type->qtName, type->qtNameLength);
982 signal->parameterNames[index] = QHashedStringRef(p->name);
987 signal->location = location(node->typeToken, node->semicolonToken);
988 _stateStack.top().object->dynamicSignals.append(signal);
990 const QStringRef &memberType = node->memberType;
991 const QStringRef &name = node->name;
993 bool typeFound = false;
994 Object::DynamicProperty::Type type;
996 if ((unsigned)memberType.length() == strlen("alias") &&
997 QHashedString::compare(memberType.constData(), "alias", strlen("alias"))) {
998 type = Object::DynamicProperty::Alias;
1002 for(int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) {
1003 const TypeNameToType *t = propTypeNameToTypes + ii;
1004 if (t->nameLength == memberType.length() &&
1005 QHashedString::compare(memberType.constData(), t->name, t->nameLength)) {
1011 if (!typeFound && memberType.at(0).isUpper()) {
1012 const QStringRef &typeModifier = node->typeModifier;
1014 if (typeModifier.isEmpty()) {
1015 type = Object::DynamicProperty::Custom;
1016 } else if((unsigned)typeModifier.length() == strlen("list") &&
1017 QHashedString::compare(typeModifier.constData(), "list", strlen("list"))) {
1018 type = Object::DynamicProperty::CustomList;
1020 QDeclarativeError error;
1021 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Invalid property type modifier"));
1022 error.setLine(node->typeModifierToken.startLine);
1023 error.setColumn(node->typeModifierToken.startColumn);
1024 _parser->_errors << error;
1028 } else if (!node->typeModifier.isNull()) {
1029 QDeclarativeError error;
1030 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Unexpected property type modifier"));
1031 error.setLine(node->typeModifierToken.startLine);
1032 error.setColumn(node->typeModifierToken.startColumn);
1033 _parser->_errors << error;
1038 QDeclarativeError error;
1039 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Expected property type"));
1040 error.setLine(node->typeToken.startLine);
1041 error.setColumn(node->typeToken.startColumn);
1042 _parser->_errors << error;
1046 Object::DynamicProperty *property = _parser->_pool.New<Object::DynamicProperty>();
1047 property->isDefaultProperty = node->isDefaultMember;
1048 property->isReadOnly = node->isReadonlyMember;
1049 property->type = type;
1050 property->nameLocation.line = node->identifierToken.startLine;
1051 property->nameLocation.column = node->identifierToken.startColumn;
1052 if (type >= Object::DynamicProperty::Custom) {
1053 QDeclarativeScript::TypeReference *typeRef =
1054 _parser->findOrCreateType(memberType.toString());
1055 typeRef->refObjects.append(_stateStack.top().object);
1056 property->customType = memberType;
1059 property->name = QHashedStringRef(name);
1060 property->location = location(node->firstSourceLocation(),
1061 node->lastSourceLocation());
1063 if (node->statement) { // default value
1064 property->defaultValue = _parser->_pool.New<Property>();
1065 property->defaultValue->parent = _stateStack.top().object;
1066 property->defaultValue->location =
1067 location(node->statement->firstSourceLocation(),
1068 node->statement->lastSourceLocation());
1069 QDeclarativeScript::Value *value = _parser->_pool.New<QDeclarativeScript::Value>();
1070 value->location = location(node->statement->firstSourceLocation(),
1071 node->statement->lastSourceLocation());
1072 value->value = getVariant(node->statement);
1073 property->defaultValue->values.append(value);
1076 _stateStack.top().object->dynamicProperties.append(property);
1078 // process QML-like initializers (e.g. property Object o: Object {})
1079 accept(node->binding);
1086 // UiObjectMember: UiQualifiedId UiObjectInitializer ;
1087 bool ProcessAST::visit(AST::UiObjectDefinition *node)
1089 LocationSpan l = location(node->firstSourceLocation(),
1090 node->lastSourceLocation());
1092 const QString objectType = asString(node->qualifiedTypeNameId);
1093 const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken;
1095 defineObjectBinding(/*propertyName = */ 0, false, objectType,
1096 typeLocation, l, node->initializer);
1102 // UiObjectMember: UiQualifiedId T_COLON UiQualifiedId UiObjectInitializer ;
1103 bool ProcessAST::visit(AST::UiObjectBinding *node)
1105 LocationSpan l = location(node->qualifiedTypeNameId->identifierToken,
1106 node->initializer->rbraceToken);
1108 const QString objectType = asString(node->qualifiedTypeNameId);
1109 const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken;
1111 defineObjectBinding(node->qualifiedId, node->hasOnToken, objectType,
1112 typeLocation, l, node->initializer);
1117 QDeclarativeScript::Variant ProcessAST::getVariant(AST::Statement *stmt)
1120 if (AST::ExpressionStatement *exprStmt = AST::cast<AST::ExpressionStatement *>(stmt))
1121 return getVariant(exprStmt->expression);
1123 return QDeclarativeScript::Variant(asStringRef(stmt), stmt);
1126 return QDeclarativeScript::Variant();
1129 QDeclarativeScript::Variant ProcessAST::getVariant(AST::ExpressionNode *expr)
1131 if (AST::StringLiteral *lit = AST::cast<AST::StringLiteral *>(expr)) {
1132 return QDeclarativeScript::Variant(lit);
1133 } else if (expr->kind == AST::Node::Kind_TrueLiteral) {
1134 return QDeclarativeScript::Variant(true);
1135 } else if (expr->kind == AST::Node::Kind_FalseLiteral) {
1136 return QDeclarativeScript::Variant(false);
1137 } else if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(expr)) {
1138 return QDeclarativeScript::Variant(lit->value, asStringRef(expr));
1141 if (AST::UnaryMinusExpression *unaryMinus = AST::cast<AST::UnaryMinusExpression *>(expr)) {
1142 if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(unaryMinus->expression)) {
1143 return QDeclarativeScript::Variant(-lit->value, asStringRef(expr));
1147 return QDeclarativeScript::Variant(asStringRef(expr), expr);
1152 // UiObjectMember: UiQualifiedId T_COLON Statement ;
1153 bool ProcessAST::visit(AST::UiScriptBinding *node)
1155 int propertyCount = 0;
1156 AST::UiQualifiedId *propertyName = node->qualifiedId;
1157 for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
1159 _stateStack.pushProperty(name->name,
1163 Property *prop = currentProperty();
1165 if (!prop->values.isEmpty()) {
1166 QDeclarativeError error;
1167 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Property value set multiple times"));
1168 error.setLine(this->location(propertyName).start.line);
1169 error.setColumn(this->location(propertyName).start.column);
1170 _parser->_errors << error;
1174 QDeclarativeScript::Variant primitive;
1176 if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(node->statement)) {
1177 primitive = getVariant(stmt->expression);
1178 } else { // do binding
1179 primitive = QDeclarativeScript::Variant(asStringRef(node->statement), node->statement);
1182 prop->location.range.length = prop->location.range.offset + prop->location.range.length - node->qualifiedId->identifierToken.offset;
1183 prop->location.range.offset = node->qualifiedId->identifierToken.offset;
1184 QDeclarativeScript::Value *v = _parser->_pool.New<QDeclarativeScript::Value>();
1185 v->value = primitive;
1186 v->location = location(node->statement->firstSourceLocation(),
1187 node->statement->lastSourceLocation());
1191 while (propertyCount--)
1197 // UiObjectMember: UiQualifiedId T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ;
1198 bool ProcessAST::visit(AST::UiArrayBinding *node)
1200 int propertyCount = 0;
1201 AST::UiQualifiedId *propertyName = node->qualifiedId;
1202 for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
1204 _stateStack.pushProperty(name->name,
1208 Property* prop = currentProperty();
1210 if (!prop->values.isEmpty()) {
1211 QDeclarativeError error;
1212 error.setDescription(QCoreApplication::translate("QDeclarativeParser","Property value set multiple times"));
1213 error.setLine(this->location(propertyName).start.line);
1214 error.setColumn(this->location(propertyName).start.column);
1215 _parser->_errors << error;
1219 accept(node->members);
1221 // For the DOM, store the position of the T_LBRACKET upto the T_RBRACKET as the range:
1222 prop->listValueRange.offset = node->lbracketToken.offset;
1223 prop->listValueRange.length = node->rbracketToken.offset + node->rbracketToken.length - node->lbracketToken.offset;
1225 while (propertyCount--)
1231 bool ProcessAST::visit(AST::UiSourceElement *node)
1233 QDeclarativeScript::Object *obj = currentObject();
1235 if (AST::FunctionDeclaration *funDecl = AST::cast<AST::FunctionDeclaration *>(node->sourceElement)) {
1237 Object::DynamicSlot *slot = _parser->_pool.New<Object::DynamicSlot>();
1238 slot->location = location(funDecl->identifierToken, funDecl->lastSourceLocation());
1240 AST::FormalParameterList *f = funDecl->formals;
1242 slot->parameterNames << f->name.toUtf8();
1246 AST::SourceLocation loc = funDecl->rparenToken;
1247 loc.offset = loc.end();
1248 loc.startColumn += 1;
1249 QString body = textAt(loc, funDecl->rbraceToken);
1250 slot->name = funDecl->name;
1252 obj->dynamicSlots.append(slot);
1255 QDeclarativeError error;
1256 error.setDescription(QCoreApplication::translate("QDeclarativeParser","JavaScript declaration outside Script element"));
1257 error.setLine(node->firstSourceLocation().startLine);
1258 error.setColumn(node->firstSourceLocation().startColumn);
1259 _parser->_errors << error;
1264 } // end of anonymous namespace
1267 QDeclarativeScript::Parser::Parser()
1273 QDeclarativeScript::Parser::~Parser()
1278 namespace QDeclarativeScript {
1279 class ParserJsASTData
1282 ParserJsASTData(const QString &filename)
1283 : filename(filename) {}
1290 bool QDeclarativeScript::Parser::parse(const QByteArray &qmldata, const QUrl &url)
1294 const QString fileName = url.toString();
1295 _scriptFile = fileName;
1297 QTextStream stream(qmldata, QIODevice::ReadOnly);
1298 #ifndef QT_NO_TEXTCODEC
1299 stream.setCodec("UTF-8");
1301 QString *code = _pool.NewString(stream.readAll());
1303 data = new QDeclarativeScript::ParserJsASTData(fileName);
1305 Lexer lexer(&data->engine);
1306 lexer.setCode(*code, /*line = */ 1);
1308 QDeclarativeJS::Parser parser(&data->engine);
1310 if (! parser.parse() || !_errors.isEmpty()) {
1312 // Extract errors from the parser
1313 foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) {
1318 QDeclarativeError error;
1320 error.setDescription(m.message);
1321 error.setLine(m.loc.startLine);
1322 error.setColumn(m.loc.startColumn);
1328 if (_errors.isEmpty()) {
1329 ProcessAST process(this);
1330 process(*code, parser.ast());
1332 // Set the url for process errors
1333 for(int ii = 0; ii < _errors.count(); ++ii)
1334 _errors[ii].setUrl(url);
1337 return _errors.isEmpty();
1340 QList<QDeclarativeScript::TypeReference*> QDeclarativeScript::Parser::referencedTypes() const
1345 QDeclarativeScript::Object *QDeclarativeScript::Parser::tree() const
1350 QList<QDeclarativeScript::Import> QDeclarativeScript::Parser::imports() const
1355 QList<QDeclarativeError> QDeclarativeScript::Parser::errors() const
1360 static void replaceWithSpace(QString &str, int idx, int n)
1362 QChar *data = str.data() + idx;
1363 const QChar space(QLatin1Char(' '));
1364 for (int ii = 0; ii < n; ++ii)
1368 static QDeclarativeScript::LocationSpan
1369 locationFromLexer(const QDeclarativeJS::Lexer &lex, int startLine, int startColumn, int startOffset)
1371 QDeclarativeScript::LocationSpan l;
1373 l.start.line = startLine; l.start.column = startColumn;
1374 l.end.line = lex.tokenEndLine(); l.end.column = lex.tokenEndColumn();
1375 l.range.offset = startOffset;
1376 l.range.length = lex.tokenOffset() + lex.tokenLength() - startOffset;
1382 Searches for ".pragma <value>" declarations within \a script. Currently supported pragmas
1386 QDeclarativeScript::Object::ScriptBlock::Pragmas QDeclarativeScript::Parser::extractPragmas(QString &script)
1388 QDeclarativeScript::Object::ScriptBlock::Pragmas rv = QDeclarativeScript::Object::ScriptBlock::None;
1390 const QString pragma(QLatin1String("pragma"));
1391 const QString library(QLatin1String("library"));
1393 QDeclarativeJS::Lexer l(0);
1394 l.setCode(script, 0);
1396 int token = l.lex();
1399 if (token != QDeclarativeJSGrammar::T_DOT)
1402 int startOffset = l.tokenOffset();
1403 int startLine = l.tokenStartLine();
1407 if (token != QDeclarativeJSGrammar::T_IDENTIFIER ||
1408 l.tokenStartLine() != startLine ||
1409 script.mid(l.tokenOffset(), l.tokenLength()) != pragma)
1414 if (token != QDeclarativeJSGrammar::T_IDENTIFIER ||
1415 l.tokenStartLine() != startLine)
1418 QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
1419 int endOffset = l.tokenLength() + l.tokenOffset();
1422 if (l.tokenStartLine() == startLine)
1425 if (pragmaValue == library) {
1426 rv |= QDeclarativeScript::Object::ScriptBlock::Shared;
1427 replaceWithSpace(script, startOffset, endOffset - startOffset);
1435 #define CHECK_LINE if (l.tokenStartLine() != startLine) return rv;
1436 #define CHECK_TOKEN(t) if (token != QDeclarativeJSGrammar:: t) return rv;
1438 static const int uriTokens[] = {
1439 QDeclarativeJSGrammar::T_IDENTIFIER,
1440 QDeclarativeJSGrammar::T_PROPERTY,
1441 QDeclarativeJSGrammar::T_SIGNAL,
1442 QDeclarativeJSGrammar::T_READONLY,
1443 QDeclarativeJSGrammar::T_ON,
1444 QDeclarativeJSGrammar::T_BREAK,
1445 QDeclarativeJSGrammar::T_CASE,
1446 QDeclarativeJSGrammar::T_CATCH,
1447 QDeclarativeJSGrammar::T_CONTINUE,
1448 QDeclarativeJSGrammar::T_DEFAULT,
1449 QDeclarativeJSGrammar::T_DELETE,
1450 QDeclarativeJSGrammar::T_DO,
1451 QDeclarativeJSGrammar::T_ELSE,
1452 QDeclarativeJSGrammar::T_FALSE,
1453 QDeclarativeJSGrammar::T_FINALLY,
1454 QDeclarativeJSGrammar::T_FOR,
1455 QDeclarativeJSGrammar::T_FUNCTION,
1456 QDeclarativeJSGrammar::T_IF,
1457 QDeclarativeJSGrammar::T_IN,
1458 QDeclarativeJSGrammar::T_INSTANCEOF,
1459 QDeclarativeJSGrammar::T_NEW,
1460 QDeclarativeJSGrammar::T_NULL,
1461 QDeclarativeJSGrammar::T_RETURN,
1462 QDeclarativeJSGrammar::T_SWITCH,
1463 QDeclarativeJSGrammar::T_THIS,
1464 QDeclarativeJSGrammar::T_THROW,
1465 QDeclarativeJSGrammar::T_TRUE,
1466 QDeclarativeJSGrammar::T_TRY,
1467 QDeclarativeJSGrammar::T_TYPEOF,
1468 QDeclarativeJSGrammar::T_VAR,
1469 QDeclarativeJSGrammar::T_VOID,
1470 QDeclarativeJSGrammar::T_WHILE,
1471 QDeclarativeJSGrammar::T_CONST,
1472 QDeclarativeJSGrammar::T_DEBUGGER,
1473 QDeclarativeJSGrammar::T_RESERVED_WORD,
1474 QDeclarativeJSGrammar::T_WITH,
1476 QDeclarativeJSGrammar::EOF_SYMBOL
1478 static inline bool isUriToken(int token)
1480 const int *current = uriTokens;
1481 while (*current != QDeclarativeJSGrammar::EOF_SYMBOL) {
1482 if (*current == token)
1489 QDeclarativeScript::Parser::JavaScriptMetaData QDeclarativeScript::Parser::extractMetaData(QString &script)
1491 JavaScriptMetaData rv;
1493 QDeclarativeScript::Object::ScriptBlock::Pragmas &pragmas = rv.pragmas;
1495 const QString pragma(QLatin1String("pragma"));
1496 const QString js(QLatin1String(".js"));
1497 const QString library(QLatin1String("library"));
1499 QDeclarativeJS::Lexer l(0);
1500 l.setCode(script, 0);
1502 int token = l.lex();
1505 if (token != QDeclarativeJSGrammar::T_DOT)
1508 int startOffset = l.tokenOffset();
1509 int startLine = l.tokenStartLine();
1510 int startColumn = l.tokenStartColumn();
1516 if (token == QDeclarativeJSGrammar::T_IMPORT) {
1518 // .import <URI> <Version> as <Identifier>
1519 // .import <file.js> as <Identifier>
1525 if (token == QDeclarativeJSGrammar::T_STRING_LITERAL) {
1527 QString file = l.tokenText();
1529 if (!file.endsWith(js))
1539 CHECK_TOKEN(T_IDENTIFIER);
1542 int endOffset = l.tokenLength() + l.tokenOffset();
1544 QString importId = script.mid(l.tokenOffset(), l.tokenLength());
1546 if (!importId.at(0).isUpper())
1549 QDeclarativeScript::LocationSpan location =
1550 locationFromLexer(l, startLine, startColumn, startOffset);
1553 if (l.tokenStartLine() == startLine)
1556 replaceWithSpace(script, startOffset, endOffset - startOffset);
1559 import.type = Import::Script;
1561 import.qualifier = importId;
1562 import.location = location;
1564 rv.imports << import;
1571 if (!isUriToken(token))
1574 uri.append(l.tokenText());
1578 if (token != QDeclarativeJSGrammar::T_DOT)
1581 uri.append(QLatin1Char('.'));
1587 CHECK_TOKEN(T_NUMERIC_LITERAL);
1588 version = script.mid(l.tokenOffset(), l.tokenLength());
1597 CHECK_TOKEN(T_IDENTIFIER);
1600 int endOffset = l.tokenLength() + l.tokenOffset();
1602 QString importId = script.mid(l.tokenOffset(), l.tokenLength());
1604 if (!importId.at(0).isUpper())
1607 QDeclarativeScript::LocationSpan location =
1608 locationFromLexer(l, startLine, startColumn, startOffset);
1611 if (l.tokenStartLine() == startLine)
1614 replaceWithSpace(script, startOffset, endOffset - startOffset);
1617 import.type = Import::Library;
1619 import.version = version;
1620 import.qualifier = importId;
1621 import.location = location;
1623 rv.imports << import;
1626 } else if (token == QDeclarativeJSGrammar::T_IDENTIFIER &&
1627 script.mid(l.tokenOffset(), l.tokenLength()) == pragma) {
1631 CHECK_TOKEN(T_IDENTIFIER);
1634 QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
1635 int endOffset = l.tokenLength() + l.tokenOffset();
1637 if (pragmaValue == library) {
1638 pragmas |= QDeclarativeScript::Object::ScriptBlock::Shared;
1639 replaceWithSpace(script, startOffset, endOffset - startOffset);
1645 if (l.tokenStartLine() == startLine)
1655 void QDeclarativeScript::Parser::clear()
1658 qDeleteAll(_refTypes);
1670 QDeclarativeScript::TypeReference *QDeclarativeScript::Parser::findOrCreateType(const QString &name)
1672 TypeReference *type = 0;
1674 for (; i < _refTypes.size(); ++i) {
1675 if (_refTypes.at(i)->name == name) {
1676 type = _refTypes.at(i);
1681 type = new TypeReference(i, name);
1682 _refTypes.append(type);
1688 void QDeclarativeScript::Parser::setTree(QDeclarativeScript::Object *tree)