diff options
Diffstat (limited to 'src/qml/qml/qqmlscript.cpp')
-rw-r--r-- | src/qml/qml/qqmlscript.cpp | 1415 |
1 files changed, 19 insertions, 1396 deletions
diff --git a/src/qml/qml/qqmlscript.cpp b/src/qml/qml/qqmlscript.cpp index 0618c90fa5..cf56d5701d 100644 --- a/src/qml/qml/qqmlscript.cpp +++ b/src/qml/qml/qqmlscript.cpp @@ -58,1359 +58,6 @@ QT_BEGIN_NAMESPACE using namespace QQmlJS; using namespace QQmlScript; -// -// Parser IR classes -// -QQmlScript::Object::Object() -: type(-1), typeReference(0), 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)); -} - -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; - } -} - -int QQmlScript::Object::aggregateDynamicSignalParameterCount() const -{ - int sum = 0; - for (DynamicSignal *s = dynamicSignals.first(); s; s = dynamicSignals.next(s)) - sum += s->parameterTypes.count() + 1; // +1 for return type - return sum; -} - -int QQmlScript::Object::aggregateDynamicSlotParameterCount() const -{ - int sum = 0; - for (DynamicSlot *s = dynamicSlots.first(); s; s = dynamicSlots.next(s)) - sum += s->parameterNames.count() + 1; // +1 for return type - return sum; -} - -QQmlScript::Object::DynamicProperty::DynamicProperty() -: isDefaultProperty(false), isReadOnly(false), type(Variant), defaultValue(0), nextProperty(0), - nameIndex(-1) -{ -} - -QQmlScript::Object::DynamicSignal::DynamicSignal() -: nextSignal(0), nameIndex(-1) -{ -} - -QQmlScript::Object::DynamicSlot::DynamicSlot() -: funcDecl(0), nextSlot(0), nameIndex(-1) -{ -} - -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 -// -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); - - static void extractVersion(QStringRef string, int *maj, int *min); - -protected: - - QQmlScript::Object *defineObjectBinding(AST::Node *node, 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::UiPragma *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 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; - 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; -} - -void ProcessAST::extractVersion(QStringRef string, int *maj, int *min) -{ - *maj = -1; *min = -1; - - if (!string.isEmpty()) { - - int dot = string.indexOf(QLatin1Char('.')); - - if (dot < 0) { - *maj = string.toInt(); - *min = 0; - } else { - *maj = string.left(dot).toInt(); - *min = string.mid(dot + 1).toInt(); - } - } -} - -QString ProcessAST::asString(AST::UiQualifiedId *node) const -{ - QString s; - - for (AST::UiQualifiedId *it = node; it; it = it->next) { - s.append(it->name); - - if (it->next) - s.append(QLatin1Char('.')); - } - - return s; -} - -QQmlScript::Object * -ProcessAST::defineObjectBinding(AST::Node *node, - 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 - - QQmlScript::Object *obj = _parser->_pool.New<QQmlScript::Object>(); - - obj->type = _parser->findOrCreateTypeId(objectType, obj); - obj->typeReference = _parser->_refTypes.at(obj->type); - obj->location = location; - obj->astNode = node; - - 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 == 0) { - 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: UiHeaderItemListOpt UiObjectMemberList ; -bool ProcessAST::visit(AST::UiProgram *node) -{ - accept(node->headers); - 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()) { - extractVersion(textRefAt(node->versionToken), &import.majorVersion, &import.minorVersion); - } 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::UiPragma *node) -{ - QQmlScript::Pragma pragma; - - // For now the only valid pragma is Singleton, so lets validate the input - if (!node->pragmaType->name.isNull()) - { - if (QLatin1String("Singleton") == node->pragmaType->name) - { - pragma.type = QQmlScript::Pragma::Singleton; - } else { - QQmlError error; - error.setDescription(QCoreApplication::translate("QQmlParser","Pragma requires a valid qualifier")); - error.setLine(node->pragmaToken.startLine); - error.setColumn(node->pragmaToken.startColumn); - _parser->_errors << error; - return false; - } - } else { - QQmlError error; - error.setDescription(QCoreApplication::translate("QQmlParser","Pragma requires a valid qualifier")); - error.setLine(node->pragmaToken.startLine); - error.setColumn(node->pragmaToken.startColumn); - _parser->_errors << error; - return false; - } - - AST::SourceLocation startLoc = node->pragmaToken; - AST::SourceLocation endLoc = node->semicolonToken; - pragma.location = location(startLoc, endLoc); - _parser->_pragmas << pragma; - - return false; -} - -bool ProcessAST::visit(AST::UiPublicMember *node) -{ - static const struct TypeNameToType { - const char *name; - size_t nameLength; - Object::DynamicProperty::Type type; - } propTypeNameToTypes[] = { - { "int", strlen("int"), Object::DynamicProperty::Int }, - { "bool", strlen("bool"), Object::DynamicProperty::Bool }, - { "double", strlen("double"), Object::DynamicProperty::Real }, - { "real", strlen("real"), Object::DynamicProperty::Real }, - { "string", strlen("string"), Object::DynamicProperty::String }, - { "url", strlen("url"), Object::DynamicProperty::Url }, - { "color", strlen("color"), Object::DynamicProperty::Color }, - // 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 }, - // { "date", strlen("date"), Object::DynamicProperty::Date }, - { "date", strlen("date"), Object::DynamicProperty::DateTime }, - { "rect", strlen("rect"), Object::DynamicProperty::Rect }, - { "point", strlen("point"), Object::DynamicProperty::Point }, - { "size", strlen("size"), Object::DynamicProperty::Size }, - { "font", strlen("font"), Object::DynamicProperty::Font }, - { "vector2d", strlen("vector2d"), Object::DynamicProperty::Vector2D }, - { "vector3d", strlen("vector3d"), Object::DynamicProperty::Vector3D }, - { "vector4d", strlen("vector4d"), Object::DynamicProperty::Vector4D }, - { "quaternion", strlen("quaternion"), Object::DynamicProperty::Quaternion }, - { "matrix4x4", strlen("matrix4x4"), Object::DynamicProperty::Matrix4x4 }, - { "variant", strlen("variant"), Object::DynamicProperty::Variant }, - { "var", strlen("var"), Object::DynamicProperty::Var } - }; - 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<Object::DynamicProperty::Type>(paramLength); - signal->parameterTypeNames = _parser->_pool.NewRawList<QHashedStringRef>(paramLength); - signal->parameterNames = _parser->_pool.NewRawList<QHashedStringRef>(paramLength); - } - - int index = 0; - while (p) { - const QStringRef &memberType = p->type; - - if (memberType.isEmpty()) { - 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; - } - - const TypeNameToType *type = 0; - for(int typeIndex = 0; typeIndex < propTypeNameToTypesCount; ++typeIndex) { - const TypeNameToType *t = propTypeNameToTypes + typeIndex; - if (t->nameLength == size_t(memberType.length()) && - QHashedString::compare(memberType.constData(), t->name, int(t->nameLength))) { - type = t; - break; - } - } - - if (!type) { - if (memberType.at(0).isUpper()) { - // Must be a QML object type. - // Lazily determine type during compilation. - signal->parameterTypes[index] = Object::DynamicProperty::Custom; - signal->parameterTypeNames[index] = QHashedStringRef(p->type); - } else { - QQmlError error; - QString errStr = QCoreApplication::translate("QQmlParser","Invalid signal parameter type: "); - errStr.append(memberType.toString()); - error.setDescription(errStr); - error.setLine(node->typeToken.startLine); - error.setColumn(node->typeToken.startColumn); - _parser->_errors << error; - return false; - } - } else { - // the parameter is a known basic type - signal->parameterTypes[index] = type->type; - } - - 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", int(strlen("alias")))) { - type = Object::DynamicProperty::Alias; - typeFound = true; - } - - for(int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) { - const TypeNameToType *t = propTypeNameToTypes + ii; - if (t->nameLength == size_t(memberType.length()) && - QHashedString::compare(memberType.constData(), t->name, int(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", int(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) { - // This forces the type to be added to the resolved types list - _parser->findOrCreateTypeId(memberType.toString(), _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(node, /*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, 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; - slot->name = funDecl->name; - slot->funcDecl = funDecl; - 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), _qmlRoot(0), data(0) -{ - -} - -QQmlScript::Parser::~Parser() -{ - clear(); -} - -namespace QQmlScript { -class ParserJsASTData -{ -public: - ParserJsASTData(const QString &filename) - : filename(filename) {} - - QString filename; - Engine engine; -}; -} - -QByteArray QQmlScript::Parser::preparseData() const -{ - return QByteArray(); -} - -bool QQmlScript::Parser::parse(const QString &qmlcode, const QByteArray & /* preparseData */, - const QUrl &url, const QString &urlString) -{ - clear(); - - if (urlString.isEmpty()) { - _scriptFile = url.toString(); - } else { - // Q_ASSERT(urlString == url.toString()); - _scriptFile = urlString; - } - - QString *code = _pool.NewString(qmlcode); - - data = new QQmlScript::ParserJsASTData(_scriptFile); - - Lexer lexer(&data->engine); - lexer.setCode(*code, /*line = */ 1); - - QQmlJS::Parser parser(&data->engine); - - if (! parser.parse() || !parser.diagnosticMessages().isEmpty()) { - - // Extract errors from the parser - foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) { - - if (m.isWarning()) { - qWarning("%s:%d : %s", qPrintable(_scriptFile), m.loc.startLine, qPrintable(m.message)); - 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); - } - - _qmlRoot = parser.ast(); - - 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<QQmlScript::Pragma> QQmlScript::Parser::pragmas() const -{ - return _pragmas; -} - -QList<QQmlError> QQmlScript::Parser::errors() const -{ - return _errors; -} - static void replaceWithSpace(QString &str, int idx, int n) { QChar *data = str.data() + idx; @@ -1540,6 +187,24 @@ static inline bool isUriToken(int token) return false; } +static void extractVersion(QStringRef string, int *maj, int *min) +{ + *maj = -1; *min = -1; + + if (!string.isEmpty()) { + + int dot = string.indexOf(QLatin1Char('.')); + + if (dot < 0) { + *maj = string.toInt(); + *min = 0; + } else { + *maj = string.left(dot).toInt(); + *min = string.mid(dot + 1).toInt(); + } + } +} + QQmlScript::Parser::JavaScriptMetaData QQmlScript::Parser::extractMetaData(QString &script, QQmlError *error) { Q_ASSERT(error); @@ -1670,7 +335,7 @@ QQmlScript::Parser::JavaScriptMetaData QQmlScript::Parser::extractMetaData(QStri } int vmaj, vmin; - ProcessAST::extractVersion(QStringRef(&script, l.tokenOffset(), l.tokenLength()), + extractVersion(QStringRef(&script, l.tokenOffset(), l.tokenLength()), &vmaj, &vmin); bool invalidImport = false; @@ -1749,46 +414,4 @@ QQmlScript::Parser::JavaScriptMetaData QQmlScript::Parser::extractMetaData(QStri return rv; } -void QQmlScript::Parser::clear() -{ - _pragmas.clear(); - _imports.clear(); - _refTypes.clear(); - _errors.clear(); - - if (data) { - delete data; - data = 0; - } - - _pool.clear(); - _qmlRoot = 0; -} - -int QQmlScript::Parser::findOrCreateTypeId(const QString &name, Object *object) -{ - for (int ii = 0; ii < _refTypes.size(); ++ii) { - if (_refTypes.at(ii)->name == name) - return ii; - } - - TypeReference *type = _pool.New<TypeReference>(); - type->name = name; - type->firstUse = object; - _refTypes.append(type); - return _refTypes.size() - 1; -} - -void QQmlScript::Parser::setTree(QQmlScript::Object *tree) -{ - Q_ASSERT(! root); - - root = tree; -} - -Engine *QQmlScript::Parser::jsEngine() const -{ - return data ? &data->engine : 0; -} - QT_END_NAMESPACE |