aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmlscript.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqmlscript.cpp')
-rw-r--r--src/qml/qml/qqmlscript.cpp1700
1 files changed, 1700 insertions, 0 deletions
diff --git a/src/qml/qml/qqmlscript.cpp b/src/qml/qml/qqmlscript.cpp
new file mode 100644
index 0000000000..d1c2faa138
--- /dev/null
+++ b/src/qml/qml/qqmlscript.cpp
@@ -0,0 +1,1700 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlscript_p.h"
+
+#include "parser/qqmljsengine_p.h"
+#include "parser/qqmljsparser_p.h"
+#include "parser/qqmljslexer_p.h"
+#include "parser/qqmljsmemorypool_p.h"
+#include "parser/qqmljsastvisitor_p.h"
+#include "parser/qqmljsast_p.h"
+#include <private/qqmlrewrite_p.h>
+
+#include <QStack>
+#include <QCoreApplication>
+#include <QtDebug>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QQmlJS;
+using namespace QQmlScript;
+
+//
+// Parser IR classes
+//
+QQmlScript::Object::Object()
+: type(-1), idIndex(-1), metatype(0), synthCache(0), defaultProperty(0), parserStatusCast(-1),
+ componentCompileState(0), nextAliasingObject(0), nextIdObject(0)
+{
+}
+
+QQmlScript::Object::~Object()
+{
+ if (synthCache) synthCache->release();
+}
+
+void Object::setBindingBit(int b)
+{
+ while (bindingBitmask.size() < 4 * (1 + b / 32))
+ bindingBitmask.append(char(0));
+
+ quint32 *bits = (quint32 *)bindingBitmask.data();
+ bits[b / 32] |= (1 << (b % 32));
+}
+
+const QMetaObject *Object::metaObject() const
+{
+ if (!metadata.isEmpty() && metatype)
+ return &extObject;
+ else
+ return metatype;
+}
+
+QQmlScript::Property *Object::getDefaultProperty()
+{
+ if (!defaultProperty) {
+ defaultProperty = pool()->New<Property>();
+ defaultProperty->parent = this;
+ }
+ return defaultProperty;
+}
+
+void QQmlScript::Object::addValueProperty(Property *p)
+{
+ valueProperties.append(p);
+}
+
+void QQmlScript::Object::addSignalProperty(Property *p)
+{
+ signalProperties.append(p);
+}
+
+void QQmlScript::Object::addAttachedProperty(Property *p)
+{
+ attachedProperties.append(p);
+}
+
+void QQmlScript::Object::addGroupedProperty(Property *p)
+{
+ groupedProperties.append(p);
+}
+
+void QQmlScript::Object::addValueTypeProperty(Property *p)
+{
+ valueTypeProperties.append(p);
+}
+
+void QQmlScript::Object::addScriptStringProperty(Property *p)
+{
+ scriptStringProperties.append(p);
+}
+
+// This lookup is optimized for missing, and having to create a new property.
+Property *QQmlScript::Object::getProperty(const QHashedStringRef &name, bool create)
+{
+ if (create) {
+ quint32 h = name.hash();
+ if (propertiesHashField.testAndSet(h)) {
+ for (Property *p = properties.first(); p; p = properties.next(p)) {
+ if (p->name() == name)
+ return p;
+ }
+ }
+
+ Property *property = pool()->New<Property>();
+ property->parent = this;
+ property->_name = name;
+ property->isDefault = false;
+ properties.prepend(property);
+ return property;
+ } else {
+ for (Property *p = properties.first(); p; p = properties.next(p)) {
+ if (p->name() == name)
+ return p;
+ }
+ }
+
+ return 0;
+}
+
+Property *QQmlScript::Object::getProperty(const QStringRef &name, bool create)
+{
+ return getProperty(QHashedStringRef(name), create);
+}
+
+Property *QQmlScript::Object::getProperty(const QString &name, bool create)
+{
+ for (Property *p = properties.first(); p; p = properties.next(p)) {
+ if (p->name() == name)
+ return p;
+ }
+
+ if (create) {
+ Property *property = pool()->New<Property>();
+ property->parent = this;
+ property->_name = QStringRef(pool()->NewString(name));
+ propertiesHashField.testAndSet(property->_name.hash());
+ property->isDefault = false;
+ properties.prepend(property);
+ return property;
+ } else {
+ return 0;
+ }
+}
+
+QQmlScript::Object::DynamicProperty::DynamicProperty()
+: isDefaultProperty(false), isReadOnly(false), type(Variant), defaultValue(0), nextProperty(0),
+ resolvedCustomTypeName(0)
+{
+}
+
+QQmlScript::Object::DynamicSignal::DynamicSignal()
+: nextSignal(0)
+{
+}
+
+// Returns length in utf8 bytes
+int QQmlScript::Object::DynamicSignal::parameterTypesLength() const
+{
+ int rv = 0;
+ for (int ii = 0; ii < parameterTypes.count(); ++ii)
+ rv += parameterTypes.at(ii).length();
+ return rv;
+}
+
+// Returns length in utf8 bytes
+int QQmlScript::Object::DynamicSignal::parameterNamesLength() const
+{
+ int rv = 0;
+ for (int ii = 0; ii < parameterNames.count(); ++ii)
+ rv += parameterNames.at(ii).utf8length();
+ return rv;
+}
+
+QQmlScript::Object::DynamicSlot::DynamicSlot()
+: nextSlot(0)
+{
+}
+
+int QQmlScript::Object::DynamicSlot::parameterNamesLength() const
+{
+ int rv = 0;
+ for (int ii = 0; ii < parameterNames.count(); ++ii)
+ rv += parameterNames.at(ii).length();
+ return rv;
+}
+
+QQmlScript::Property::Property()
+: parent(0), type(0), index(-1), value(0), isDefault(true), isDeferred(false),
+ isValueTypeSubProperty(false), isAlias(false), isReadOnlyDeclaration(false),
+ scriptStringScope(-1), nextMainProperty(0), nextProperty(0)
+{
+}
+
+QQmlScript::Object *QQmlScript::Property::getValue(const LocationSpan &l)
+{
+ if (!value) { value = pool()->New<Object>(); value->location = l; }
+ return value;
+}
+
+void QQmlScript::Property::addValue(Value *v)
+{
+ values.append(v);
+}
+
+void QQmlScript::Property::addOnValue(Value *v)
+{
+ onValues.append(v);
+}
+
+bool QQmlScript::Property::isEmpty() const
+{
+ return !value && values.isEmpty() && onValues.isEmpty();
+}
+
+QQmlScript::Value::Value()
+: type(Unknown), object(0), bindingReference(0), nextValue(0)
+{
+}
+
+QQmlScript::Variant::Variant()
+: t(Invalid)
+{
+}
+
+QQmlScript::Variant::Variant(const Variant &o)
+: t(o.t), d(o.d), asWritten(o.asWritten)
+{
+}
+
+QQmlScript::Variant::Variant(bool v)
+: t(Boolean), b(v)
+{
+}
+
+QQmlScript::Variant::Variant(double v, const QStringRef &asWritten)
+: t(Number), d(v), asWritten(asWritten)
+{
+}
+
+QQmlScript::Variant::Variant(QQmlJS::AST::StringLiteral *v)
+: t(String), l(v)
+{
+}
+
+QQmlScript::Variant::Variant(const QStringRef &asWritten, QQmlJS::AST::Node *n)
+: t(Script), n(n), asWritten(asWritten)
+{
+}
+
+QQmlScript::Variant &QQmlScript::Variant::operator=(const Variant &o)
+{
+ t = o.t;
+ d = o.d;
+ asWritten = o.asWritten;
+ return *this;
+}
+
+QQmlScript::Variant::Type QQmlScript::Variant::type() const
+{
+ return t;
+}
+
+bool QQmlScript::Variant::asBoolean() const
+{
+ return b;
+}
+
+QString QQmlScript::Variant::asString() const
+{
+ if (t == String) {
+ return l->value.toString();
+ } else {
+ return asWritten.toString();
+ }
+}
+
+double QQmlScript::Variant::asNumber() const
+{
+ return d;
+}
+
+//reverse of Lexer::singleEscape()
+QString escapedString(const QString &string)
+{
+ QString tmp = QLatin1String("\"");
+ for (int i = 0; i < string.length(); ++i) {
+ const QChar &c = string.at(i);
+ switch(c.unicode()) {
+ case 0x08:
+ tmp += QLatin1String("\\b");
+ break;
+ case 0x09:
+ tmp += QLatin1String("\\t");
+ break;
+ case 0x0A:
+ tmp += QLatin1String("\\n");
+ break;
+ case 0x0B:
+ tmp += QLatin1String("\\v");
+ break;
+ case 0x0C:
+ tmp += QLatin1String("\\f");
+ break;
+ case 0x0D:
+ tmp += QLatin1String("\\r");
+ break;
+ case 0x22:
+ tmp += QLatin1String("\\\"");
+ break;
+ case 0x27:
+ tmp += QLatin1String("\\\'");
+ break;
+ case 0x5C:
+ tmp += QLatin1String("\\\\");
+ break;
+ default:
+ tmp += c;
+ break;
+ }
+ }
+ tmp += QLatin1Char('\"');
+ return tmp;
+}
+
+QString QQmlScript::Variant::asScript() const
+{
+ switch(type()) {
+ default:
+ case Invalid:
+ return QString();
+ case Boolean:
+ return b?QLatin1String("true"):QLatin1String("false");
+ case Number:
+ if (asWritten.isEmpty())
+ return QString::number(d);
+ else
+ return asWritten.toString();
+ case String:
+ return escapedString(asString());
+ case Script:
+ if (AST::IdentifierExpression *i = AST::cast<AST::IdentifierExpression *>(n)) {
+ return i->name.toString();
+ } else
+ return asWritten.toString();
+ }
+}
+
+QQmlJS::AST::Node *QQmlScript::Variant::asAST() const
+{
+ if (type() == Script)
+ return n;
+ else
+ return 0;
+}
+
+bool QQmlScript::Variant::isStringList() const
+{
+ if (isString())
+ return true;
+
+ if (type() != Script || !n)
+ return false;
+
+ AST::ArrayLiteral *array = AST::cast<AST::ArrayLiteral *>(n);
+ if (!array)
+ return false;
+
+ AST::ElementList *elements = array->elements;
+
+ while (elements) {
+
+ if (!AST::cast<AST::StringLiteral *>(elements->expression))
+ return false;
+
+ elements = elements->next;
+ }
+
+ return true;
+}
+
+QStringList QQmlScript::Variant::asStringList() const
+{
+ QStringList rv;
+ if (isString()) {
+ rv << asString();
+ return rv;
+ }
+
+ AST::ArrayLiteral *array = AST::cast<AST::ArrayLiteral *>(n);
+ if (!array)
+ return rv;
+
+ AST::ElementList *elements = array->elements;
+ while (elements) {
+
+ AST::StringLiteral *string = AST::cast<AST::StringLiteral *>(elements->expression);
+ if (!string)
+ return QStringList();
+ rv.append(string->value.toString());
+
+ elements = elements->next;
+ }
+
+ return rv;
+}
+
+//
+// Actual parser classes
+//
+void QQmlScript::Import::extractVersion(int *maj, int *min) const
+{
+ *maj = -1; *min = -1;
+
+ if (!version.isEmpty()) {
+ int dot = version.indexOf(QLatin1Char('.'));
+ if (dot < 0) {
+ *maj = version.toInt();
+ *min = 0;
+ } else {
+ *maj = version.left(dot).toInt();
+ *min = version.mid(dot+1).toInt();
+ }
+ }
+}
+
+namespace {
+
+class ProcessAST: protected AST::Visitor
+{
+ struct State {
+ State() : object(0), property(0) {}
+ State(QQmlScript::Object *o) : object(o), property(0) {}
+ State(QQmlScript::Object *o, Property *p) : object(o), property(p) {}
+
+ QQmlScript::Object *object;
+ Property *property;
+ };
+
+ struct StateStack : public QStack<State>
+ {
+ void pushObject(QQmlScript::Object *obj)
+ {
+ push(State(obj));
+ }
+
+ void pushProperty(const QString &name, const LocationSpan &location)
+ {
+ const State &state = top();
+ if (state.property) {
+ State s(state.property->getValue(location),
+ state.property->getValue(location)->getProperty(name));
+ s.property->location = location;
+ push(s);
+ } else {
+ State s(state.object, state.object->getProperty(name));
+
+ s.property->location = location;
+ push(s);
+ }
+ }
+
+ void pushProperty(const QStringRef &name, const LocationSpan &location)
+ {
+ const State &state = top();
+ if (state.property) {
+ State s(state.property->getValue(location),
+ state.property->getValue(location)->getProperty(name));
+ s.property->location = location;
+ push(s);
+ } else {
+ State s(state.object, state.object->getProperty(name));
+
+ s.property->location = location;
+ push(s);
+ }
+ }
+ };
+
+public:
+ ProcessAST(QQmlScript::Parser *parser);
+ virtual ~ProcessAST();
+
+ void operator()(const QString &code, AST::Node *node);
+
+protected:
+
+ QQmlScript::Object *defineObjectBinding(AST::UiQualifiedId *propertyName, bool onAssignment,
+ const QString &objectType,
+ AST::SourceLocation typeLocation,
+ LocationSpan location,
+ AST::UiObjectInitializer *initializer = 0);
+
+ QQmlScript::Variant getVariant(AST::Statement *stmt);
+ QQmlScript::Variant getVariant(AST::ExpressionNode *expr);
+
+ LocationSpan location(AST::SourceLocation start, AST::SourceLocation end);
+ LocationSpan location(AST::UiQualifiedId *);
+
+ using AST::Visitor::visit;
+ using AST::Visitor::endVisit;
+
+ virtual bool visit(AST::UiProgram *node);
+ virtual bool visit(AST::UiImport *node);
+ virtual bool visit(AST::UiObjectDefinition *node);
+ virtual bool visit(AST::UiPublicMember *node);
+ virtual bool visit(AST::UiObjectBinding *node);
+
+ virtual bool visit(AST::UiScriptBinding *node);
+ virtual bool visit(AST::UiArrayBinding *node);
+ virtual bool visit(AST::UiSourceElement *node);
+
+ void accept(AST::Node *node);
+
+ QString asString(AST::UiQualifiedId *node) const;
+
+ const State state() const;
+ QQmlScript::Object *currentObject() const;
+ Property *currentProperty() const;
+
+ QString qualifiedNameId() const;
+
+ QString textAt(const AST::SourceLocation &loc) const
+ { return _contents->mid(loc.offset, loc.length); }
+
+ QStringRef textRefAt(const AST::SourceLocation &loc) const
+ { return QStringRef(_contents, loc.offset, loc.length); }
+
+ QString textAt(const AST::SourceLocation &first,
+ const AST::SourceLocation &last) const
+ { return _contents->mid(first.offset, last.offset + last.length - first.offset); }
+
+ QStringRef textRefAt(const AST::SourceLocation &first,
+ const AST::SourceLocation &last) const
+ { return QStringRef(_contents, first.offset, last.offset + last.length - first.offset); }
+
+ QString asString(AST::ExpressionNode *expr)
+ {
+ if (! expr)
+ return QString();
+
+ return textAt(expr->firstSourceLocation(), expr->lastSourceLocation());
+ }
+
+ QStringRef asStringRef(AST::ExpressionNode *expr)
+ {
+ if (! expr)
+ return QStringRef();
+
+ return textRefAt(expr->firstSourceLocation(), expr->lastSourceLocation());
+ }
+
+ QString asString(AST::Statement *stmt)
+ {
+ if (! stmt)
+ return QString();
+
+ QString s = textAt(stmt->firstSourceLocation(), stmt->lastSourceLocation());
+ s += QLatin1Char('\n');
+ return s;
+ }
+
+ QStringRef asStringRef(AST::Statement *stmt)
+ {
+ if (! stmt)
+ return QStringRef();
+
+ return textRefAt(stmt->firstSourceLocation(), stmt->lastSourceLocation());
+ }
+
+private:
+ QQmlScript::Parser *_parser;
+ StateStack _stateStack;
+ QStringList _scope;
+ const QString *_contents;
+};
+
+ProcessAST::ProcessAST(QQmlScript::Parser *parser)
+ : _parser(parser)
+{
+}
+
+ProcessAST::~ProcessAST()
+{
+}
+
+void ProcessAST::operator()(const QString &code, AST::Node *node)
+{
+ _contents = &code;
+ accept(node);
+}
+
+void ProcessAST::accept(AST::Node *node)
+{
+ AST::Node::acceptChild(node, this);
+}
+
+const ProcessAST::State ProcessAST::state() const
+{
+ if (_stateStack.isEmpty())
+ return State();
+
+ return _stateStack.back();
+}
+
+QQmlScript::Object *ProcessAST::currentObject() const
+{
+ return state().object;
+}
+
+Property *ProcessAST::currentProperty() const
+{
+ return state().property;
+}
+
+QString ProcessAST::qualifiedNameId() const
+{
+ return _scope.join(QLatin1String("/"));
+}
+
+QString ProcessAST::asString(AST::UiQualifiedId *node) const
+{
+ QString s;
+
+ for (AST::UiQualifiedId *it = node; it; it = it->next) {
+ s.append(it->name.toString());
+
+ if (it->next)
+ s.append(QLatin1Char('.'));
+ }
+
+ return s;
+}
+
+QQmlScript::Object *
+ProcessAST::defineObjectBinding(AST::UiQualifiedId *propertyName,
+ bool onAssignment,
+ const QString &objectType,
+ AST::SourceLocation typeLocation,
+ LocationSpan location,
+ AST::UiObjectInitializer *initializer)
+{
+ int lastTypeDot = objectType.lastIndexOf(QLatin1Char('.'));
+
+ // With no preceding qualification, first char is at (-1 + 1) == 0
+ bool isType = !objectType.isEmpty() && objectType.at(lastTypeDot+1).isUpper();
+
+ int propertyCount = 0;
+ for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
+ ++propertyCount;
+ _stateStack.pushProperty(name->name,
+ this->location(name));
+ }
+
+ if (!onAssignment && propertyCount && currentProperty() && !currentProperty()->values.isEmpty()) {
+ QQmlError error;
+ error.setDescription(QCoreApplication::translate("QQmlParser","Property value set multiple times"));
+ error.setLine(this->location(propertyName).start.line);
+ error.setColumn(this->location(propertyName).start.column);
+ _parser->_errors << error;
+ return 0;
+ }
+
+ if (!isType) {
+
+ // Is the identifier qualified by a namespace?
+ int namespaceLength = 0;
+ if (lastTypeDot > 0) {
+ const QString qualifier(objectType.left(lastTypeDot));
+
+ for (int ii = 0; ii < _parser->_imports.count(); ++ii) {
+ const QQmlScript::Import &import = _parser->_imports.at(ii);
+ if (import.qualifier == qualifier) {
+ // The qualifier is a namespace - expect a type here
+ namespaceLength = qualifier.length() + 1;
+ break;
+ }
+ }
+ }
+
+ if (propertyCount || !currentObject() || namespaceLength) {
+ QQmlError error;
+ error.setDescription(QCoreApplication::translate("QQmlParser","Expected type name"));
+ error.setLine(typeLocation.startLine);
+ error.setColumn(typeLocation.startColumn + namespaceLength);
+ _parser->_errors << error;
+ return 0;
+ }
+
+ LocationSpan loc = ProcessAST::location(typeLocation, typeLocation);
+ if (propertyName)
+ loc = ProcessAST::location(propertyName);
+
+ _stateStack.pushProperty(objectType, loc);
+ accept(initializer);
+ _stateStack.pop();
+
+ return 0;
+
+ } else {
+ // Class
+
+ QString resolvableObjectType = objectType;
+ if (lastTypeDot >= 0)
+ resolvableObjectType.replace(QLatin1Char('.'),QLatin1Char('/'));
+
+ QQmlScript::Object *obj = _parser->_pool.New<QQmlScript::Object>();
+
+ QQmlScript::TypeReference *typeRef = _parser->findOrCreateType(resolvableObjectType);
+ obj->type = typeRef->id;
+
+ typeRef->refObjects.append(obj);
+
+ // XXX this doesn't do anything (_scope never builds up)
+ _scope.append(resolvableObjectType);
+ obj->typeName = qualifiedNameId();
+ _scope.removeLast();
+
+ obj->location = location;
+
+ if (propertyCount) {
+ Property *prop = currentProperty();
+ QQmlScript::Value *v = _parser->_pool.New<QQmlScript::Value>();
+ v->object = obj;
+ v->location = obj->location;
+ if (onAssignment)
+ prop->addOnValue(v);
+ else
+ prop->addValue(v);
+
+ while (propertyCount--)
+ _stateStack.pop();
+
+ } else {
+
+ if (! _parser->tree()) {
+ _parser->setTree(obj);
+ } else {
+ const State state = _stateStack.top();
+ QQmlScript::Value *v = _parser->_pool.New<QQmlScript::Value>();
+ v->object = obj;
+ v->location = obj->location;
+ if (state.property) {
+ state.property->addValue(v);
+ } else {
+ Property *defaultProp = state.object->getDefaultProperty();
+ if (defaultProp->location.start.line == -1) {
+ defaultProp->location = v->location;
+ defaultProp->location.end = defaultProp->location.start;
+ defaultProp->location.range.length = 0;
+ }
+ defaultProp->addValue(v);
+ }
+ }
+ }
+
+ _stateStack.pushObject(obj);
+ accept(initializer);
+ _stateStack.pop();
+
+ return obj;
+ }
+}
+
+LocationSpan ProcessAST::location(AST::UiQualifiedId *id)
+{
+ return location(id->identifierToken, id->identifierToken);
+}
+
+LocationSpan ProcessAST::location(AST::SourceLocation start, AST::SourceLocation end)
+{
+ LocationSpan rv;
+ rv.start.line = start.startLine;
+ rv.start.column = start.startColumn;
+ rv.end.line = end.startLine;
+ rv.end.column = end.startColumn + end.length - 1;
+ rv.range.offset = start.offset;
+ rv.range.length = end.offset + end.length - start.offset;
+ return rv;
+}
+
+// UiProgram: UiImportListOpt UiObjectMemberList ;
+bool ProcessAST::visit(AST::UiProgram *node)
+{
+ accept(node->imports);
+ accept(node->members->member);
+ return false;
+}
+
+// UiImport: T_IMPORT T_STRING_LITERAL ;
+bool ProcessAST::visit(AST::UiImport *node)
+{
+ QString uri;
+ QQmlScript::Import import;
+
+ if (!node->fileName.isNull()) {
+ uri = node->fileName.toString();
+
+ if (uri.endsWith(QLatin1String(".js"))) {
+ import.type = QQmlScript::Import::Script;
+ } else {
+ import.type = QQmlScript::Import::File;
+ }
+ } else {
+ import.type = QQmlScript::Import::Library;
+ uri = asString(node->importUri);
+ }
+
+ AST::SourceLocation startLoc = node->importToken;
+ AST::SourceLocation endLoc = node->semicolonToken;
+
+ // Qualifier
+ if (!node->importId.isNull()) {
+ import.qualifier = node->importId.toString();
+ if (!import.qualifier.at(0).isUpper()) {
+ QQmlError error;
+ error.setDescription(QCoreApplication::translate("QQmlParser","Invalid import qualifier ID"));
+ error.setLine(node->importIdToken.startLine);
+ error.setColumn(node->importIdToken.startColumn);
+ _parser->_errors << error;
+ return false;
+ }
+ if (import.qualifier == QLatin1String("Qt")) {
+ QQmlError error;
+ error.setDescription(QCoreApplication::translate("QQmlParser","Reserved name \"Qt\" cannot be used as an qualifier"));
+ error.setLine(node->importIdToken.startLine);
+ error.setColumn(node->importIdToken.startColumn);
+ _parser->_errors << error;
+ return false;
+ }
+
+ // Check for script qualifier clashes
+ bool isScript = import.type == QQmlScript::Import::Script;
+ for (int ii = 0; ii < _parser->_imports.count(); ++ii) {
+ const QQmlScript::Import &other = _parser->_imports.at(ii);
+ bool otherIsScript = other.type == QQmlScript::Import::Script;
+
+ if ((isScript || otherIsScript) && import.qualifier == other.qualifier) {
+ QQmlError error;
+ error.setDescription(QCoreApplication::translate("QQmlParser","Script import qualifiers must be unique."));
+ error.setLine(node->importIdToken.startLine);
+ error.setColumn(node->importIdToken.startColumn);
+ _parser->_errors << error;
+ return false;
+ }
+ }
+
+ } else if (import.type == QQmlScript::Import::Script) {
+ QQmlError error;
+ error.setDescription(QCoreApplication::translate("QQmlParser","Script import requires a qualifier"));
+ error.setLine(node->fileNameToken.startLine);
+ error.setColumn(node->fileNameToken.startColumn);
+ _parser->_errors << error;
+ return false;
+ }
+
+ if (node->versionToken.isValid()) {
+ import.version = textAt(node->versionToken);
+ } else if (import.type == QQmlScript::Import::Library) {
+ QQmlError error;
+ error.setDescription(QCoreApplication::translate("QQmlParser","Library import requires a version"));
+ error.setLine(node->importIdToken.startLine);
+ error.setColumn(node->importIdToken.startColumn);
+ _parser->_errors << error;
+ return false;
+ }
+
+
+ import.location = location(startLoc, endLoc);
+ import.uri = uri;
+
+ _parser->_imports << import;
+
+ return false;
+}
+
+bool ProcessAST::visit(AST::UiPublicMember *node)
+{
+ static const struct TypeNameToType {
+ const char *name;
+ int nameLength;
+ Object::DynamicProperty::Type type;
+ const char *qtName;
+ int qtNameLength;
+ } propTypeNameToTypes[] = {
+ { "int", strlen("int"), Object::DynamicProperty::Int, "int", strlen("int") },
+ { "bool", strlen("bool"), Object::DynamicProperty::Bool, "bool", strlen("bool") },
+ { "double", strlen("double"), Object::DynamicProperty::Real, "double", strlen("double") },
+ { "real", strlen("real"), Object::DynamicProperty::Real, "qreal", strlen("qreal") },
+ { "string", strlen("string"), Object::DynamicProperty::String, "QString", strlen("QString") },
+ { "url", strlen("url"), Object::DynamicProperty::Url, "QUrl", strlen("QUrl") },
+ { "color", strlen("color"), Object::DynamicProperty::Color, "QColor", strlen("QColor") },
+ // Internally QTime, QDate and QDateTime are all supported.
+ // To be more consistent with JavaScript we expose only
+ // QDateTime as it matches closely with the Date JS type.
+ // We also call it "date" to match.
+ // { "time", strlen("time"), Object::DynamicProperty::Time, "QTime", strlen("QTime") },
+ // { "date", strlen("date"), Object::DynamicProperty::Date, "QDate", strlen("QDate") },
+ { "date", strlen("date"), Object::DynamicProperty::DateTime, "QDateTime", strlen("QDateTime") },
+ { "variant", strlen("variant"), Object::DynamicProperty::Variant, "QVariant", strlen("QVariant") },
+ { "var", strlen("var"), Object::DynamicProperty::Var, "QVariant", strlen("QVariant") }
+ };
+ static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) /
+ sizeof(propTypeNameToTypes[0]);
+
+ if(node->type == AST::UiPublicMember::Signal) {
+ Object::DynamicSignal *signal = _parser->_pool.New<Object::DynamicSignal>();
+ signal->name = node->name;
+
+ AST::UiParameterList *p = node->parameters;
+ int paramLength = 0;
+ while (p) { paramLength++; p = p->next; }
+ p = node->parameters;
+
+ if (paramLength) {
+ signal->parameterTypes = _parser->_pool.NewRawList<QHashedCStringRef>(paramLength);
+ signal->parameterNames = _parser->_pool.NewRawList<QHashedStringRef>(paramLength);
+ }
+
+ int index = 0;
+ while (p) {
+ const QStringRef &memberType = p->type;
+
+ const TypeNameToType *type = 0;
+ for(int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) {
+ const TypeNameToType *t = propTypeNameToTypes + typeIndex;
+ if (t->nameLength == memberType.length() &&
+ QHashedString::compare(memberType.constData(), t->name, t->nameLength)) {
+ type = t;
+ break;
+ }
+ }
+
+ if (!type) {
+ QQmlError error;
+ error.setDescription(QCoreApplication::translate("QQmlParser","Expected parameter type"));
+ error.setLine(node->typeToken.startLine);
+ error.setColumn(node->typeToken.startColumn);
+ _parser->_errors << error;
+ return false;
+ }
+
+ signal->parameterTypes[index] = QHashedCStringRef(type->qtName, type->qtNameLength);
+ signal->parameterNames[index] = QHashedStringRef(p->name);
+ p = p->next;
+ index++;
+ }
+
+ signal->location = location(node->typeToken, node->semicolonToken);
+ _stateStack.top().object->dynamicSignals.append(signal);
+ } else {
+ const QStringRef &memberType = node->memberType;
+ const QStringRef &name = node->name;
+
+ bool typeFound = false;
+ Object::DynamicProperty::Type type;
+
+ if ((unsigned)memberType.length() == strlen("alias") &&
+ QHashedString::compare(memberType.constData(), "alias", strlen("alias"))) {
+ type = Object::DynamicProperty::Alias;
+ typeFound = true;
+ }
+
+ for(int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) {
+ const TypeNameToType *t = propTypeNameToTypes + ii;
+ if (t->nameLength == memberType.length() &&
+ QHashedString::compare(memberType.constData(), t->name, t->nameLength)) {
+ type = t->type;
+ typeFound = true;
+ }
+ }
+
+ if (!typeFound && memberType.at(0).isUpper()) {
+ const QStringRef &typeModifier = node->typeModifier;
+
+ if (typeModifier.isEmpty()) {
+ type = Object::DynamicProperty::Custom;
+ } else if((unsigned)typeModifier.length() == strlen("list") &&
+ QHashedString::compare(typeModifier.constData(), "list", strlen("list"))) {
+ type = Object::DynamicProperty::CustomList;
+ } else {
+ QQmlError error;
+ error.setDescription(QCoreApplication::translate("QQmlParser","Invalid property type modifier"));
+ error.setLine(node->typeModifierToken.startLine);
+ error.setColumn(node->typeModifierToken.startColumn);
+ _parser->_errors << error;
+ return false;
+ }
+ typeFound = true;
+ } else if (!node->typeModifier.isNull()) {
+ QQmlError error;
+ error.setDescription(QCoreApplication::translate("QQmlParser","Unexpected property type modifier"));
+ error.setLine(node->typeModifierToken.startLine);
+ error.setColumn(node->typeModifierToken.startColumn);
+ _parser->_errors << error;
+ return false;
+ }
+
+ if(!typeFound) {
+ QQmlError error;
+ error.setDescription(QCoreApplication::translate("QQmlParser","Expected property type"));
+ error.setLine(node->typeToken.startLine);
+ error.setColumn(node->typeToken.startColumn);
+ _parser->_errors << error;
+ return false;
+ }
+
+ Object::DynamicProperty *property = _parser->_pool.New<Object::DynamicProperty>();
+ property->isDefaultProperty = node->isDefaultMember;
+ property->isReadOnly = node->isReadonlyMember;
+ property->type = type;
+ property->nameLocation.line = node->identifierToken.startLine;
+ property->nameLocation.column = node->identifierToken.startColumn;
+ if (type >= Object::DynamicProperty::Custom) {
+ QQmlScript::TypeReference *typeRef =
+ _parser->findOrCreateType(memberType.toString());
+ typeRef->refObjects.append(_stateStack.top().object);
+ property->customType = memberType;
+ }
+
+ property->name = QHashedStringRef(name);
+ property->location = location(node->firstSourceLocation(),
+ node->lastSourceLocation());
+
+ if (node->statement) { // default value
+ property->defaultValue = _parser->_pool.New<Property>();
+ property->defaultValue->parent = _stateStack.top().object;
+ property->defaultValue->location =
+ location(node->statement->firstSourceLocation(),
+ node->statement->lastSourceLocation());
+ QQmlScript::Value *value = _parser->_pool.New<QQmlScript::Value>();
+ value->location = location(node->statement->firstSourceLocation(),
+ node->statement->lastSourceLocation());
+ value->value = getVariant(node->statement);
+ property->defaultValue->values.append(value);
+ }
+
+ _stateStack.top().object->dynamicProperties.append(property);
+
+ // process QML-like initializers (e.g. property Object o: Object {})
+ accept(node->binding);
+ }
+
+ return false;
+}
+
+
+// UiObjectMember: UiQualifiedId UiObjectInitializer ;
+bool ProcessAST::visit(AST::UiObjectDefinition *node)
+{
+ LocationSpan l = location(node->firstSourceLocation(),
+ node->lastSourceLocation());
+
+ const QString objectType = asString(node->qualifiedTypeNameId);
+ const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken;
+
+ defineObjectBinding(/*propertyName = */ 0, false, objectType,
+ typeLocation, l, node->initializer);
+
+ return false;
+}
+
+
+// UiObjectMember: UiQualifiedId T_COLON UiQualifiedId UiObjectInitializer ;
+bool ProcessAST::visit(AST::UiObjectBinding *node)
+{
+ LocationSpan l = location(node->qualifiedTypeNameId->identifierToken,
+ node->initializer->rbraceToken);
+
+ const QString objectType = asString(node->qualifiedTypeNameId);
+ const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken;
+
+ defineObjectBinding(node->qualifiedId, node->hasOnToken, objectType,
+ typeLocation, l, node->initializer);
+
+ return false;
+}
+
+QQmlScript::Variant ProcessAST::getVariant(AST::Statement *stmt)
+{
+ if (stmt) {
+ if (AST::ExpressionStatement *exprStmt = AST::cast<AST::ExpressionStatement *>(stmt))
+ return getVariant(exprStmt->expression);
+
+ return QQmlScript::Variant(asStringRef(stmt), stmt);
+ }
+
+ return QQmlScript::Variant();
+}
+
+QQmlScript::Variant ProcessAST::getVariant(AST::ExpressionNode *expr)
+{
+ if (AST::StringLiteral *lit = AST::cast<AST::StringLiteral *>(expr)) {
+ return QQmlScript::Variant(lit);
+ } else if (expr->kind == AST::Node::Kind_TrueLiteral) {
+ return QQmlScript::Variant(true);
+ } else if (expr->kind == AST::Node::Kind_FalseLiteral) {
+ return QQmlScript::Variant(false);
+ } else if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(expr)) {
+ return QQmlScript::Variant(lit->value, asStringRef(expr));
+ } else {
+
+ if (AST::UnaryMinusExpression *unaryMinus = AST::cast<AST::UnaryMinusExpression *>(expr)) {
+ if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(unaryMinus->expression)) {
+ return QQmlScript::Variant(-lit->value, asStringRef(expr));
+ }
+ }
+
+ return QQmlScript::Variant(asStringRef(expr), expr);
+ }
+}
+
+
+// UiObjectMember: UiQualifiedId T_COLON Statement ;
+bool ProcessAST::visit(AST::UiScriptBinding *node)
+{
+ int propertyCount = 0;
+ AST::UiQualifiedId *propertyName = node->qualifiedId;
+ for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
+ ++propertyCount;
+ _stateStack.pushProperty(name->name,
+ location(name));
+ }
+
+ Property *prop = currentProperty();
+
+ if (!prop->values.isEmpty()) {
+ QQmlError error;
+ error.setDescription(QCoreApplication::translate("QQmlParser","Property value set multiple times"));
+ error.setLine(this->location(propertyName).start.line);
+ error.setColumn(this->location(propertyName).start.column);
+ _parser->_errors << error;
+ return 0;
+ }
+
+ QQmlScript::Variant primitive;
+
+ if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(node->statement)) {
+ primitive = getVariant(stmt->expression);
+ } else { // do binding
+ primitive = QQmlScript::Variant(asStringRef(node->statement), node->statement);
+ }
+
+ prop->location.range.length = prop->location.range.offset + prop->location.range.length - node->qualifiedId->identifierToken.offset;
+ prop->location.range.offset = node->qualifiedId->identifierToken.offset;
+ QQmlScript::Value *v = _parser->_pool.New<QQmlScript::Value>();
+ v->value = primitive;
+ v->location = location(node->statement->firstSourceLocation(),
+ node->statement->lastSourceLocation());
+
+ prop->addValue(v);
+
+ while (propertyCount--)
+ _stateStack.pop();
+
+ return false;
+}
+
+// UiObjectMember: UiQualifiedId T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ;
+bool ProcessAST::visit(AST::UiArrayBinding *node)
+{
+ int propertyCount = 0;
+ AST::UiQualifiedId *propertyName = node->qualifiedId;
+ for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
+ ++propertyCount;
+ _stateStack.pushProperty(name->name,
+ location(name));
+ }
+
+ Property* prop = currentProperty();
+
+ if (!prop->values.isEmpty()) {
+ QQmlError error;
+ error.setDescription(QCoreApplication::translate("QQmlParser","Property value set multiple times"));
+ error.setLine(this->location(propertyName).start.line);
+ error.setColumn(this->location(propertyName).start.column);
+ _parser->_errors << error;
+ return false;
+ }
+
+ accept(node->members);
+
+ // For the DOM, store the position of the T_LBRACKET upto the T_RBRACKET as the range:
+ prop->listValueRange.offset = node->lbracketToken.offset;
+ prop->listValueRange.length = node->rbracketToken.offset + node->rbracketToken.length - node->lbracketToken.offset;
+
+ while (propertyCount--)
+ _stateStack.pop();
+
+ return false;
+}
+
+bool ProcessAST::visit(AST::UiSourceElement *node)
+{
+ QQmlScript::Object *obj = currentObject();
+
+ if (AST::FunctionDeclaration *funDecl = AST::cast<AST::FunctionDeclaration *>(node->sourceElement)) {
+
+ Object::DynamicSlot *slot = _parser->_pool.New<Object::DynamicSlot>();
+ slot->location = location(funDecl->identifierToken, funDecl->lastSourceLocation());
+
+ AST::FormalParameterList *f = funDecl->formals;
+ while (f) {
+ slot->parameterNames << f->name.toUtf8();
+ f = f->next;
+ }
+
+ AST::SourceLocation loc = funDecl->rparenToken;
+ loc.offset = loc.end();
+ loc.startColumn += 1;
+ QString body = textAt(loc, funDecl->rbraceToken);
+ slot->name = funDecl->name;
+ slot->body = body;
+ obj->dynamicSlots.append(slot);
+
+ } else {
+ QQmlError error;
+ error.setDescription(QCoreApplication::translate("QQmlParser","JavaScript declaration outside Script element"));
+ error.setLine(node->firstSourceLocation().startLine);
+ error.setColumn(node->firstSourceLocation().startColumn);
+ _parser->_errors << error;
+ }
+ return false;
+}
+
+} // end of anonymous namespace
+
+
+QQmlScript::Parser::Parser()
+: root(0), data(0)
+{
+
+}
+
+QQmlScript::Parser::~Parser()
+{
+ clear();
+}
+
+namespace QQmlScript {
+class ParserJsASTData
+{
+public:
+ ParserJsASTData(const QString &filename)
+ : filename(filename) {}
+
+ QString filename;
+ Engine engine;
+};
+}
+
+bool QQmlScript::Parser::parse(const QByteArray &qmldata, const QUrl &url,
+ const QString &urlString)
+{
+ clear();
+
+ if (urlString.isEmpty()) {
+ _scriptFile = url.toString();
+ } else {
+ // Q_ASSERT(urlString == url.toString());
+ _scriptFile = urlString;
+ }
+
+ QTextStream stream(qmldata, QIODevice::ReadOnly);
+#ifndef QT_NO_TEXTCODEC
+ stream.setCodec("UTF-8");
+#endif
+ QString *code = _pool.NewString(stream.readAll());
+
+ data = new QQmlScript::ParserJsASTData(_scriptFile);
+
+ Lexer lexer(&data->engine);
+ lexer.setCode(*code, /*line = */ 1);
+
+ QQmlJS::Parser parser(&data->engine);
+
+ if (! parser.parse() || !_errors.isEmpty()) {
+
+ // Extract errors from the parser
+ foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) {
+
+ if (m.isWarning())
+ continue;
+
+ QQmlError error;
+ error.setUrl(url);
+ error.setDescription(m.message);
+ error.setLine(m.loc.startLine);
+ error.setColumn(m.loc.startColumn);
+ _errors << error;
+
+ }
+ }
+
+ if (_errors.isEmpty()) {
+ ProcessAST process(this);
+ process(*code, parser.ast());
+
+ // Set the url for process errors
+ for(int ii = 0; ii < _errors.count(); ++ii)
+ _errors[ii].setUrl(url);
+ }
+
+ return _errors.isEmpty();
+}
+
+QList<QQmlScript::TypeReference*> QQmlScript::Parser::referencedTypes() const
+{
+ return _refTypes;
+}
+
+QQmlScript::Object *QQmlScript::Parser::tree() const
+{
+ return root;
+}
+
+QList<QQmlScript::Import> QQmlScript::Parser::imports() const
+{
+ return _imports;
+}
+
+QList<QQmlError> QQmlScript::Parser::errors() const
+{
+ return _errors;
+}
+
+static void replaceWithSpace(QString &str, int idx, int n)
+{
+ QChar *data = str.data() + idx;
+ const QChar space(QLatin1Char(' '));
+ for (int ii = 0; ii < n; ++ii)
+ *data++ = space;
+}
+
+static QQmlScript::LocationSpan
+locationFromLexer(const QQmlJS::Lexer &lex, int startLine, int startColumn, int startOffset)
+{
+ QQmlScript::LocationSpan l;
+
+ l.start.line = startLine; l.start.column = startColumn;
+ l.end.line = lex.tokenEndLine(); l.end.column = lex.tokenEndColumn();
+ l.range.offset = startOffset;
+ l.range.length = lex.tokenOffset() + lex.tokenLength() - startOffset;
+
+ return l;
+}
+
+/*
+Searches for ".pragma <value>" declarations within \a script. Currently supported pragmas
+are:
+ library
+*/
+QQmlScript::Object::ScriptBlock::Pragmas QQmlScript::Parser::extractPragmas(QString &script)
+{
+ QQmlScript::Object::ScriptBlock::Pragmas rv = QQmlScript::Object::ScriptBlock::None;
+
+ const QString pragma(QLatin1String("pragma"));
+ const QString library(QLatin1String("library"));
+
+ QQmlJS::Lexer l(0);
+ l.setCode(script, 0);
+
+ int token = l.lex();
+
+ while (true) {
+ if (token != QQmlJSGrammar::T_DOT)
+ return rv;
+
+ int startOffset = l.tokenOffset();
+ int startLine = l.tokenStartLine();
+
+ token = l.lex();
+
+ if (token != QQmlJSGrammar::T_IDENTIFIER ||
+ l.tokenStartLine() != startLine ||
+ script.mid(l.tokenOffset(), l.tokenLength()) != pragma)
+ return rv;
+
+ token = l.lex();
+
+ if (token != QQmlJSGrammar::T_IDENTIFIER ||
+ l.tokenStartLine() != startLine)
+ return rv;
+
+ QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
+ int endOffset = l.tokenLength() + l.tokenOffset();
+
+ token = l.lex();
+ if (l.tokenStartLine() == startLine)
+ return rv;
+
+ if (pragmaValue == library) {
+ rv |= QQmlScript::Object::ScriptBlock::Shared;
+ replaceWithSpace(script, startOffset, endOffset - startOffset);
+ } else {
+ return rv;
+ }
+ }
+ return rv;
+}
+
+#define CHECK_LINE if (l.tokenStartLine() != startLine) return rv;
+#define CHECK_TOKEN(t) if (token != QQmlJSGrammar:: t) return rv;
+
+static const int uriTokens[] = {
+ QQmlJSGrammar::T_IDENTIFIER,
+ QQmlJSGrammar::T_PROPERTY,
+ QQmlJSGrammar::T_SIGNAL,
+ QQmlJSGrammar::T_READONLY,
+ QQmlJSGrammar::T_ON,
+ QQmlJSGrammar::T_BREAK,
+ QQmlJSGrammar::T_CASE,
+ QQmlJSGrammar::T_CATCH,
+ QQmlJSGrammar::T_CONTINUE,
+ QQmlJSGrammar::T_DEFAULT,
+ QQmlJSGrammar::T_DELETE,
+ QQmlJSGrammar::T_DO,
+ QQmlJSGrammar::T_ELSE,
+ QQmlJSGrammar::T_FALSE,
+ QQmlJSGrammar::T_FINALLY,
+ QQmlJSGrammar::T_FOR,
+ QQmlJSGrammar::T_FUNCTION,
+ QQmlJSGrammar::T_IF,
+ QQmlJSGrammar::T_IN,
+ QQmlJSGrammar::T_INSTANCEOF,
+ QQmlJSGrammar::T_NEW,
+ QQmlJSGrammar::T_NULL,
+ QQmlJSGrammar::T_RETURN,
+ QQmlJSGrammar::T_SWITCH,
+ QQmlJSGrammar::T_THIS,
+ QQmlJSGrammar::T_THROW,
+ QQmlJSGrammar::T_TRUE,
+ QQmlJSGrammar::T_TRY,
+ QQmlJSGrammar::T_TYPEOF,
+ QQmlJSGrammar::T_VAR,
+ QQmlJSGrammar::T_VOID,
+ QQmlJSGrammar::T_WHILE,
+ QQmlJSGrammar::T_CONST,
+ QQmlJSGrammar::T_DEBUGGER,
+ QQmlJSGrammar::T_RESERVED_WORD,
+ QQmlJSGrammar::T_WITH,
+
+ QQmlJSGrammar::EOF_SYMBOL
+};
+static inline bool isUriToken(int token)
+{
+ const int *current = uriTokens;
+ while (*current != QQmlJSGrammar::EOF_SYMBOL) {
+ if (*current == token)
+ return true;
+ ++current;
+ }
+ return false;
+}
+
+QQmlScript::Parser::JavaScriptMetaData QQmlScript::Parser::extractMetaData(QString &script)
+{
+ JavaScriptMetaData rv;
+
+ QQmlScript::Object::ScriptBlock::Pragmas &pragmas = rv.pragmas;
+
+ const QString pragma(QLatin1String("pragma"));
+ const QString js(QLatin1String(".js"));
+ const QString library(QLatin1String("library"));
+
+ QQmlJS::Lexer l(0);
+ l.setCode(script, 0);
+
+ int token = l.lex();
+
+ while (true) {
+ if (token != QQmlJSGrammar::T_DOT)
+ return rv;
+
+ int startOffset = l.tokenOffset();
+ int startLine = l.tokenStartLine();
+ int startColumn = l.tokenStartColumn();
+
+ token = l.lex();
+
+ CHECK_LINE;
+
+ if (token == QQmlJSGrammar::T_IMPORT) {
+
+ // .import <URI> <Version> as <Identifier>
+ // .import <file.js> as <Identifier>
+
+ token = l.lex();
+
+ CHECK_LINE;
+
+ if (token == QQmlJSGrammar::T_STRING_LITERAL) {
+
+ QString file = l.tokenText();
+
+ if (!file.endsWith(js))
+ return rv;
+
+ token = l.lex();
+
+ CHECK_TOKEN(T_AS);
+ CHECK_LINE;
+
+ token = l.lex();
+
+ CHECK_TOKEN(T_IDENTIFIER);
+ CHECK_LINE;
+
+ int endOffset = l.tokenLength() + l.tokenOffset();
+
+ QString importId = script.mid(l.tokenOffset(), l.tokenLength());
+
+ if (!importId.at(0).isUpper())
+ return rv;
+
+ QQmlScript::LocationSpan location =
+ locationFromLexer(l, startLine, startColumn, startOffset);
+
+ token = l.lex();
+ if (l.tokenStartLine() == startLine)
+ return rv;
+
+ replaceWithSpace(script, startOffset, endOffset - startOffset);
+
+ Import import;
+ import.type = Import::Script;
+ import.uri = file;
+ import.qualifier = importId;
+ import.location = location;
+
+ rv.imports << import;
+ } else {
+ // URI
+ QString uri;
+ QString version;
+
+ while (true) {
+ if (!isUriToken(token))
+ return rv;
+
+ uri.append(l.tokenText());
+
+ token = l.lex();
+ CHECK_LINE;
+ if (token != QQmlJSGrammar::T_DOT)
+ break;
+
+ uri.append(QLatin1Char('.'));
+
+ token = l.lex();
+ CHECK_LINE;
+ }
+
+ CHECK_TOKEN(T_NUMERIC_LITERAL);
+ version = script.mid(l.tokenOffset(), l.tokenLength());
+
+ token = l.lex();
+
+ CHECK_TOKEN(T_AS);
+ CHECK_LINE;
+
+ token = l.lex();
+
+ CHECK_TOKEN(T_IDENTIFIER);
+ CHECK_LINE;
+
+ int endOffset = l.tokenLength() + l.tokenOffset();
+
+ QString importId = script.mid(l.tokenOffset(), l.tokenLength());
+
+ if (!importId.at(0).isUpper())
+ return rv;
+
+ QQmlScript::LocationSpan location =
+ locationFromLexer(l, startLine, startColumn, startOffset);
+
+ token = l.lex();
+ if (l.tokenStartLine() == startLine)
+ return rv;
+
+ replaceWithSpace(script, startOffset, endOffset - startOffset);
+
+ Import import;
+ import.type = Import::Library;
+ import.uri = uri;
+ import.version = version;
+ import.qualifier = importId;
+ import.location = location;
+
+ rv.imports << import;
+ }
+
+ } else if (token == QQmlJSGrammar::T_IDENTIFIER &&
+ script.mid(l.tokenOffset(), l.tokenLength()) == pragma) {
+
+ token = l.lex();
+
+ CHECK_TOKEN(T_IDENTIFIER);
+ CHECK_LINE;
+
+ QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
+ int endOffset = l.tokenLength() + l.tokenOffset();
+
+ if (pragmaValue == library) {
+ pragmas |= QQmlScript::Object::ScriptBlock::Shared;
+ replaceWithSpace(script, startOffset, endOffset - startOffset);
+ } else {
+ return rv;
+ }
+
+ token = l.lex();
+ if (l.tokenStartLine() == startLine)
+ return rv;
+
+ } else {
+ return rv;
+ }
+ }
+ return rv;
+}
+
+void QQmlScript::Parser::clear()
+{
+ _imports.clear();
+ qDeleteAll(_refTypes);
+ _refTypes.clear();
+ _errors.clear();
+
+ if (data) {
+ delete data;
+ data = 0;
+ }
+
+ _pool.clear();
+}
+
+QQmlScript::TypeReference *QQmlScript::Parser::findOrCreateType(const QString &name)
+{
+ TypeReference *type = 0;
+ int i = 0;
+ for (; i < _refTypes.size(); ++i) {
+ if (_refTypes.at(i)->name == name) {
+ type = _refTypes.at(i);
+ break;
+ }
+ }
+ if (!type) {
+ type = new TypeReference(i, name);
+ _refTypes.append(type);
+ }
+
+ return type;
+}
+
+void QQmlScript::Parser::setTree(QQmlScript::Object *tree)
+{
+ Q_ASSERT(! root);
+
+ root = tree;
+}
+
+QT_END_NAMESPACE