diff --git a/src/libs/qmljs/qmljsbind.cpp b/src/libs/qmljs/qmljsbind.cpp index c7e387bd9a1a5731513012a40e229ad4e4ff775f..5076ef912162b12194db7fcb7f8d935b1b0cc25f 100644 --- a/src/libs/qmljs/qmljsbind.cpp +++ b/src/libs/qmljs/qmljsbind.cpp @@ -139,6 +139,9 @@ ObjectValue *Bind::bindObject(UiQualifiedId *qualifiedTypeNameId, UiObjectInitia // normal component instance ASTObjectValue *objectValue = new ASTObjectValue(qualifiedTypeNameId, initializer, &_interp); + QmlPrototypeReference *prototypeReference = new QmlPrototypeReference(qualifiedTypeNameId, &_interp); + objectValue->setPrototype(prototypeReference); + parentObjectValue = switchObjectValue(objectValue); if (parentObjectValue) @@ -241,9 +244,9 @@ bool Bind::visit(FunctionDeclaration *ast) { if (!ast->name) return false; - // the first declaration counts - if (_currentObjectValue->property(ast->name->asString())) - return false; + // ### FIXME: the first declaration counts + //if (_currentObjectValue->property(ast->name->asString(), 0)) + // return false; ASTFunctionValue *function = new ASTFunctionValue(ast, &_interp); // ### set the function's scope. diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index 31b4567af3e35a1be819d2142310cfed4865bef0..f25a7df52f0e68469b6f371f2420979237cd6555 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -177,7 +177,7 @@ bool Check::visit(AST::UiQualifiedId *ast) if (! name) break; - const Value *value = base->property(name->asString()); + const Value *value = base->property(name->asString(), _context); if (! it->next) _result = value; else @@ -311,7 +311,7 @@ bool Check::visit(AST::FieldMemberExpression *ast) if (const Interpreter::Value *base = _engine->convertToObject(check(ast->base))) { if (const Interpreter::ObjectValue *obj = base->asObjectValue()) { - _result = obj->property(ast->name->asString()); + _result = obj->property(ast->name->asString(), _context); } } diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index 0a650c98177f08ea6397b42ee9b36aa397f08141..9cbce73e0064b09a9bd5d2bd55e632c49f855861 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -162,9 +162,9 @@ QmlObjectValue::QmlObjectValue(const QMetaObject *metaObject, const QString &qml QmlObjectValue::~QmlObjectValue() {} -const Value *QmlObjectValue::lookupMember(const QString &name) const +const Value *QmlObjectValue::lookupMember(const QString &name, Context *context) const { - return ObjectValue::lookupMember(name); + return ObjectValue::lookupMember(name, context); } const Value *QmlObjectValue::findOrCreateSignature(int index, const QMetaMethod &method, QString *methodName) const @@ -708,6 +708,16 @@ void Context::setLookupMode(LookupMode lookupMode) _lookupMode = lookupMode; } +const ObjectValue *Context::typeEnvironment() const +{ + return _typeEnvironment; +} + +void Context::setTypeEnvironment(const ObjectValue *typeEnvironment) +{ + _typeEnvironment = typeEnvironment; +} + void Context::pushScope(const ObjectValue *object) { _scopeChain.append(object); @@ -718,12 +728,12 @@ void Context::popScope() _scopeChain.removeLast(); } -const Value *Context::lookup(const QString &name) const +const Value *Context::lookup(const QString &name) { for (int index = _scopeChain.size() - 1; index != -1; --index) { const ObjectValue *scope = _scopeChain.at(index); - if (const Value *member = scope->lookupMember(name)) { + if (const Value *member = scope->lookupMember(name, this)) { if (_lookupMode == JSLookup || ! dynamic_cast<const ASTVariableReference *>(member)) { return member; } @@ -733,6 +743,24 @@ const Value *Context::lookup(const QString &name) const return _engine->undefinedValue(); } +const ObjectValue *Context::lookupType(AST::UiQualifiedId *qmlTypeName) +{ + const ObjectValue *objectValue = _typeEnvironment; + + for (UiQualifiedId *iter = qmlTypeName; objectValue && iter; iter = iter->next) { + if (! iter->name) + return 0; + + const Value *value = objectValue->property(iter->name->asString(), this); + if (!value) + return 0; + + objectValue = value->asObjectValue(); + } + + return objectValue; +} + const Value *Context::property(const ObjectValue *object, const QString &name) const { const Properties properties = _properties.value(object); @@ -833,9 +861,21 @@ void ObjectValue::setClassName(const QString &className) _className = className; } -const ObjectValue *ObjectValue::prototype() const +const ObjectValue *ObjectValue::prototype(Context *context) const { - return _prototype; + const ObjectValue *prototypeObject = value_cast<const ObjectValue *>(_prototype); + if (! prototypeObject) { + if (const Reference *prototypeReference = value_cast<const Reference *>(_prototype)) { + prototypeObject = value_cast<const ObjectValue *>(prototypeReference->value(context)); + } + } + return prototypeObject; +} + +void ObjectValue::setPrototype(const Value *prototype) +{ + // ### FIXME: Check for cycles. + _prototype = prototype; } void ObjectValue::setProperty(const QString &name, const Value *value) @@ -858,23 +898,14 @@ void ObjectValue::accept(ValueVisitor *visitor) const visitor->visit(this); } -const Value *ObjectValue::property(const QString &name) const +const Value *ObjectValue::property(const QString &name, Context *context) const { - return lookupMember(name); -} - -void ObjectValue::setPrototype(const ObjectValue *prototype) -{ - QSet<const ObjectValue *> processed; - - if (! prototype || checkPrototype(prototype, &processed)) - _prototype = prototype; - else - qWarning() << "**** invalid prototype:"; + return lookupMember(name, context); } -bool ObjectValue::checkPrototype(const ObjectValue *proto, QSet<const ObjectValue *> *processed) const +bool ObjectValue::checkPrototype(const ObjectValue *, QSet<const ObjectValue *> *) const { +#if 0 const int previousSize = processed->size(); processed->insert(this); @@ -887,7 +918,7 @@ bool ObjectValue::checkPrototype(const ObjectValue *proto, QSet<const ObjectValu return true; } - +#endif return false; } @@ -903,7 +934,7 @@ void ObjectValue::processMembers(MemberProcessor *processor) const } } -const Value *ObjectValue::lookupMember(const QString &name) const +const Value *ObjectValue::lookupMember(const QString &name, Context *context) const { if (const Value *m = _members.value(name)) return m; @@ -914,17 +945,19 @@ const Value *ObjectValue::lookupMember(const QString &name) const return slowLookup.value(); } - if (_prototype) { - if (const Value *m = _prototype->lookupMember(name)) + const ObjectValue *prototypeObject = prototype(context); + if (prototypeObject) { + if (const Value *m = prototypeObject->lookupMember(name, context)) return m; } return 0; } -Activation::Activation() +Activation::Activation(Context *parentContext) : _thisObject(0), - _calledAsFunction(true) + _calledAsFunction(true), + _parentContext(parentContext) { } @@ -932,6 +965,17 @@ Activation::~Activation() { } +Context *Activation::parentContext() const +{ + return _parentContext; +} + +Context *Activation::context() const +{ + // ### FIXME: Real context for activations. + return 0; +} + bool Activation::calledAsConstructor() const { return ! _calledAsFunction; @@ -1083,12 +1127,12 @@ const Value *Function::argument(int index) const return _arguments.at(index); } -const Value *Function::property(const QString &name) const +const Value *Function::property(const QString &name, Context *context) const { if (name == "length") return engine()->numberValue(); - return FunctionValue::property(name); + return FunctionValue::property(name, context); } const Value *Function::invoke(const Activation *activation) const @@ -1148,14 +1192,14 @@ void ConvertToNumber::visit(const StringValue *) void ConvertToNumber::visit(const ObjectValue *object) { - if (const FunctionValue *valueOfMember = value_cast<const FunctionValue *>(object->lookupMember("valueOf"))) { + if (const FunctionValue *valueOfMember = value_cast<const FunctionValue *>(object->lookupMember("valueOf", 0))) { _result = value_cast<const NumberValue *>(valueOfMember->call(object)); // ### invoke convert-to-number? } } void ConvertToNumber::visit(const FunctionValue *object) { - if (const FunctionValue *valueOfMember = value_cast<const FunctionValue *>(object->lookupMember("valueOf"))) { + if (const FunctionValue *valueOfMember = value_cast<const FunctionValue *>(object->lookupMember("valueOf", 0))) { _result = value_cast<const NumberValue *>(valueOfMember->call(object)); // ### invoke convert-to-number? } } @@ -1209,14 +1253,14 @@ void ConvertToString::visit(const StringValue *value) void ConvertToString::visit(const ObjectValue *object) { - if (const FunctionValue *toStringMember = value_cast<const FunctionValue *>(object->lookupMember("toString"))) { + if (const FunctionValue *toStringMember = value_cast<const FunctionValue *>(object->lookupMember("toString", 0))) { _result = value_cast<const StringValue *>(toStringMember->call(object)); // ### invoke convert-to-string? } } void ConvertToString::visit(const FunctionValue *object) { - if (const FunctionValue *toStringMember = value_cast<const FunctionValue *>(object->lookupMember("toString"))) { + if (const FunctionValue *toStringMember = value_cast<const FunctionValue *>(object->lookupMember("toString", 0))) { _result = value_cast<const StringValue *>(toStringMember->call(object)); // ### invoke convert-to-string? } } @@ -1947,3 +1991,24 @@ bool ASTFunctionValue::isVariadic() const { return true; } + +QmlPrototypeReference::QmlPrototypeReference(AST::UiQualifiedId *qmlTypeName, Engine *engine) + : Reference(engine), + _qmlTypeName(qmlTypeName) +{ +} + +QmlPrototypeReference::~QmlPrototypeReference() +{ +} + +UiQualifiedId *QmlPrototypeReference::qmlTypeName() const +{ + return _qmlTypeName; +} + +const Value *QmlPrototypeReference::value(Context *context) const +{ + return context->lookupType(_qmlTypeName); +} + diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index 1d3e261e883fe59f83705eb9d498f96751f28974..8144c9950dfeeb00f6544a595b0af5aa9effce56 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -229,10 +229,14 @@ public: LookupMode lookupMode() const; void setLookupMode(LookupMode lookupMode); + const ObjectValue *typeEnvironment() const; + void setTypeEnvironment(const ObjectValue *typeEnvironment); + void pushScope(const ObjectValue *object); void popScope(); - const Value *lookup(const QString &name) const; + const Value *lookup(const QString &name); + const ObjectValue *lookupType(AST::UiQualifiedId *qmlTypeName); const Value *property(const ObjectValue *object, const QString &name) const; void setProperty(const ObjectValue *object, const QString &name, const Value *value); @@ -242,6 +246,7 @@ private: Engine *_engine; LookupMode _lookupMode; + const ObjectValue *_typeEnvironment; QHash<const ObjectValue *, Properties> _properties; ScopeChain _scopeChain; }; @@ -274,16 +279,16 @@ public: QString className() const; void setClassName(const QString &className); - const ObjectValue *prototype() const; - void setPrototype(const ObjectValue *prototype); + const ObjectValue *prototype(Context *context) const; + void setPrototype(const Value *prototype); virtual void processMembers(MemberProcessor *processor) const; - virtual const Value *property(const QString &name) const; + virtual const Value *property(const QString &name, Context *context) const; virtual void setProperty(const QString &name, const Value *value); virtual void removeProperty(const QString &name); - virtual const Value *lookupMember(const QString &name) const; + virtual const Value *lookupMember(const QString &name, Context *context) const; // Value interface virtual const ObjectValue *asObjectValue() const; @@ -294,7 +299,7 @@ private: private: Engine *_engine; - const ObjectValue *_prototype; + const Value *_prototype; QHash<QString, const Value *> _members; QString _className; }; @@ -307,7 +312,7 @@ 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 const Value *lookupMember(const QString &name, Context *context) const; virtual void processMembers(MemberProcessor *processor) const; const Value *propertyValue(const QMetaProperty &prop) const; @@ -336,9 +341,12 @@ private: class QMLJS_EXPORT Activation { public: - Activation(); + explicit Activation(Context *parentContext = 0); virtual ~Activation(); + Context *context() const; + Context *parentContext() const; + bool calledAsConstructor() const; void setCalledAsConstructor(bool calledAsConstructor); @@ -355,6 +363,7 @@ private: ObjectValue *_thisObject; ValueList _arguments; bool _calledAsFunction; + Context *_parentContext; }; @@ -398,7 +407,7 @@ public: void setReturnValue(const Value *returnValue); // ObjectValue interface - virtual const Value *property(const QString &name) const; + virtual const Value *property(const QString &name, Context *context) const; // FunctionValue interface virtual const Value *returnValue() const; @@ -614,6 +623,20 @@ private: // internal +class QMLJS_EXPORT QmlPrototypeReference: public Reference +{ +public: + QmlPrototypeReference(AST::UiQualifiedId *qmlTypeName, Engine *engine); + virtual ~QmlPrototypeReference(); + + AST::UiQualifiedId *qmlTypeName() const; + + virtual const Value *value(Context *context) const; + +private: + AST::UiQualifiedId *_qmlTypeName; +}; + class QMLJS_EXPORT ASTObjectValue: public ObjectValue { AST::UiQualifiedId *_typeName; diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp index 6ce97d04f6249eeeda9900a2765b2ac175ecd6a0..2b786b6d12d71d834a054618dcfbdb83bd36c607 100644 --- a/src/libs/qmljs/qmljslink.cpp +++ b/src/libs/qmljs/qmljslink.cpp @@ -23,16 +23,6 @@ Link::Link(Document::Ptr currentDoc, const Snapshot &snapshot, Interpreter::Engi Link::~Link() { - // unset all prototypes - foreach (Document::Ptr doc, _docs) { - BindPtr bind = doc->bind(); - - if (doc->qmlProgram()) { - foreach (ObjectValue *object, bind->_qmlObjects) { - object->setPrototype(0); - } - } - } } Context *Link::context() @@ -90,31 +80,20 @@ void Link::scopeChainAt(Document::Ptr doc, Node *currentObject) if (bind->_idEnvironment) _context.pushScope(bind->_idEnvironment); - if (const ObjectValue *typeEnvironment = _typeEnvironments.value(doc.data())) + if (const ObjectValue *typeEnvironment = _typeEnvironments.value(doc.data())) { _context.pushScope(typeEnvironment); + _context.setTypeEnvironment(typeEnvironment); + } } void Link::linkImports() { foreach (Document::Ptr doc, _docs) { - BindPtr bind = doc->bind(); - ObjectValue *typeEnv = engine()->newObject(/*prototype =*/0); // ### FIXME _typeEnvironments.insert(doc.data(), typeEnv); // Populate the _typeEnvironment with imports. populateImportedTypes(typeEnv, doc); - - // Set the prototypes. - QHashIterator<Node *, ObjectValue *> it(bind->_qmlObjects); - while (it.hasNext()) { - it.next(); - Node *binding = it.key(); - ObjectValue *value = it.value(); - - if (UiQualifiedId *qualifiedId = qualifiedTypeNameId(binding)) - value->setPrototype(lookupType(typeEnv, qualifiedId)); - } } } @@ -260,24 +239,6 @@ void Link::importNonFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc, A } } -const ObjectValue *Link::lookupType(ObjectValue *env, UiQualifiedId *id) -{ - const ObjectValue *objectValue = env; - - for (UiQualifiedId *iter = id; objectValue && iter; iter = iter->next) { - if (! iter->name) - return 0; - - const Value *value = objectValue->property(iter->name->asString()); - if (!value) - return 0; - - objectValue = value->asObjectValue(); - } - - return objectValue; -} - UiQualifiedId *Link::qualifiedTypeNameId(Node *node) { if (UiObjectBinding *binding = AST::cast<UiObjectBinding *>(node)) diff --git a/src/libs/qmljs/qmljslink.h b/src/libs/qmljs/qmljslink.h index 8c346ac62bec3daa250d6bbdec0b704f4e7b78c9..c588c37b16b163cea3363928502705ccf092c0c2 100644 --- a/src/libs/qmljs/qmljslink.h +++ b/src/libs/qmljs/qmljslink.h @@ -31,7 +31,6 @@ public: private: static QList<Document::Ptr> reachableDocuments(Document::Ptr startDoc, const Snapshot &snapshot); - static const Interpreter::ObjectValue *lookupType(Interpreter::ObjectValue *env, AST::UiQualifiedId *id); static AST::UiQualifiedId *qualifiedTypeNameId(AST::Node *node); void linkImports(); diff --git a/src/plugins/qmljseditor/qmlcodecompletion.cpp b/src/plugins/qmljseditor/qmlcodecompletion.cpp index fed3ee48e2c916737218a1a8f8168ef00daca37e..0a97e19a68e04fb60d04d8d6dedc0c09a21041ad 100644 --- a/src/plugins/qmljseditor/qmlcodecompletion.cpp +++ b/src/plugins/qmljseditor/qmlcodecompletion.cpp @@ -219,7 +219,7 @@ private: return; _processed.insert(object); - enumerateProperties(object->prototype()); + enumerateProperties(object->prototype(_context)); object->processMembers(this); } diff --git a/src/plugins/qmljseditor/qmlhoverhandler.cpp b/src/plugins/qmljseditor/qmlhoverhandler.cpp index 8b94716a170aa60ead59ec5f02c50e23cf2d9d88..5885be5383b4373e788912b45756bc5455147f8a 100644 --- a/src/plugins/qmljseditor/qmlhoverhandler.cpp +++ b/src/plugins/qmljseditor/qmlhoverhandler.cpp @@ -179,7 +179,7 @@ void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, in const Interpreter::Value *value = check(node); QStringList baseClasses; - m_toolTip = prettyPrint(value, &interp, &baseClasses); + m_toolTip = prettyPrint(value, link.context(), &baseClasses); foreach (const QString &baseClass, baseClasses) { QString helpId = QLatin1String("QML."); @@ -210,10 +210,10 @@ void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, in } } -QString QmlHoverHandler::prettyPrint(const QmlJS::Interpreter::Value *value, QmlJS::Interpreter::Engine *interp, +QString QmlHoverHandler::prettyPrint(const QmlJS::Interpreter::Value *value, QmlJS::Interpreter::Context *context, QStringList *baseClasses) const { - if (!value) + if (! value) return QString(); if (const Interpreter::ObjectValue *objectValue = value->asObjectValue()) { @@ -223,14 +223,14 @@ QString QmlHoverHandler::prettyPrint(const QmlJS::Interpreter::Value *value, Qml if (! className.isEmpty()) baseClasses->append(className); - objectValue = objectValue->prototype(); + objectValue = objectValue->prototype(context); } while (objectValue); if (! baseClasses->isEmpty()) return baseClasses->first(); } - QString typeId = interp->typeId(value); + QString typeId = context->engine()->typeId(value); if (typeId == QLatin1String("undefined")) typeId.clear(); diff --git a/src/plugins/qmljseditor/qmlhoverhandler.h b/src/plugins/qmljseditor/qmlhoverhandler.h index 0f481e444bcf4d9ce7c2411c609a2927a34f8960..e564f58d53e77dd2420f274d0f4ca57a18ee0f42 100644 --- a/src/plugins/qmljseditor/qmlhoverhandler.h +++ b/src/plugins/qmljseditor/qmlhoverhandler.h @@ -47,6 +47,7 @@ class IEditor; namespace QmlJS { namespace Interpreter { class Engine; + class Context; class Value; } } @@ -74,7 +75,7 @@ private slots: private: void updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int pos); - QString prettyPrint(const QmlJS::Interpreter::Value *value, QmlJS::Interpreter::Engine *interp, + QString prettyPrint(const QmlJS::Interpreter::Value *value, QmlJS::Interpreter::Context *context, QStringList *baseClasses) const; private: