diff --git a/src/libs/qmljs/qmljs-lib.pri b/src/libs/qmljs/qmljs-lib.pri index d63d85bc21bff8f39c2ed75b286ee108acb30212..4d450da21b6e88f61970e0485423328b48a7a6fb 100644 --- a/src/libs/qmljs/qmljs-lib.pri +++ b/src/libs/qmljs/qmljs-lib.pri @@ -22,7 +22,8 @@ HEADERS += \ $$PWD/qmljsscanner.h \ $$PWD/qmljssymbol.h \ $$PWD/qmljstypesystem.h \ - $$PWD/qmljsinterpreter.h + $$PWD/qmljsinterpreter.h \ + $$PWD/qmljsmetatypesystem.h SOURCES += \ $$PWD/qmljsbind.cpp \ @@ -34,7 +35,8 @@ SOURCES += \ $$PWD/qmljsscanner.cpp \ $$PWD/qmljssymbol.cpp \ $$PWD/qmljstypesystem.cpp \ - $$PWD/qmljsinterpreter.cpp + $$PWD/qmljsinterpreter.cpp \ + $$PWD/qmljsmetatypesystem.cpp contains(QT_CONFIG, declarative) { QT += declarative diff --git a/src/libs/qmljs/qmljsbind.cpp b/src/libs/qmljs/qmljsbind.cpp index 299b13179e14d717cf8e8699dfcc0ace3f3d8225..0354c9992acbf2133deba7f7581748abac8c3a83 100644 --- a/src/libs/qmljs/qmljsbind.cpp +++ b/src/libs/qmljs/qmljsbind.cpp @@ -27,10 +27,13 @@ ** **************************************************************************/ -#include "qmljsbind.h" #include "parser/qmljsast_p.h" +#include "qmljsbind.h" +#include "qmljsmetatypesystem.h" using namespace QmlJS; +using namespace QmlJS::AST; +using namespace QmlJS::Interpreter; Bind::Bind() { @@ -40,462 +43,629 @@ Bind::~Bind() { } -void Bind::operator()(Document::Ptr doc) +Interpreter::ObjectValue *Bind::operator()(Document::Ptr doc, Snapshot &snapshot, UiObjectMember *member, Interpreter::Engine &interp) { + UiProgram *program = doc->qmlProgram(); + if (!program) + return 0; + _doc = doc; + _snapshot = &snapshot; + _interestingMember = member; + _interp = &interp; + + _currentObjectValue = 0; + _typeEnvironment = _interp->newObject(0); + _idEnvironment = _interp->newObject(0); + _interestingObjectValue = 0; + _rootObjectValue = 0; + + accept(program); + + if (_interestingObjectValue) { + _idEnvironment->setScope(_interestingObjectValue); + + if (_interestingObjectValue != _rootObjectValue) + _interestingObjectValue->setScope(_rootObjectValue); + } else { + _idEnvironment->setScope(_rootObjectValue); + } + _typeEnvironment->setScope(_idEnvironment); + + return _typeEnvironment; } -void Bind::accept(AST::Node *node) +void Bind::accept(Node *node) { - AST::Node::accept(node, this); + Node::accept(node, this); } -bool Bind::visit(AST::UiProgram *) +bool Bind::visit(UiProgram *) { return true; } -bool Bind::visit(AST::UiImportList *) +bool Bind::visit(UiImportList *) { return true; } -bool Bind::visit(AST::UiImport *) +static QString serialize(UiQualifiedId *qualifiedId, QChar delimiter) { - return true; + QString result; + + for (UiQualifiedId *iter = qualifiedId; iter; iter = iter->next) { + if (iter != qualifiedId) + result += delimiter; + + if (iter->name) + result += iter->name->asString(); + } + + return result; } -bool Bind::visit(AST::UiPublicMember *) +/* + import Qt 4.6 + import Qt 4.6 as Xxx + (import com.nokia.qt is the same as the ones above) + + import "content" + import "content" as Xxx + import "content" 4.6 + import "content" 4.6 as Xxx + + import "http://www.ovi.com/" as Ovi + */ +bool Bind::visit(UiImport *ast) { - return true; + ObjectValue *namespaceObject; + + if (ast->asToken.isValid()) { // with namespace we insert an object in the type env. to hold the imported types + namespaceObject = _interp->newObject(0); + if (!ast->importId) + return false; // this should never happen, but better be safe than sorry + _typeEnvironment->setProperty(ast->importId->asString(), namespaceObject); + } else { // without namespace we insert all types directly into the type env. + namespaceObject = _typeEnvironment; + } + + // look at files first + + // else try the metaobject system + if (!ast->importUri) + return false; + + const QString package = serialize(ast->importUri, '/'); + int majorVersion = -1; // ### TODO: Check these magic version numbers + int minorVersion = -1; // ### TODO: Check these magic version numbers + + if (ast->versionToken.isValid()) { + const QString versionString = _doc->source().mid(ast->versionToken.offset, ast->versionToken.length); + int dotIdx = versionString.indexOf('.'); + if (dotIdx == -1) { + // only major (which is probably invalid, but let's handle it anyway) + majorVersion = versionString.toInt(); + minorVersion = 0; // ### TODO: Check with magic version numbers above + } else { + majorVersion = versionString.left(dotIdx).toInt(); + minorVersion = versionString.mid(dotIdx + 1).toInt(); + } + } + +#ifndef NO_DECLARATIVE_BACKEND + foreach (QmlObjectValue *object, _interp->metaTypeSystem().staticTypesForImport(package, majorVersion, minorVersion)) { + namespaceObject->setProperty(object->qmlTypeName(), object); + } +#endif // NO_DECLARATIVE_BACKEND + + return false; } -bool Bind::visit(AST::UiSourceElement *) +bool Bind::visit(UiPublicMember *ast) { - return true; + if (! (ast->name && ast->memberType)) + return false; + + const QString propName = ast->name->asString(); + const QString propType = ast->memberType->asString(); + + // ### TODO: generalize + if (propType == QLatin1String("string")) + _currentObjectValue->setProperty(propName, _interp->stringValue()); + else if (propType == QLatin1String("bool")) + _currentObjectValue->setProperty(propName, _interp->booleanValue()); + else if (propType == QLatin1String("int") || propType == QLatin1String("real")) + _currentObjectValue->setProperty(propName, _interp->numberValue()); + + return false; } -bool Bind::visit(AST::UiObjectDefinition *) +bool Bind::visit(UiSourceElement *) { return true; } -bool Bind::visit(AST::UiObjectInitializer *) +const ObjectValue *Bind::lookupType(UiQualifiedId *qualifiedTypeNameId) { - return true; + const ObjectValue *objectValue = _typeEnvironment; + + for (UiQualifiedId *iter = qualifiedTypeNameId; iter; iter = iter->next) { + if (! (iter->name)) + return 0; + const Value *value = objectValue->property(iter->name->asString()); + if (!value) + return 0; + objectValue = value->asObjectValue(); + if (!objectValue) + return 0; + } + + return objectValue; } -bool Bind::visit(AST::UiObjectBinding *) +ObjectValue *Bind::bindObject(UiQualifiedId *qualifiedTypeNameId, UiObjectInitializer *initializer) { - return true; + const ObjectValue *prototype = lookupType(qualifiedTypeNameId); + ObjectValue *objectValue = _interp->newObject(prototype); + ObjectValue *oldObjectValue = switchObjectValue(objectValue); + if (oldObjectValue) + objectValue->setProperty("parent", oldObjectValue); + else + _rootObjectValue = objectValue; + + accept(initializer); + + return switchObjectValue(oldObjectValue); +} + +bool Bind::visit(UiObjectDefinition *ast) +{ + ObjectValue *value = bindObject(ast->qualifiedTypeNameId, ast->initializer); + + if (_interestingMember == ast) + _interestingObjectValue = value; + return false; } -bool Bind::visit(AST::UiScriptBinding *) +bool Bind::visit(UiObjectBinding *ast) +{ +// const QString name = serialize(ast->qualifiedId); + ObjectValue *value = bindObject(ast->qualifiedTypeNameId, ast->initializer); + // ### FIXME: we don't handle dot-properties correctly (i.e. font.size) +// _currentObjectValue->setProperty(name, value); + + if (_interestingMember == ast) + _interestingObjectValue = value; + return false; +} + +bool Bind::visit(UiObjectInitializer *) { return true; } -bool Bind::visit(AST::UiArrayBinding *) +bool Bind::visit(UiScriptBinding *ast) +{ + if (!(ast->qualifiedId->next) && ast->qualifiedId->name->asString() == "id") + if (ExpressionStatement *e = cast<ExpressionStatement*>(ast->statement)) + if (IdentifierExpression *i = cast<IdentifierExpression*>(e->expression)) + if (i->name) + _idEnvironment->setProperty(i->name->asString(), _currentObjectValue); + + return false; +} + +bool Bind::visit(UiArrayBinding *) { + // ### FIXME: do we need to store the members into the property? Or, maybe the property type is an JS Array? + return true; } -bool Bind::visit(AST::UiObjectMemberList *) +bool Bind::visit(UiObjectMemberList *) { return true; } -bool Bind::visit(AST::UiArrayMemberList *) +bool Bind::visit(UiArrayMemberList *) { return true; } -bool Bind::visit(AST::UiQualifiedId *) +bool Bind::visit(UiQualifiedId *) { return true; } -bool Bind::visit(AST::UiSignature *) +bool Bind::visit(UiSignature *) { return true; } -bool Bind::visit(AST::UiFormalList *) +bool Bind::visit(UiFormalList *) { return true; } -bool Bind::visit(AST::UiFormal *) +bool Bind::visit(UiFormal *) { return true; } -bool Bind::visit(AST::ThisExpression *) +bool Bind::visit(ThisExpression *) { return true; } -bool Bind::visit(AST::IdentifierExpression *) +bool Bind::visit(IdentifierExpression *) { return true; } -bool Bind::visit(AST::NullExpression *) +bool Bind::visit(NullExpression *) { return true; } -bool Bind::visit(AST::TrueLiteral *) +bool Bind::visit(TrueLiteral *) { return true; } -bool Bind::visit(AST::FalseLiteral *) +bool Bind::visit(FalseLiteral *) { return true; } -bool Bind::visit(AST::StringLiteral *) +bool Bind::visit(StringLiteral *) { return true; } -bool Bind::visit(AST::NumericLiteral *) +bool Bind::visit(NumericLiteral *) { return true; } -bool Bind::visit(AST::RegExpLiteral *) +bool Bind::visit(RegExpLiteral *) { return true; } -bool Bind::visit(AST::ArrayLiteral *) +bool Bind::visit(ArrayLiteral *) { return true; } -bool Bind::visit(AST::ObjectLiteral *) +bool Bind::visit(ObjectLiteral *) { return true; } -bool Bind::visit(AST::ElementList *) +bool Bind::visit(ElementList *) { return true; } -bool Bind::visit(AST::Elision *) +bool Bind::visit(Elision *) { return true; } -bool Bind::visit(AST::PropertyNameAndValueList *) +bool Bind::visit(PropertyNameAndValueList *) { return true; } -bool Bind::visit(AST::NestedExpression *) +bool Bind::visit(NestedExpression *) { return true; } -bool Bind::visit(AST::IdentifierPropertyName *) +bool Bind::visit(IdentifierPropertyName *) { return true; } -bool Bind::visit(AST::StringLiteralPropertyName *) +bool Bind::visit(StringLiteralPropertyName *) { return true; } -bool Bind::visit(AST::NumericLiteralPropertyName *) +bool Bind::visit(NumericLiteralPropertyName *) { return true; } -bool Bind::visit(AST::ArrayMemberExpression *) +bool Bind::visit(ArrayMemberExpression *) { return true; } -bool Bind::visit(AST::FieldMemberExpression *) +bool Bind::visit(FieldMemberExpression *) { return true; } -bool Bind::visit(AST::NewMemberExpression *) +bool Bind::visit(NewMemberExpression *) { return true; } -bool Bind::visit(AST::NewExpression *) +bool Bind::visit(NewExpression *) { return true; } -bool Bind::visit(AST::CallExpression *) +bool Bind::visit(CallExpression *) { return true; } -bool Bind::visit(AST::ArgumentList *) +bool Bind::visit(ArgumentList *) { return true; } -bool Bind::visit(AST::PostIncrementExpression *) +bool Bind::visit(PostIncrementExpression *) { return true; } -bool Bind::visit(AST::PostDecrementExpression *) +bool Bind::visit(PostDecrementExpression *) { return true; } -bool Bind::visit(AST::DeleteExpression *) +bool Bind::visit(DeleteExpression *) { return true; } -bool Bind::visit(AST::VoidExpression *) +bool Bind::visit(VoidExpression *) { return true; } -bool Bind::visit(AST::TypeOfExpression *) +bool Bind::visit(TypeOfExpression *) { return true; } -bool Bind::visit(AST::PreIncrementExpression *) +bool Bind::visit(PreIncrementExpression *) { return true; } -bool Bind::visit(AST::PreDecrementExpression *) +bool Bind::visit(PreDecrementExpression *) { return true; } -bool Bind::visit(AST::UnaryPlusExpression *) +bool Bind::visit(UnaryPlusExpression *) { return true; } -bool Bind::visit(AST::UnaryMinusExpression *) +bool Bind::visit(UnaryMinusExpression *) { return true; } -bool Bind::visit(AST::TildeExpression *) +bool Bind::visit(TildeExpression *) { return true; } -bool Bind::visit(AST::NotExpression *) +bool Bind::visit(NotExpression *) { return true; } -bool Bind::visit(AST::BinaryExpression *) +bool Bind::visit(BinaryExpression *) { return true; } -bool Bind::visit(AST::ConditionalExpression *) +bool Bind::visit(ConditionalExpression *) { return true; } -bool Bind::visit(AST::Expression *) +bool Bind::visit(Expression *) { return true; } -bool Bind::visit(AST::Block *) +bool Bind::visit(Block *) { return true; } -bool Bind::visit(AST::StatementList *) +bool Bind::visit(StatementList *) { return true; } -bool Bind::visit(AST::VariableStatement *) +bool Bind::visit(VariableStatement *) { return true; } -bool Bind::visit(AST::VariableDeclarationList *) +bool Bind::visit(VariableDeclarationList *) { return true; } -bool Bind::visit(AST::VariableDeclaration *) +bool Bind::visit(VariableDeclaration *) { return true; } -bool Bind::visit(AST::EmptyStatement *) +bool Bind::visit(EmptyStatement *) { return true; } -bool Bind::visit(AST::ExpressionStatement *) +bool Bind::visit(ExpressionStatement *) { return true; } -bool Bind::visit(AST::IfStatement *) +bool Bind::visit(IfStatement *) { return true; } -bool Bind::visit(AST::DoWhileStatement *) +bool Bind::visit(DoWhileStatement *) { return true; } -bool Bind::visit(AST::WhileStatement *) +bool Bind::visit(WhileStatement *) { return true; } -bool Bind::visit(AST::ForStatement *) +bool Bind::visit(ForStatement *) { return true; } -bool Bind::visit(AST::LocalForStatement *) +bool Bind::visit(LocalForStatement *) { return true; } -bool Bind::visit(AST::ForEachStatement *) +bool Bind::visit(ForEachStatement *) { return true; } -bool Bind::visit(AST::LocalForEachStatement *) +bool Bind::visit(LocalForEachStatement *) { return true; } -bool Bind::visit(AST::ContinueStatement *) +bool Bind::visit(ContinueStatement *) { return true; } -bool Bind::visit(AST::BreakStatement *) +bool Bind::visit(BreakStatement *) { return true; } -bool Bind::visit(AST::ReturnStatement *) +bool Bind::visit(ReturnStatement *) { return true; } -bool Bind::visit(AST::WithStatement *) +bool Bind::visit(WithStatement *) { return true; } -bool Bind::visit(AST::SwitchStatement *) +bool Bind::visit(SwitchStatement *) { return true; } -bool Bind::visit(AST::CaseBlock *) +bool Bind::visit(CaseBlock *) { return true; } -bool Bind::visit(AST::CaseClauses *) +bool Bind::visit(CaseClauses *) { return true; } -bool Bind::visit(AST::CaseClause *) +bool Bind::visit(CaseClause *) { return true; } -bool Bind::visit(AST::DefaultClause *) +bool Bind::visit(DefaultClause *) { return true; } -bool Bind::visit(AST::LabelledStatement *) +bool Bind::visit(LabelledStatement *) { return true; } -bool Bind::visit(AST::ThrowStatement *) +bool Bind::visit(ThrowStatement *) { return true; } -bool Bind::visit(AST::TryStatement *) +bool Bind::visit(TryStatement *) { return true; } -bool Bind::visit(AST::Catch *) +bool Bind::visit(Catch *) { return true; } -bool Bind::visit(AST::Finally *) +bool Bind::visit(Finally *) { return true; } -bool Bind::visit(AST::FunctionDeclaration *) +bool Bind::visit(FunctionDeclaration *) { return true; } -bool Bind::visit(AST::FunctionExpression *) +bool Bind::visit(FunctionExpression *) { return true; } -bool Bind::visit(AST::FormalParameterList *) +bool Bind::visit(FormalParameterList *) { return true; } -bool Bind::visit(AST::FunctionBody *) +bool Bind::visit(FunctionBody *) { return true; } -bool Bind::visit(AST::Program *) +bool Bind::visit(Program *) { return true; } -bool Bind::visit(AST::SourceElements *) +bool Bind::visit(SourceElements *) { return true; } -bool Bind::visit(AST::FunctionSourceElement *) +bool Bind::visit(FunctionSourceElement *) { return true; } -bool Bind::visit(AST::StatementSourceElement *) +bool Bind::visit(StatementSourceElement *) { return true; } -bool Bind::visit(AST::DebuggerStatement *) +bool Bind::visit(DebuggerStatement *) { return true; } + +ObjectValue *Bind::switchObjectValue(ObjectValue *newObjectValue) +{ + ObjectValue *oldObjectValue = _currentObjectValue; + _currentObjectValue = newObjectValue; + return oldObjectValue; +} diff --git a/src/libs/qmljs/qmljsbind.h b/src/libs/qmljs/qmljsbind.h index 869c61ef780b66dd342ea4e0fd00b27fa836c365..cb89e2634f2b687c8c1d01765574919da19c8bc0 100644 --- a/src/libs/qmljs/qmljsbind.h +++ b/src/libs/qmljs/qmljsbind.h @@ -30,8 +30,9 @@ #ifndef QMLBIND_H #define QMLBIND_H -#include "parser/qmljsastvisitor_p.h" -#include "qmljsdocument.h" +#include <qmljs/parser/qmljsastvisitor_p.h> +#include <qmljs/qmljsdocument.h> +#include <qmljs/qmljsinterpreter.h> namespace QmlJS { @@ -41,7 +42,7 @@ public: Bind(); virtual ~Bind(); - void operator()(QmlJS::Document::Ptr doc); + Interpreter::ObjectValue* operator()(Document::Ptr doc, Snapshot &snapshot, AST::UiObjectMember *member, Interpreter::Engine &interp); protected: void accept(AST::Node *node); @@ -140,8 +141,23 @@ protected: virtual bool visit(AST::StatementSourceElement *ast); virtual bool visit(AST::DebuggerStatement *ast); +protected: + Interpreter::ObjectValue *switchObjectValue(Interpreter::ObjectValue *newObjectValue); + const Interpreter::ObjectValue *lookupType(AST::UiQualifiedId *qualifiedTypeNameId); + Interpreter::ObjectValue *bindObject(AST::UiQualifiedId *qualifiedTypeNameId, AST::UiObjectInitializer *initializer); + private: - QmlJS::Document::Ptr _doc; + Document::Ptr _doc; + Snapshot *_snapshot; + AST::UiObjectMember *_interestingMember; + Interpreter::Engine *_interp; + + Interpreter::ObjectValue *_currentObjectValue; + + Interpreter::ObjectValue *_typeEnvironment; + Interpreter::ObjectValue *_idEnvironment; + Interpreter::ObjectValue *_interestingObjectValue; + Interpreter::ObjectValue *_rootObjectValue; }; } // end of namespace Qml diff --git a/src/libs/qmljs/qmljsdocument.cpp b/src/libs/qmljs/qmljsdocument.cpp index 539961d32af35033569fe71aaccbafebcd0cceff..ff736fff68f7c1e0d6f0d406e8aa437503e7013b 100644 --- a/src/libs/qmljs/qmljsdocument.cpp +++ b/src/libs/qmljs/qmljsdocument.cpp @@ -225,6 +225,8 @@ void Snapshot::insert(const Document::Ptr &document) Document::PtrList Snapshot::importedDocuments(const Document::Ptr &doc, const QString &importPath) const { + // ### TODO: maybe we should add all imported documents in the parse Document::parse() method, regardless of whether they're in the path or not. + Document::PtrList result; const QString docPath = doc->path() + '/' + importPath; diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index 4d664ee8cc9d62d73f6fc911b2a7cf8bfdcd4360..1389d4d01d7cb24b2481e7bb91dd0bee0c867cf2 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -43,8 +43,6 @@ using namespace QmlJS::Interpreter; -namespace { - #ifndef NO_DECLARATIVE_BACKEND class QmlAttachedKeys: public ObjectValue @@ -166,93 +164,102 @@ public: } }; -class QmlObjectValue: public ObjectValue -{ -public: - QmlObjectValue(const QMetaObject *metaObject, Engine *engine) - : ObjectValue(engine), _metaObject(metaObject) - { - } +QmlObjectValue::QmlObjectValue(const QMetaObject *metaObject, const QString &qmlTypeName, int majorVersion, int minorVersion, Engine *engine) + : ObjectValue(engine), _metaObject(metaObject), _qmlTypeName(qmlTypeName), _majorVersion(majorVersion), _minorVersion(minorVersion) +{} - virtual ~QmlObjectValue() {} +QmlObjectValue::~QmlObjectValue() {} - virtual const Value *lookupMember(const QString &name) const - { - for (int index = 0; index < _metaObject->propertyCount(); ++index) { - QMetaProperty prop = _metaObject->property(index); +const Value *QmlObjectValue::lookupMember(const QString &name) const +{ + for (int index = 0; index < _metaObject->propertyCount(); ++index) { + QMetaProperty prop = _metaObject->property(index); - if (name == QString::fromUtf8(prop.name())) - return propertyValue(prop); - } + if (name == QString::fromUtf8(prop.name())) + return propertyValue(prop); + } - for (int index = 0; index < _metaObject->methodCount(); ++index) { - QMetaMethod method = _metaObject->method(index); + for (int index = 0; index < _metaObject->methodCount(); ++index) { + QMetaMethod method = _metaObject->method(index); - const QString signature = QString::fromUtf8(method.signature()); + const QString signature = QString::fromUtf8(method.signature()); - const int indexOfParen = signature.indexOf(QLatin1Char('(')); - if (indexOfParen == -1) - continue; // skip it, invalid signature. + const int indexOfParen = signature.indexOf(QLatin1Char('(')); + if (indexOfParen == -1) + continue; // skip it, invalid signature. - const QString methodName = signature.left(indexOfParen); + const QString methodName = signature.left(indexOfParen); - if (methodName != name) { - continue; + if (methodName != name) { + continue; - } else if (method.methodType() == QMetaMethod::Slot && method.access() == QMetaMethod::Public) { - return new MetaFunction(method, engine()); + } else if (method.methodType() == QMetaMethod::Slot && method.access() == QMetaMethod::Public) { + return new MetaFunction(method, engine()); - } else if (method.methodType() == QMetaMethod::Signal && method.access() != QMetaMethod::Private) { - return new MetaFunction(method, engine()); - } + } else if (method.methodType() == QMetaMethod::Signal && method.access() != QMetaMethod::Private) { + return new MetaFunction(method, engine()); } - - return ObjectValue::lookupMember(name); } - virtual void processMembers(MemberProcessor *processor) const - { - for (int index = 0; index < _metaObject->propertyCount(); ++index) { - QMetaProperty prop = _metaObject->property(index); + return ObjectValue::lookupMember(name); +} - processor->processProperty(prop.name(), propertyValue(prop)); - } +void QmlObjectValue::processMembers(MemberProcessor *processor) const +{ + for (int index = 0; index < _metaObject->propertyCount(); ++index) { + QMetaProperty prop = _metaObject->property(index); - for (int index = 0; index < _metaObject->methodCount(); ++index) { - QMetaMethod method = _metaObject->method(index); + processor->processProperty(prop.name(), propertyValue(prop)); + } - const QString signature = QString::fromUtf8(method.signature()); + for (int index = 0; index < _metaObject->methodCount(); ++index) { + QMetaMethod method = _metaObject->method(index); - const int indexOfParen = signature.indexOf(QLatin1Char('(')); - if (indexOfParen == -1) - continue; // skip it, invalid signature. + const QString signature = QString::fromUtf8(method.signature()); - const QString methodName = signature.left(indexOfParen); + const int indexOfParen = signature.indexOf(QLatin1Char('(')); + if (indexOfParen == -1) + continue; // skip it, invalid signature. - if (method.methodType() == QMetaMethod::Slot && method.access() == QMetaMethod::Public) { - processor->processSlot(methodName, engine()->undefinedValue()); + const QString methodName = signature.left(indexOfParen); - } else if (method.methodType() == QMetaMethod::Signal && method.access() != QMetaMethod::Private) { - // process the signal - processor->processSignal(methodName, engine()->undefinedValue()); + if (method.methodType() == QMetaMethod::Slot && method.access() == QMetaMethod::Public) { + processor->processSlot(methodName, engine()->undefinedValue()); - QString slotName; - slotName += QLatin1String("on"); - slotName += methodName.at(0).toUpper(); - slotName += methodName.midRef(1); + } else if (method.methodType() == QMetaMethod::Signal && method.access() != QMetaMethod::Private) { + // process the signal + processor->processSignal(methodName, engine()->undefinedValue()); - // process the generated slot - processor->processGeneratedSlot(slotName, engine()->undefinedValue()); - } - } + QString slotName; + slotName += QLatin1String("on"); + slotName += methodName.at(0).toUpper(); + slotName += methodName.midRef(1); - ObjectValue::processMembers(processor); + // process the generated slot + processor->processGeneratedSlot(slotName, engine()->undefinedValue()); + } } - const Value *propertyValue(const QMetaProperty &prop) const { - const Value *value = engine()->undefinedValue(); + ObjectValue::processMembers(processor); +} + +const Value *QmlObjectValue::propertyValue(const QMetaProperty &prop) const +{ + if (QmlMetaType::isObject(prop.userType())) { + QmlType *qmlPropertyType = QmlMetaType::qmlType(QmlMetaType::metaObjectForType(prop.userType())); - if (QmlMetaType::isObject(prop.userType())) { + if (qmlPropertyType && !qmlPropertyType->qmlTypeName().isEmpty()) { + QString typeName = qmlPropertyType->qmlTypeName(); + int slashIdx = typeName.lastIndexOf(QLatin1Char('/')); + QString package; + if (slashIdx != -1) { + package = typeName.left(slashIdx); + typeName = typeName.mid(slashIdx + 1); + } + + if (const ObjectValue *objectValue = engine()->newQmlObject(typeName, package, qmlPropertyType->majorVersion(), qmlPropertyType->minorVersion())) + return objectValue; + } else { QString typeName = QString::fromUtf8(prop.typeName()); if (typeName.endsWith(QLatin1Char('*'))) @@ -260,78 +267,78 @@ public: typeName.replace(QLatin1Char('.'), QLatin1Char('/')); - if (const ObjectValue *objectValue = engine()->newQmlObject(typeName)) + if (const ObjectValue *objectValue = engine()->newQmlObject(typeName, "", -1, -1)) // ### we should extend this to lookup the property types in the QmlType object, instead of the QMetaProperty. return objectValue; } - - switch (prop.type()) { - case QMetaType::QByteArray: - case QMetaType::QString: - case QMetaType::QUrl: - value = engine()->stringValue(); - break; - - case QMetaType::Bool: - value = engine()->booleanValue(); - break; - - case QMetaType::Int: - case QMetaType::Float: - case QMetaType::Double: - value = engine()->numberValue(); - break; - - case QMetaType::QFont: { - // ### cache - ObjectValue *object = engine()->newObject(/*prototype =*/ 0); - object->setProperty("family", engine()->stringValue()); - object->setProperty("weight", engine()->undefinedValue()); // ### make me an object - object->setProperty("copitalization", engine()->undefinedValue()); // ### make me an object - object->setProperty("bold", engine()->booleanValue()); - object->setProperty("italic", engine()->booleanValue()); - object->setProperty("underline", engine()->booleanValue()); - object->setProperty("overline", engine()->booleanValue()); - object->setProperty("strikeout", engine()->booleanValue()); - object->setProperty("pointSize", engine()->numberValue()); - object->setProperty("pixelSize", engine()->numberValue()); - object->setProperty("letterSpacing", engine()->numberValue()); - object->setProperty("wordSpacing", engine()->numberValue()); - value = object; - } break; - - case QMetaType::QPoint: - case QMetaType::QPointF: { - // ### cache - ObjectValue *object = engine()->newObject(/*prototype =*/ 0); - object->setProperty("x", engine()->numberValue()); - object->setProperty("y", engine()->numberValue()); - value = object; - } break; - - case QMetaType::QRect: - case QMetaType::QRectF: { - // ### cache - ObjectValue *object = engine()->newObject(/*prototype =*/ 0); - object->setProperty("x", engine()->numberValue()); - object->setProperty("y", engine()->numberValue()); - object->setProperty("width", engine()->numberValue()); - object->setProperty("height", engine()->numberValue()); - value = object; - } break; - - default: - break; - } // end of switch - - return value; } -private: - const QMetaObject *_metaObject; -}; - + const Value *value = engine()->undefinedValue(); + + switch (prop.type()) { + case QMetaType::QByteArray: + case QMetaType::QString: + case QMetaType::QUrl: + value = engine()->stringValue(); + break; + + case QMetaType::Bool: + value = engine()->booleanValue(); + break; + + case QMetaType::Int: + case QMetaType::Float: + case QMetaType::Double: + value = engine()->numberValue(); + break; + + case QMetaType::QFont: { + // ### cache + ObjectValue *object = engine()->newObject(/*prototype =*/ 0); + object->setProperty("family", engine()->stringValue()); + object->setProperty("weight", engine()->undefinedValue()); // ### make me an object + object->setProperty("copitalization", engine()->undefinedValue()); // ### make me an object + object->setProperty("bold", engine()->booleanValue()); + object->setProperty("italic", engine()->booleanValue()); + object->setProperty("underline", engine()->booleanValue()); + object->setProperty("overline", engine()->booleanValue()); + object->setProperty("strikeout", engine()->booleanValue()); + object->setProperty("pointSize", engine()->numberValue()); + object->setProperty("pixelSize", engine()->numberValue()); + object->setProperty("letterSpacing", engine()->numberValue()); + object->setProperty("wordSpacing", engine()->numberValue()); + value = object; + } break; + + case QMetaType::QPoint: + case QMetaType::QPointF: { + // ### cache + ObjectValue *object = engine()->newObject(/*prototype =*/ 0); + object->setProperty("x", engine()->numberValue()); + object->setProperty("y", engine()->numberValue()); + value = object; + } break; + + case QMetaType::QRect: + case QMetaType::QRectF: { + // ### cache + ObjectValue *object = engine()->newObject(/*prototype =*/ 0); + object->setProperty("x", engine()->numberValue()); + object->setProperty("y", engine()->numberValue()); + object->setProperty("width", engine()->numberValue()); + object->setProperty("height", engine()->numberValue()); + value = object; + } break; + + default: + break; + } // end of switch + + return value; +} #endif +namespace { + //////////////////////////////////////////////////////////////////////////////// // constructors //////////////////////////////////////////////////////////////////////////////// @@ -1300,8 +1307,9 @@ Engine::Engine() _convertToString(this), _convertToObject(this) { - initializePrototypes(); + + _metaTypeSystem.reload(this); } Engine::~Engine() @@ -1780,19 +1788,19 @@ const Value *Engine::defaultValueForBuiltinType(const QString &typeName) const return undefinedValue(); } -ObjectValue *Engine::newQmlObject(const QString &name) -{ #ifndef NO_DECLARATIVE_BACKEND +QmlObjectValue *Engine::newQmlObject(const QString &name, const QString &prefix, int majorVersion, int minorVersion) +{ if (name == QLatin1String("QmlGraphicsAnchors")) { - QmlObjectValue *object = new QmlObjectValue(&QmlGraphicsAnchors::staticMetaObject, this); + QmlObjectValue *object = new QmlObjectValue(&QmlGraphicsAnchors::staticMetaObject, QLatin1String("Anchors"), -1, -1, this); _objects.append(object); return object; } else if (name == QLatin1String("QmlGraphicsPen")) { - QmlObjectValue *object = new QmlObjectValue(&QmlGraphicsPen::staticMetaObject, this); + QmlObjectValue *object = new QmlObjectValue(&QmlGraphicsPen::staticMetaObject, QLatin1String("Pen"), -1, -1, this); _objects.append(object); return object; } else if (name == QLatin1String("QmlGraphicsScaleGrid")) { - ObjectValue *object = newObject(/*prototype =*/ 0); + QmlObjectValue *object = new QmlObjectValue(&QObject::staticMetaObject, QLatin1String("ScaleGrid"), -1, -1, this); object->setProperty("left", numberValue()); object->setProperty("top", numberValue()); object->setProperty("right", numberValue()); @@ -1801,20 +1809,17 @@ ObjectValue *Engine::newQmlObject(const QString &name) } // ### TODO: add support for QML packages - QString componentName; - componentName += QLatin1String("Qt/"); - componentName += name; - componentName.replace(QLatin1Char('.'), QLatin1Char('/')); + const QString componentName = prefix + QLatin1Char('/') + name; - if (QmlType *qmlType = QmlMetaType::qmlType(componentName.toUtf8(), 4, 6)) { - QmlObjectValue *object = new QmlObjectValue(qmlType->metaObject(), this); + if (QmlType *qmlType = QmlMetaType::qmlType(componentName.toUtf8(), majorVersion, minorVersion)) { + const QString typeName = qmlType->qmlTypeName(); + const QString strippedTypeName = typeName.mid(typeName.lastIndexOf('/') + 1); + QmlObjectValue *object = new QmlObjectValue(qmlType->metaObject(), strippedTypeName, majorVersion, minorVersion, this); _objects.append(object); return object; } return 0; -#else - return newObject(/*prototype = */ 0); -#endif } +#endif diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index 59716961ba6b11bf88a668040d960837c73d932d..ea61857770c91017ef22248f296e85948d7cd4fd 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -30,7 +30,8 @@ #ifndef QMLJS_INTERPRETER_H #define QMLJS_INTERPRETER_H -#include "qmljs_global.h" +#include <qmljs/qmljs_global.h> +#include <qmljs/qmljsmetatypesystem.h> #include <QtCore/QList> #include <QtCore/QString> @@ -250,6 +251,36 @@ private: QString _className; }; +#ifndef NO_DECLARATIVE_BACKEND + +class QmlObjectValue: public ObjectValue +{ +public: + QmlObjectValue(const QMetaObject *metaObject, const QString &qmlTypeName, int majorVersion, int minorVersion, Engine *engine); + virtual ~QmlObjectValue(); + + virtual const Value *lookupMember(const QString &name) const; + virtual void processMembers(MemberProcessor *processor) const; + const Value *propertyValue(const QMetaProperty &prop) const; + + QString qmlTypeName() const + { return _qmlTypeName; } + + int majorVersion() const + { return _majorVersion; } + + int minorVersion() const + { return _minorVersion; } + +private: + const QMetaObject *_metaObject; + QString _qmlTypeName; + int _majorVersion; + int _minorVersion; +}; + +#endif // !NO_DECLARATIVE_BACKEND + class QMLJS_EXPORT Activation: public ObjectValue { public: @@ -440,9 +471,11 @@ public: const Value *newArray(); // ### remove me // QML objects - ObjectValue *newQmlObject(const QString &name); const ObjectValue *qmlKeysObject(); const Value *defaultValueForBuiltinType(const QString &typeName) const; +#ifndef NO_DECLARATIVE_BACKEND + QmlObjectValue *newQmlObject(const QString &name, const QString &prefix, int majorVersion, int minorVersion); +#endif // global object ObjectValue *globalObject() const; @@ -477,6 +510,11 @@ public: const Value *convertToObject(const Value *value); QString typeId(const Value *value); + // typing: + const MetaTypeSystem &metaTypeSystem() const + { return _metaTypeSystem; } + + private: void initializePrototypes(); @@ -519,6 +557,8 @@ private: ConvertToString _convertToString; ConvertToObject _convertToObject; TypeId _typeId; + + MetaTypeSystem _metaTypeSystem; }; } } // end of namespace QmlJS::Interpreter diff --git a/src/plugins/qmljseditor/qmlcodecompletion.cpp b/src/plugins/qmljseditor/qmlcodecompletion.cpp index de3faa557a7620ed3fb7cd1bddcc01047987ecdb..a48f63aa4c08939fa21033cfd64ec5bff31a877b 100644 --- a/src/plugins/qmljseditor/qmlcodecompletion.cpp +++ b/src/plugins/qmljseditor/qmlcodecompletion.cpp @@ -33,6 +33,7 @@ #include "qmllookupcontext.h" #include <qmljs/parser/qmljsast_p.h> +#include <qmljs/qmljsbind.h> #include <qmljs/qmljsinterpreter.h> #include <qmljs/qmljssymbol.h> #include <qmljs/qmljsscanner.h> @@ -112,6 +113,7 @@ static QString qualifiedNameId(AST::UiQualifiedId *it) return text; } +#if 0 static Interpreter::ObjectValue *newComponent(Interpreter::Engine *engine, const QString &name, const QHash<QString, Document::Ptr> &userComponents, QSet<QString> *processed) @@ -157,6 +159,7 @@ static Interpreter::ObjectValue *newComponent(Interpreter::Engine *engine, QSet<QString> processed; return newComponent(engine, name, userComponents, &processed); } +#endif namespace { @@ -688,6 +691,8 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) SemanticInfo semanticInfo = edit->semanticInfo(); Document::Ptr qmlDocument = semanticInfo.document; + if (qmlDocument.isNull()) + return -1; const QFileInfo currentFileInfo(fileName); const QString currentFilePath = currentFileInfo.absolutePath(); @@ -701,7 +706,7 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) Interpreter::Engine interp; - QHash<QString, Document::Ptr> userComponents; + QHash<QString, Document::Ptr> userComponents; // #### foreach (Document::Ptr doc, snapshot) { const QFileInfo fileInfo(doc->fileName()); @@ -730,6 +735,7 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) else if (absolutePath != currentFilePath && ! isImported(qmlDocument, absolutePath)) continue; +#if 0 QMapIterator<QString, IdSymbol *> it(doc->ids()); while (it.hasNext()) { it.next(); @@ -746,6 +752,7 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) } } } +#endif } // Set up the current scope chain. @@ -765,6 +772,7 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) } } +#if 0 // ### TODO: remove me. This is just a quick and dirty hack to get some completion // for the property definitions. SearchPropertyDefinitions searchPropertyDefinitions; @@ -811,6 +819,10 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) if (parentItem) scope->setProperty(QLatin1String("parent"), parentItem); +#endif + + Bind bind; + scope = bind(qmlDocument, snapshot, declaringMember, interp); } // Search for the operator that triggered the completion. diff --git a/tests/auto/qml/qmleditor/lookup/lookup.pro b/tests/auto/qml/qmleditor/lookup/lookup.pro index c4f210ac940d0048976ddacb9ccbf851a7c8ef5a..1c7d8b2adcbc14ba1ee05cda5a15804cc99f039a 100644 --- a/tests/auto/qml/qmleditor/lookup/lookup.pro +++ b/tests/auto/qml/qmleditor/lookup/lookup.pro @@ -14,3 +14,9 @@ SOURCES += tst_lookup.cpp \ HEADERS += $$EDITOR_DIR/qmllookupcontext.h RESOURCES += testfiles.qrc + +OTHER_FILES += \ + data/localIdLookup.qml \ + data/localScriptMethodLookup.qml \ + data/localScopeLookup.qml \ + data/localRootLookup.qml