diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index 61cd953301655acc5247fce0f78a7e527f2fdadd..a715c6900e380a0c5dc1126aae7088ad0a404053 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -1675,7 +1675,9 @@ void Context::setProperty(const ObjectValue *object, const QString &name, const QString Context::defaultPropertyName(const ObjectValue *object) const { - for (const ObjectValue *o = object; o; o = o->prototype(this)) { + PrototypeIterator iter(object, this); + while (iter.hasNext()) { + const ObjectValue *o = iter.next(); if (const ASTObjectValue *astObjValue = dynamic_cast<const ASTObjectValue *>(o)) { QString defaultProperty = astObjValue->defaultPropertyName(); if (!defaultProperty.isEmpty()) @@ -1881,9 +1883,11 @@ const Value *ObjectValue::lookupMember(const QString &name, const Context *conte } if (examinePrototypes) { - const ObjectValue *prototypeObject = prototype(context); - if (prototypeObject) { - if (const Value *m = prototypeObject->lookupMember(name, context)) + PrototypeIterator iter(this, context); + iter.next(); // skip this + while (iter.hasNext()) { + const ObjectValue *prototypeObject = iter.next(); + if (const Value *m = prototypeObject->lookupMember(name, context, false)) return m; } } @@ -1891,6 +1895,55 @@ const Value *ObjectValue::lookupMember(const QString &name, const Context *conte return 0; } +PrototypeIterator::PrototypeIterator(const ObjectValue *start, const Context *context) + : m_current(0) + , m_next(start) + , m_context(context) +{ + if (start) + m_prototypes.reserve(10); +} + +bool PrototypeIterator::hasNext() +{ + if (m_next) + return true; + if (!m_current) + return false; + m_next = m_current->prototype(m_context); + if (!m_next || m_prototypes.contains(m_next)) { + m_next = 0; + return false; + } + return true; +} + +const ObjectValue *PrototypeIterator::next() +{ + if (hasNext()) { + m_current = m_next; + m_prototypes += m_next; + m_next = 0; + return m_current; + } + return 0; +} + +const ObjectValue *PrototypeIterator::peekNext() +{ + if (hasNext()) { + return m_next; + } + return 0; +} + +QList<const ObjectValue *> PrototypeIterator::all() +{ + while (hasNext()) + next(); + return m_prototypes; +} + Activation::Activation(Context *parentContext) : _thisObject(0), _calledAsFunction(true), diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index 2a2036be44cbc5b082d301a2012dac189fb5df60..27f0d4f5ca9d0ff0e03149fdaed4d7d33fbcbfe4 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -397,6 +397,7 @@ public: QString className() const; void setClassName(const QString &className); + // not guaranteed to not recurse, use PrototypeIterator! const ObjectValue *prototype(const Context *context) const; void setPrototype(const Value *prototype); @@ -422,6 +423,24 @@ private: QString _className; }; +class QMLJS_EXPORT PrototypeIterator +{ +public: + PrototypeIterator(const ObjectValue *start, const Context *context); + + bool hasNext(); + const ObjectValue *peekNext(); + const ObjectValue *next(); + + QList<const ObjectValue *> all(); + +private: + const ObjectValue *m_current; + const ObjectValue *m_next; + QList<const ObjectValue *> m_prototypes; + const Context *m_context; +}; + class QMLJS_EXPORT QmlObjectValue: public ObjectValue { public: diff --git a/src/libs/qmljs/qmljsscopebuilder.cpp b/src/libs/qmljs/qmljsscopebuilder.cpp index 7cd3261232df2a0de4689b223ba44b143a00e76f..1809b954bd07d30cdcf1760ad130d2dc198719d8 100644 --- a/src/libs/qmljs/qmljsscopebuilder.cpp +++ b/src/libs/qmljs/qmljsscopebuilder.cpp @@ -202,8 +202,10 @@ void ScopeBuilder::setQmlScopeObject(Node *node) // check if the object has a Qt.ListElement or Qt.Connections ancestor // ### allow only signal bindings for Connections - const ObjectValue *prototype = scopeObject->prototype(_context); - while (prototype) { + PrototypeIterator iter(scopeObject, _context); + iter.next(); + while (iter.hasNext()) { + const ObjectValue *prototype = iter.next(); if (const QmlObjectValue *qmlMetaObject = dynamic_cast<const QmlObjectValue *>(prototype)) { if ((qmlMetaObject->className() == QLatin1String("ListElement") || qmlMetaObject->className() == QLatin1String("Connections") @@ -213,11 +215,10 @@ void ScopeBuilder::setQmlScopeObject(Node *node) break; } } - prototype = prototype->prototype(_context); } // check if the object has a Qt.PropertyChanges ancestor - prototype = scopeObject->prototype(_context); + const ObjectValue *prototype = scopeObject->prototype(_context); prototype = isPropertyChangesObject(_context, prototype); // find the target script binding if (prototype) { @@ -281,15 +282,15 @@ const Value *ScopeBuilder::scopeObjectLookup(AST::UiQualifiedId *id) const ObjectValue *ScopeBuilder::isPropertyChangesObject(const Context *context, const ObjectValue *object) { - const ObjectValue *prototype = object; - while (prototype) { + PrototypeIterator iter(object, context); + while (iter.hasNext()) { + const ObjectValue *prototype = iter.next(); if (const QmlObjectValue *qmlMetaObject = dynamic_cast<const QmlObjectValue *>(prototype)) { if (qmlMetaObject->className() == QLatin1String("PropertyChanges") && (qmlMetaObject->packageName() == QLatin1String("Qt") || qmlMetaObject->packageName() == QLatin1String("QtQuick"))) return prototype; } - prototype = prototype->prototype(context); } return 0; } diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 7544d452eee946f0f1e4fbe494c3c4e32a3f3686..e3a49be860a2c365495d8c37296a371b760a58d5 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -372,10 +372,12 @@ public: if (objectValue && objectValue->prototype(m_context) == m_context->engine()->arrayPrototype()) return true; - for (const Interpreter::ObjectValue *iter = containingObject; iter; iter = iter->prototype(m_context)) { - if (iter->property(name, m_context) == m_context->engine()->arrayPrototype()) + Interpreter::PrototypeIterator iter(containingObject, m_context); + while (iter.hasNext()) { + const Interpreter::ObjectValue *proto = iter.next(); + if (proto->property(name, m_context) == m_context->engine()->arrayPrototype()) return true; - if (const Interpreter::QmlObjectValue *qmlIter = dynamic_cast<const Interpreter::QmlObjectValue *>(iter)) { + if (const Interpreter::QmlObjectValue *qmlIter = dynamic_cast<const Interpreter::QmlObjectValue *>(proto)) { if (qmlIter->isListProperty(name)) return true; } @@ -397,9 +399,11 @@ public: return hasQuotes ? QVariant(cleanedValue) : cleverConvert(cleanedValue); } - for (const Interpreter::ObjectValue *iter = containingObject; iter; iter = iter->prototype(m_context)) { - if (iter->lookupMember(name, m_context, false)) { - containingObject = iter; + Interpreter::PrototypeIterator iter(containingObject, m_context); + while (iter.hasNext()) { + const Interpreter::ObjectValue *proto = iter.next(); + if (proto->lookupMember(name, m_context, false)) { + containingObject = proto; break; } } @@ -446,9 +450,11 @@ public: return QVariant(); } - for (const Interpreter::ObjectValue *iter = containingObject; iter; iter = iter->prototype(m_context)) { - if (iter->lookupMember(name, m_context, false)) { - containingObject = iter; + Interpreter::PrototypeIterator iter(containingObject, m_context); + while (iter.hasNext()) { + const Interpreter::ObjectValue *proto = iter.next(); + if (proto->lookupMember(name, m_context, false)) { + containingObject = proto; break; } } @@ -473,12 +479,11 @@ public: rhsValueName = memberExp->name->asString(); } - if (!rhsValueObject) - return QVariant(); - - for (const Interpreter::ObjectValue *iter = rhsValueObject; iter; iter = iter->prototype(m_context)) { - if (iter->lookupMember(rhsValueName, m_context, false)) { - rhsValueObject = iter; + iter = Interpreter::PrototypeIterator(rhsValueObject, m_context); + while (iter.hasNext()) { + const Interpreter::ObjectValue *proto = iter.next(); + if (proto->lookupMember(rhsValueName, m_context, false)) { + rhsValueObject = proto; break; } } diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp index f14765b7924a4bfe0a97e328fa9ab44812a45f46..1a8490dc673d47827c65f8b10eb24b2b8201fbc4 100644 --- a/src/plugins/qmljseditor/qmljseditor.cpp +++ b/src/plugins/qmljseditor/qmljseditor.cpp @@ -1055,11 +1055,13 @@ protected: { Bind *bind = m_lookupContext->document()->bind(); const Interpreter::ObjectValue *objValue = bind->findQmlObject(ast); - QStringList prototypes; + if (!objValue) + return false; - while (objValue) { - prototypes.append(objValue->className()); - objValue = objValue->prototype(m_lookupContext->context()); + QStringList prototypes; + foreach (const Interpreter::ObjectValue *value, + Interpreter::PrototypeIterator(objValue, m_lookupContext->context()).all()) { + prototypes.append(value->className()); } return prototypes.contains(QString("QGraphicsObject")); diff --git a/src/plugins/qmljseditor/qmljsfindreferences.cpp b/src/plugins/qmljseditor/qmljsfindreferences.cpp index ff7f106c7b0c5c88b215eaf7467a80aa0f13848e..d17158ba235169a96d89c4304187855183564261 100644 --- a/src/plugins/qmljseditor/qmljsfindreferences.cpp +++ b/src/plugins/qmljseditor/qmljsfindreferences.cpp @@ -71,12 +71,16 @@ static const ObjectValue *prototypeWithMember(const Context *context, const Obje const Value *value = object->property(name, context); if (!value) return 0; - forever { - const ObjectValue *prototype = object->prototype(context); - if (!prototype || prototype->property(name, context) != value) - return object; - object = prototype; - } + const ObjectValue *prev = object; + PrototypeIterator iter(object, context); + iter.next(); + while (iter.hasNext()) { + const ObjectValue *prototype = iter.next(); + if (prototype->property(name, context) != value) + return prev; + prev = prototype; + } + return prev; } namespace { diff --git a/src/plugins/qmljseditor/qmljshoverhandler.cpp b/src/plugins/qmljseditor/qmljshoverhandler.cpp index b2ac1eb457410e55d1cd78f7ead1b2db3da58ac2..510469c39778e9d2787e2d446be294314c958d4f 100644 --- a/src/plugins/qmljseditor/qmljshoverhandler.cpp +++ b/src/plugins/qmljseditor/qmljshoverhandler.cpp @@ -237,17 +237,19 @@ void HoverHandler::prettyPrintTooltip(const QmlJS::Interpreter::Value *value, if (const Interpreter::ObjectValue *objectValue = value->asObjectValue()) { bool found = false; - do { - const QString className = objectValue->className(); + Interpreter::PrototypeIterator iter(objectValue, context); + while (iter.hasNext()) { + const Interpreter::ObjectValue *prototype = iter.next(); + const QString className = prototype->className(); if (! className.isEmpty()) { found = !qmlHelpId(className).isEmpty(); if (toolTip().isEmpty() || found) setToolTip(className); } - - objectValue = objectValue->prototype(context); - } while (objectValue && !found); + if (found) + break; + } } else if (const Interpreter::QmlEnumValue *enumValue = dynamic_cast<const Interpreter::QmlEnumValue *>(value)) { setToolTip(enumValue->name()); diff --git a/src/plugins/qmljseditor/quicktoolbar.cpp b/src/plugins/qmljseditor/quicktoolbar.cpp index 49e134b151569f056fe24e6a82d1db3f36302768..1ac28eaf8820545531e277cd53139137e1a5a553 100644 --- a/src/plugins/qmljseditor/quicktoolbar.cpp +++ b/src/plugins/qmljseditor/quicktoolbar.cpp @@ -115,19 +115,21 @@ void QuickToolBar::apply(TextEditor::BaseTextEditorEditable *editor, Document::P const Interpreter::ObjectValue *scopeObject = document->bind()->findQmlObject(node); - if (!lookupContext.isNull()) { + if (!lookupContext.isNull() && scopeObject) { m_prototypes.clear(); - while (scopeObject) { - m_prototypes.append(scopeObject->className()); - scopeObject = scopeObject->prototype(lookupContext->context()); + foreach (const Interpreter::ObjectValue *object, + Interpreter::PrototypeIterator(scopeObject, lookupContext->context()).all()) { + m_prototypes.append(object->className()); } if (m_prototypes.contains("PropertyChanges")) { const Interpreter::ObjectValue *targetObject = getPropertyChangesTarget(node, lookupContext); m_prototypes.clear(); - while (targetObject) { - m_prototypes.append(targetObject->className()); - targetObject = targetObject->prototype(lookupContext->context()); + if (targetObject) { + foreach (const Interpreter::ObjectValue *object, + Interpreter::PrototypeIterator(targetObject, lookupContext->context()).all()) { + m_prototypes.append(object->className()); + } } } }