QmlJS: Refactor LookupContext creation for speed.

* If possible, create LookupContexts through SemanticInfo; it caches the
  linked Context and will be faster.
* Contexts now own their Engine.

Reviewed-by: Lasse Holmstedt
This commit is contained in:
Christian Kamm
2010-08-26 10:50:00 +02:00
parent af46c3d947
commit 87e04df257
15 changed files with 141 additions and 96 deletions

View File

@@ -691,7 +691,7 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
const QIcon keywordIcon = iconForColor(Qt::darkYellow);
const QList<AST::Node *> path = semanticInfo.astPath(editor->position());
LookupContext::Ptr lookupContext = LookupContext::create(document, snapshot, path);
LookupContext::Ptr lookupContext = semanticInfo.lookupContext(path);
const Interpreter::Context *context = lookupContext->context();
// Search for the operator that triggered the completion.

View File

@@ -40,6 +40,7 @@
#include <qmljs/qmljsdocument.h>
#include <qmljs/qmljsicontextpane.h>
#include <qmljs/qmljslookupcontext.h>
#include <qmljs/qmljslink.h>
#include <qmljs/parser/qmljsastvisitor_p.h>
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/parser/qmljsengine_p.h>
@@ -553,6 +554,18 @@ QList<AST::Node *> SemanticInfo::astPath(int cursorPosition) const
return path;
}
LookupContext::Ptr SemanticInfo::lookupContext(const QList<QmlJS::AST::Node *> &path) const
{
// create and link context if necessary
if (!m_context) {
Interpreter::Context *ctx = new Interpreter::Context;
Link link(ctx, document, snapshot, ModelManagerInterface::instance()->importPaths());
m_context = QSharedPointer<const QmlJS::Interpreter::Context>(ctx);
}
return LookupContext::create(document, snapshot, *m_context, path);
}
static bool importContainsCursor(UiImport *importAst, unsigned cursorPosition)
{
return cursorPosition >= importAst->firstSourceLocation().begin()
@@ -962,9 +975,9 @@ void QmlJSTextEditor::updateCursorPositionNow()
Node *oldNode = m_semanticInfo.declaringMemberNoProperties(m_oldCursorPosition);
Node *newNode = m_semanticInfo.declaringMemberNoProperties(position());
if (oldNode != newNode && m_oldCursorPosition != -1)
m_contextPane->apply(editableInterface(), m_semanticInfo.document, m_semanticInfo.snapshot, newNode, false);
m_contextPane->apply(editableInterface(), m_semanticInfo.lookupContext(), newNode, false);
if (oldNode != newNode &&
m_contextPane->isAvailable(editableInterface(), m_semanticInfo.document, m_semanticInfo.snapshot, newNode) &&
m_contextPane->isAvailable(editableInterface(), m_semanticInfo.lookupContext(), newNode) &&
!m_contextPane->widget()->isVisible()) {
QList<TextEditor::Internal::RefactorMarker> markers;
if (UiObjectMember *m = newNode->uiObjectMemberCast()) {
@@ -1042,22 +1055,19 @@ class SelectedElement: protected Visitor
unsigned m_cursorPositionStart;
unsigned m_cursorPositionEnd;
QList<UiObjectMember *> m_selectedMembers;
Document::Ptr m_document;
Snapshot m_snapshot;
LookupContext::Ptr m_lookupContext;
public:
SelectedElement()
: m_cursorPositionStart(0), m_cursorPositionEnd(0) {}
QList<UiObjectMember *> operator()(Document::Ptr doc, Snapshot snapshot, unsigned startPosition, unsigned endPosition)
QList<UiObjectMember *> operator()(LookupContext::Ptr lookupContext, unsigned startPosition, unsigned endPosition)
{
m_document = doc;
m_snapshot = snapshot;
m_lookupContext = lookupContext;
m_cursorPositionStart = startPosition;
m_cursorPositionEnd = endPosition;
m_selectedMembers.clear();
Node::accept(doc->qmlProgram(), this);
Node::accept(lookupContext->document()->qmlProgram(), this);
return m_selectedMembers;
}
@@ -1092,14 +1102,10 @@ protected:
inline bool hasVisualPresentation(Node *ast)
{
Bind *bind = m_document->bind();
Bind *bind = m_lookupContext->document()->bind();
const Interpreter::ObjectValue *objValue = bind->findQmlObject(ast);
QStringList prototypes;
if (m_lookupContext.isNull()) {
m_lookupContext = LookupContext::create(m_document, m_snapshot, QList<Node*>());
}
while (objValue) {
prototypes.append(objValue->className());
objValue = objValue->prototype(m_lookupContext->context());
@@ -1189,7 +1195,7 @@ void QmlJSTextEditor::setSelectedElements()
if (m_semanticInfo.document) {
SelectedElement selectedMembers;
QList<UiObjectMember *> members = selectedMembers(m_semanticInfo.document, m_semanticInfo.snapshot,
QList<UiObjectMember *> members = selectedMembers(m_semanticInfo.lookupContext(),
startPos, endPos);
if (!members.isEmpty()) {
foreach(UiObjectMember *m, members) {
@@ -1376,7 +1382,7 @@ TextEditor::BaseTextEditor::Link QmlJSTextEditor::findLinkAt(const QTextCursor &
return Link();
}
LookupContext::Ptr lookupContext = LookupContext::create(semanticInfo.document, semanticInfo.snapshot, semanticInfo.astPath(cursorPosition));
LookupContext::Ptr lookupContext = semanticInfo.lookupContext(semanticInfo.astPath(cursorPosition));
const Interpreter::Value *value = lookupContext->evaluate(node);
QString fileName;
@@ -1422,7 +1428,7 @@ void QmlJSTextEditor::showContextPane()
{
if (m_contextPane) {
Node *newNode = m_semanticInfo.declaringMemberNoProperties(position());
m_contextPane->apply(editableInterface(), m_semanticInfo.document, m_semanticInfo.snapshot, newNode, false, true);
m_contextPane->apply(editableInterface(), m_semanticInfo.lookupContext(), newNode, false, true);
m_oldCursorPosition = position();
QList<TextEditor::Internal::RefactorMarker> markers;
setRefactorMarkers(markers);
@@ -1481,7 +1487,7 @@ void QmlJSTextEditor::wheelEvent(QWheelEvent *event)
BaseTextEditor::wheelEvent(event);
if (visible)
m_contextPane->apply(editableInterface(), m_semanticInfo.document, m_semanticInfo.snapshot, m_semanticInfo.declaringMemberNoProperties(position()), false, true);
m_contextPane->apply(editableInterface(), m_semanticInfo.lookupContext(), m_semanticInfo.declaringMemberNoProperties(position()), false, true);
}
void QmlJSTextEditor::resizeEvent(QResizeEvent *event)
@@ -1723,7 +1729,7 @@ void QmlJSTextEditor::updateSemanticInfo(const SemanticInfo &semanticInfo)
if (m_contextPane) {
Node *newNode = m_semanticInfo.declaringMemberNoProperties(position());
if (newNode) {
m_contextPane->apply(editableInterface(), doc, m_semanticInfo.snapshot, newNode, true);
m_contextPane->apply(editableInterface(), m_semanticInfo.lookupContext(), newNode, true);
showTextMarker();
}
}
@@ -1776,7 +1782,7 @@ bool QmlJSTextEditor::hideContextPane()
{
bool b = (m_contextPane) && m_contextPane->widget()->isVisible();
if (b)
m_contextPane->apply(editableInterface(), m_semanticInfo.document, m_semanticInfo.snapshot, 0, false);
m_contextPane->apply(editableInterface(), m_semanticInfo.lookupContext(), 0, false);
return b;
}

View File

@@ -34,6 +34,7 @@
#include <qmljs/qmljsdocument.h>
#include <qmljs/qmljsscanner.h>
#include <qmljs/qmljsinterpreter.h>
#include <texteditor/basetexteditor.h>
#include <QtCore/QWaitCondition>
@@ -53,6 +54,7 @@ class ICore;
namespace QmlJS {
class ModelManagerInterface;
class IContextPane;
class LookupContext;
}
/*!
@@ -129,6 +131,9 @@ public:
// Returns the list of nodes that enclose the given position.
QList<QmlJS::AST::Node *> astPath(int cursorPosition) const;
// Returns a context for the given path
QSharedPointer<QmlJS::LookupContext> lookupContext(const QList<QmlJS::AST::Node *> &path = QList<QmlJS::AST::Node *>()) const;
public: // attributes
QmlJS::Document::Ptr document;
QmlJS::Snapshot snapshot;
@@ -138,6 +143,10 @@ public: // attributes
// these are in addition to the parser messages in the document
QList<QmlJS::DiagnosticMessage> semanticMessages;
private:
// created lazily
mutable QSharedPointer<const QmlJS::Interpreter::Context> m_context;
};
class SemanticHighlighter: public QThread

View File

@@ -111,9 +111,8 @@ void HoverHandler::identifyMatch(TextEditor::ITextEditor *editor, int pos)
if (astPath.isEmpty())
return;
const Snapshot &snapshot = semanticInfo.snapshot;
const Document::Ptr qmlDocument = semanticInfo.document;
LookupContext::Ptr lookupContext = LookupContext::create(qmlDocument, snapshot, astPath);
LookupContext::Ptr lookupContext = semanticInfo.lookupContext(astPath);
if (!matchColorItem(lookupContext, qmlDocument, astPath, pos))
handleOrdinaryMatch(lookupContext, semanticInfo.nodeUnderCursor(pos));

View File

@@ -42,10 +42,7 @@ QVariant QmlOutlineItem::data(int role) const
return QVariant();
QList<AST::Node *> astPath = m_outlineModel->m_semanticInfo.astPath(location.begin());
Document::Ptr document = m_outlineModel->m_semanticInfo.document;
Snapshot snapshot = m_outlineModel->m_semanticInfo.snapshot;
LookupContext::Ptr lookupContext = LookupContext::create(document, snapshot, astPath);
LookupContext::Ptr lookupContext = m_outlineModel->m_semanticInfo.lookupContext(astPath);
const Interpreter::Value *value = lookupContext->evaluate(uiQualifiedId);
return prettyPrint(value, lookupContext->context());
@@ -331,10 +328,9 @@ void QmlOutlineModel::update(const SemanticInfo &semanticInfo)
// Set up lookup context once to do the element type lookup
//
// We're simplifying here by using the root context everywhere
// (empty node list). However, creating the LookupContext is quite expensive (about 3ms),
// and there is AFAIK no way to introduce new type names in a sub-context.
m_context = LookupContext::create(semanticInfo.document, semanticInfo.snapshot, QList<AST::Node*>());
// We're simplifying here by using the root context everywhere; should be
// ok since there is AFAIK no way to introduce new type names in a sub-context.
m_context = semanticInfo.lookupContext();
m_typeToIcon.clear();
m_itemToNode.clear();
m_itemToIdNode.clear();

View File

@@ -98,13 +98,16 @@ QuickToolBar::~QuickToolBar()
m_widget.clear();
}
void QuickToolBar::apply(TextEditor::BaseTextEditorEditable *editor, Document::Ptr doc, const QmlJS::Snapshot &snapshot, AST::Node *node, bool update, bool force)
void QuickToolBar::apply(TextEditor::BaseTextEditorEditable *editor, LookupContext::Ptr lookupContext, AST::Node *node, bool update, bool force)
{
if (!QuickToolBarSettings::get().enableContextPane && !force && !update) {
contextWidget()->hide();
return;
}
if (lookupContext.isNull())
return;
Document::Ptr doc = lookupContext->document();
if (doc.isNull())
return;
@@ -113,7 +116,6 @@ void QuickToolBar::apply(TextEditor::BaseTextEditorEditable *editor, Document::P
m_blockWriting = true;
LookupContext::Ptr lookupContext = LookupContext::create(doc, snapshot, QList<Node*>());
const Interpreter::ObjectValue *scopeObject = doc->bind()->findQmlObject(node);
QStringList prototypes;
@@ -211,43 +213,45 @@ void QuickToolBar::apply(TextEditor::BaseTextEditorEditable *editor, Document::P
}
bool QuickToolBar::isAvailable(TextEditor::BaseTextEditorEditable *, Document::Ptr doc, const QmlJS::Snapshot &snapshot, AST::Node *node)
bool QuickToolBar::isAvailable(TextEditor::BaseTextEditorEditable *, LookupContext::Ptr lookupContext, AST::Node *node)
{
if (lookupContext.isNull())
return false;
Document::Ptr doc = lookupContext->document();
if (doc.isNull())
return false;
if (!node)
return false;
LookupContext::Ptr lookupContext = LookupContext::create(doc, snapshot, QList<Node*>());
const Interpreter::ObjectValue *scopeObject = doc->bind()->findQmlObject(node);
const Interpreter::ObjectValue *scopeObject = doc->bind()->findQmlObject(node);
QStringList prototypes;
QStringList prototypes;
while (scopeObject) {
prototypes.append(scopeObject->className());
scopeObject = scopeObject->prototype(lookupContext->context());
while (scopeObject) {
prototypes.append(scopeObject->className());
scopeObject = scopeObject->prototype(lookupContext->context());
}
if (prototypes.contains("PropertyChanges")) {
const Interpreter::ObjectValue *targetObject = getPropertyChangesTarget(node, lookupContext);
prototypes.clear();
while (targetObject) {
prototypes.append(targetObject->className());
targetObject = targetObject->prototype(lookupContext->context());
}
}
if (prototypes.contains("PropertyChanges")) {
const Interpreter::ObjectValue *targetObject = getPropertyChangesTarget(node, lookupContext);
prototypes.clear();
while (targetObject) {
prototypes.append(targetObject->className());
targetObject = targetObject->prototype(lookupContext->context());
}
}
if (prototypes.contains("Rectangle") ||
if (prototypes.contains("Rectangle") ||
prototypes.contains("Image") ||
prototypes.contains("BorderImage") ||
prototypes.contains("TextEdit") ||
prototypes.contains("TextInput") ||
prototypes.contains("PropertyAnimation") ||
prototypes.contains("Text"))
return true;
return true;
return false;
return false;
}
void QuickToolBar::setProperty(const QString &propertyName, const QVariant &value)

View File

@@ -30,8 +30,8 @@ class QuickToolBar : public QmlJS::IContextPane
public:
QuickToolBar(QObject *parent = 0);
~QuickToolBar();
void apply(TextEditor::BaseTextEditorEditable *editor, QmlJS::Document::Ptr doc, const QmlJS::Snapshot &snapshot, QmlJS::AST::Node *node, bool update, bool force = 0);
bool isAvailable(TextEditor::BaseTextEditorEditable *editor, QmlJS::Document::Ptr doc, const QmlJS::Snapshot &snapshot, QmlJS::AST::Node *node);
void apply(TextEditor::BaseTextEditorEditable *editor, QmlJS::LookupContext::Ptr lookupContext, QmlJS::AST::Node *node, bool update, bool force = 0);
bool isAvailable(TextEditor::BaseTextEditorEditable *editor, QmlJS::LookupContext::Ptr lookupContext, QmlJS::AST::Node *node);
void setProperty(const QString &propertyName, const QVariant &value);
void removeProperty(const QString &propertyName);
void setEnabled(bool);