aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhjk <hjk@theqtcompany.com>2015-07-08 13:14:03 +0200
committerhjk <hjk@theqtcompany.com>2015-07-09 12:59:03 +0000
commitbcd29daa48d4294fc02e434c63f0d5b9ac87f1d3 (patch)
treedd9bd409cd8ac4a0fe259da105cb2baf0ba29817
parent0e0f1babdb1db8b17131dee0a1ba280074a675a5 (diff)
Debugger: Consolidate QmlEngine
Create a QmlEnginePrivate. Move the QmlDebugClient there. Merge QmlAdapter into QmlEngine Abstraction is not used anymore. Move some helper bits to a qmlengineutils.{h,cpp} Change-Id: I63117355d786cc12641101b7fd38c7cd208d11eb Reviewed-by: Christian Stenger <christian.stenger@theqtcompany.com>
-rw-r--r--src/plugins/debugger/debugger.qbs3
-rw-r--r--src/plugins/debugger/qml/qml.pri6
-rw-r--r--src/plugins/debugger/qml/qmladapter.cpp233
-rw-r--r--src/plugins/debugger/qml/qmladapter.h107
-rw-r--r--src/plugins/debugger/qml/qmlengine.cpp2419
-rw-r--r--src/plugins/debugger/qml/qmlengine.h92
-rw-r--r--src/plugins/debugger/qml/qmlengineutils.cpp270
-rw-r--r--src/plugins/debugger/qml/qmlengineutils.h47
-rw-r--r--src/plugins/debugger/qml/qmlinspectoradapter.cpp39
-rw-r--r--src/plugins/debugger/qml/qmlinspectoradapter.h10
-rw-r--r--src/plugins/debugger/qml/qmlv8debuggerclient.cpp1822
-rw-r--r--src/plugins/debugger/qml/qmlv8debuggerclient.h143
12 files changed, 2256 insertions, 2935 deletions
diff --git a/src/plugins/debugger/debugger.qbs b/src/plugins/debugger/debugger.qbs
index cfb777f97c..f682cdc4b5 100644
--- a/src/plugins/debugger/debugger.qbs
+++ b/src/plugins/debugger/debugger.qbs
@@ -146,12 +146,11 @@ QtcPlugin {
prefix: "qml/"
files: [
"interactiveinterpreter.cpp", "interactiveinterpreter.h",
- "qmladapter.cpp", "qmladapter.h",
"qmlcppengine.cpp", "qmlcppengine.h",
"qmlengine.cpp", "qmlengine.h",
+ "qmlengineutils.cpp", "qmlengineutils.h",
"qmlinspectoradapter.cpp", "qmlinspectoradapter.h",
"qmlinspectoragent.cpp", "qmlinspectoragent.h",
- "qmlv8debuggerclient.cpp", "qmlv8debuggerclient.h",
"qmlv8debuggerclientconstants.h"
]
}
diff --git a/src/plugins/debugger/qml/qml.pri b/src/plugins/debugger/qml/qml.pri
index 12d34e0dc8..18ed7fe1f5 100644
--- a/src/plugins/debugger/qml/qml.pri
+++ b/src/plugins/debugger/qml/qml.pri
@@ -1,8 +1,7 @@
HEADERS += \
$$PWD/qmlengine.h \
- $$PWD/qmladapter.h \
+ $$PWD/qmlengineutils.h \
$$PWD/qmlcppengine.h \
- $$PWD/qmlv8debuggerclient.h \
$$PWD/interactiveinterpreter.h \
$$PWD/qmlv8debuggerclientconstants.h \
$$PWD/qmlinspectoragent.h \
@@ -10,9 +9,8 @@ HEADERS += \
SOURCES += \
$$PWD/qmlengine.cpp \
- $$PWD/qmladapter.cpp \
+ $$PWD/qmlengineutils.cpp \
$$PWD/qmlcppengine.cpp \
- $$PWD/qmlv8debuggerclient.cpp \
$$PWD/interactiveinterpreter.cpp \
$$PWD/qmlinspectoragent.cpp \
$$PWD/qmlinspectoradapter.cpp
diff --git a/src/plugins/debugger/qml/qmladapter.cpp b/src/plugins/debugger/qml/qmladapter.cpp
deleted file mode 100644
index c2698bdf17..0000000000
--- a/src/plugins/debugger/qml/qmladapter.cpp
+++ /dev/null
@@ -1,233 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms and
-** conditions see http://www.qt.io/terms-conditions. For further information
-** use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-****************************************************************************/
-
-#include "qmladapter.h"
-
-#include <debugger/debuggerstringutils.h>
-#include "qmlengine.h"
-#include "qmlv8debuggerclient.h"
-
-#include <utils/qtcassert.h>
-
-#include <QDebug>
-
-using namespace QmlDebug;
-
-namespace Debugger {
-namespace Internal {
-
-/*!
- QmlAdapter manages the connection & clients for QML/JS debugging.
- */
-
-QmlAdapter::QmlAdapter(DebuggerEngine *engine, QObject *parent)
- : QObject(parent)
- , m_engine(engine)
- , m_qmlClient(0)
- , m_conn(0)
- , m_msgClient(0)
-{
- m_connectionTimer.setInterval(4000);
- m_connectionTimer.setSingleShot(true);
- connect(&m_connectionTimer, &QTimer::timeout, this, &QmlAdapter::checkConnectionState);
-
- m_conn = new QmlDebugConnection(this);
- connect(m_conn, &QmlDebugConnection::stateMessage,
- this, &QmlAdapter::showConnectionStateMessage);
- connect(m_conn, &QmlDebugConnection::errorMessage,
- this, &QmlAdapter::showConnectionErrorMessage);
- connect(m_conn, &QmlDebugConnection::error,
- this, &QmlAdapter::connectionErrorOccurred);
- connect(m_conn, &QmlDebugConnection::opened,
- &m_connectionTimer, &QTimer::stop);
- connect(m_conn, &QmlDebugConnection::opened,
- this, &QmlAdapter::connected);
- connect(m_conn, &QmlDebugConnection::closed,
- this, &QmlAdapter::disconnected);
-
- createDebuggerClients();
- m_msgClient = new QDebugMessageClient(m_conn);
- connect(m_msgClient, &QDebugMessageClient::newState, this, &QmlAdapter::clientStateChanged);
-
-}
-
-QmlAdapter::~QmlAdapter()
-{
-}
-
-void QmlAdapter::beginConnectionTcp(const QString &address, quint16 port)
-{
- if (m_engine.isNull() || !m_conn || m_conn->isOpen())
- return;
-
- m_conn->connectToHost(address, port);
-
- //A timeout to check the connection state
- m_connectionTimer.start();
-}
-
-void QmlAdapter::closeConnection()
-{
- if (m_connectionTimer.isActive()) {
- m_connectionTimer.stop();
- } else {
- if (m_conn)
- m_conn->close();
- }
-}
-
-void QmlAdapter::connectionErrorOccurred(QDebugSupport::Error error)
-{
- // this is only an error if we are already connected and something goes wrong.
- if (isConnected()) {
- emit connectionError(error);
- } else {
- m_connectionTimer.stop();
- emit connectionStartupFailed();
- }
-}
-
-void QmlAdapter::clientStateChanged(QmlDebugClient::State state)
-{
- QString serviceName;
- float version = 0;
- if (QmlDebugClient *client = qobject_cast<QmlDebugClient*>(sender())) {
- serviceName = client->name();
- version = client->remoteVersion();
- }
-
- logServiceStateChange(serviceName, version, state);
-}
-
-void QmlAdapter::debugClientStateChanged(QmlDebugClient::State state)
-{
- if (state != QmlDebugClient::Enabled)
- return;
- QmlDebugClient *client = qobject_cast<QmlDebugClient*>(sender());
- QTC_ASSERT(client, return);
-
- m_qmlClient = qobject_cast<QmlV8DebuggerClient *>(client);
- m_qmlClient->startSession();
-}
-
-void QmlAdapter::checkConnectionState()
-{
- if (!isConnected()) {
- closeConnection();
- emit connectionStartupFailed();
- }
-}
-
-bool QmlAdapter::isConnected() const
-{
- return m_conn && m_qmlClient && m_conn->isOpen();
-}
-
-void QmlAdapter::createDebuggerClients()
-{
- QmlV8DebuggerClient *debugClient2 = new QmlV8DebuggerClient(m_conn);
- connect(debugClient2, &QmlV8DebuggerClient::newState,
- this, &QmlAdapter::clientStateChanged);
- connect(debugClient2, &QmlV8DebuggerClient::newState,
- this, &QmlAdapter::debugClientStateChanged);
-
- m_debugClients.insert(debugClient2->name(),debugClient2);
-
- debugClient2->setEngine((QmlEngine*)(m_engine.data()));
-}
-
-QmlDebugConnection *QmlAdapter::connection() const
-{
- return m_conn;
-}
-
-DebuggerEngine *QmlAdapter::debuggerEngine() const
-{
- return m_engine.data();
-}
-
-void QmlAdapter::showConnectionStateMessage(const QString &message)
-{
- if (!m_engine.isNull())
- m_engine.data()->showMessage(_("QML Debugger: ") + message, LogStatus);
-}
-
-void QmlAdapter::showConnectionErrorMessage(const QString &message)
-{
- if (!m_engine.isNull())
- m_engine.data()->showMessage(_("QML Debugger: ") + message, LogError);
-}
-
-QmlV8DebuggerClient *QmlAdapter::activeDebuggerClient() const
-{
- return m_qmlClient;
-}
-
-QHash<QString, QmlV8DebuggerClient*> QmlAdapter::debuggerClients() const
-{
- return m_debugClients;
-}
-
-QDebugMessageClient *QmlAdapter::messageClient() const
-{
- return m_msgClient;
-}
-
-void QmlAdapter::logServiceStateChange(const QString &service, float version,
- QmlDebugClient::State newState)
-{
- switch (newState) {
- case QmlDebugClient::Unavailable: {
- showConnectionStateMessage(_("Status of \"%1\" Version: %2 changed to 'unavailable'.").
- arg(service).arg(QString::number(version)));
- break;
- }
- case QmlDebugClient::Enabled: {
- showConnectionStateMessage(_("Status of \"%1\" Version: %2 changed to 'enabled'.").
- arg(service).arg(QString::number(version)));
- break;
- }
-
- case QmlDebugClient::NotConnected: {
- showConnectionStateMessage(_("Status of \"%1\" Version: %2 changed to 'not connected'.").
- arg(service).arg(QString::number(version)));
- break;
- }
- }
-}
-
-void QmlAdapter::logServiceActivity(const QString &service, const QString &logMessage)
-{
- if (!m_engine.isNull())
- m_engine.data()->showMessage(service + QLatin1Char(' ') + logMessage, LogDebug);
-}
-
-} // namespace Internal
-} // namespace Debugger
diff --git a/src/plugins/debugger/qml/qmladapter.h b/src/plugins/debugger/qml/qmladapter.h
deleted file mode 100644
index 7e966e4068..0000000000
--- a/src/plugins/debugger/qml/qmladapter.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms and
-** conditions see http://www.qt.io/terms-conditions. For further information
-** use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-****************************************************************************/
-
-#ifndef QMLADAPTER_H
-#define QMLADAPTER_H
-
-#include <qmldebug/qmldebugclient.h>
-
-#include <QPointer>
-#include <QTimer>
-
-namespace QmlDebug {
-class BaseEngineDebugClient;
-class QmlDebugConnection;
-class QDebugMessageClient;
-}
-
-namespace Debugger {
-namespace Internal {
-
-class QmlV8DebuggerClient;
-class DebuggerEngine;
-class QmlAdapterPrivate;
-
-class QmlAdapter : public QObject
-{
- Q_OBJECT
-
-public:
- explicit QmlAdapter(DebuggerEngine *engine, QObject *parent = 0);
- virtual ~QmlAdapter();
-
- void beginConnectionTcp(const QString &address, quint16 port);
- void closeConnection();
-
- QmlDebug::QmlDebugConnection *connection() const;
- DebuggerEngine *debuggerEngine() const;
-
- QmlV8DebuggerClient *activeDebuggerClient() const;
- QHash<QString, QmlV8DebuggerClient*> debuggerClients() const;
-
- QmlDebug::QDebugMessageClient *messageClient() const;
-
-public slots:
- void logServiceStateChange(const QString &service, float version,
- QmlDebug::QmlDebugClient::State newState);
- void logServiceActivity(const QString &service, const QString &logMessage);
-
-signals:
- void connected();
- void disconnected();
- void connectionStartupFailed();
- void connectionError(QDebugSupport::Error error);
- void serviceConnectionError(const QString serviceName);
-
-private slots:
- void connectionErrorOccurred(QDebugSupport::Error socketError);
- void clientStateChanged(QmlDebug::QmlDebugClient::State state);
- void debugClientStateChanged(QmlDebug::QmlDebugClient::State state);
- void checkConnectionState();
- void showConnectionStateMessage(const QString &message);
- void showConnectionErrorMessage(const QString &message);
-
-private:
- bool isConnected() const;
- void createDebuggerClients();
-
-private:
- QPointer<DebuggerEngine> m_engine;
- QmlV8DebuggerClient *m_qmlClient;
- QTimer m_connectionTimer;
- QmlDebug::QmlDebugConnection *m_conn;
- QHash<QString, QmlV8DebuggerClient*> m_debugClients;
- QmlDebug::QDebugMessageClient *m_msgClient;
-};
-
-} // namespace Internal
-} // namespace Debugger
-
-#endif // QMLADAPTER_H
diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp
index ea1aabbb62..753ca5d65f 100644
--- a/src/plugins/debugger/qml/qmlengine.cpp
+++ b/src/plugins/debugger/qml/qmlengine.cpp
@@ -29,46 +29,54 @@
****************************************************************************/
#include "qmlengine.h"
+
+#include "interactiveinterpreter.h"
+#include "qmlinspectoradapter.h"
#include "qmlinspectoragent.h"
-#include "qmlv8debuggerclient.h"
+#include "qmlv8debuggerclientconstants.h"
+#include "qmlengineutils.h"
+#include <debugger/breakhandler.h>
#include <debugger/debuggeractions.h>
#include <debugger/debuggercore.h>
#include <debugger/debuggerinternalconstants.h>
-#include <debugger/debuggermainwindow.h>
#include <debugger/debuggerruncontrol.h>
-#include <debugger/debuggerstartparameters.h>
#include <debugger/debuggerstringutils.h>
#include <debugger/debuggertooltipmanager.h>
-#include <debugger/localsandexpressionswindow.h>
+#include <debugger/sourcefileshandler.h>
+#include <debugger/stackhandler.h>
#include <debugger/threaddata.h>
+#include <debugger/watchhandler.h>
#include <debugger/watchwindow.h>
-#include <debugger/breakhandler.h>
-#include <debugger/stackhandler.h>
-#include <debugger/watchhandler.h>
-#include <debugger/sourcefileshandler.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/helpmanager.h>
+#include <coreplugin/icore.h>
+
+#include <projectexplorer/applicationlauncher.h>
#include <qmljseditor/qmljseditorconstants.h>
-#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmljs/consolemanagerinterface.h>
-#include <utils/qtcassert.h>
-
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
-#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/helpmanager.h>
-#include <coreplugin/icore.h>
+
+#include <utils/qtcassert.h>
#include <QDebug>
#include <QDir>
#include <QDockWidget>
+#include <QFileInfo>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
#include <QMessageBox>
#include <QPlainTextEdit>
+#include <QTextBlock>
+#include <QTimer>
-#define DEBUG_QML 1
+#define DEBUG_QML 0
#if DEBUG_QML
# define SDEBUG(s) qDebug() << s
#else
@@ -76,185 +84,141 @@
#endif
# define XSDEBUG(s) qDebug() << s
+using namespace Core;
+using namespace ProjectExplorer;
+using namespace QmlDebug;
using namespace QmlJS;
-using namespace AST;
+using namespace TextEditor;
namespace Debugger {
namespace Internal {
-static QTreeView *inspectorTreeView()
+enum Exceptions
{
- return Internal::inspectorView();
-}
+ NoExceptions,
+ UncaughtExceptions,
+ AllExceptions
+};
-class ASTWalker : public Visitor
+enum StepAction
{
-public:
- void operator()(Node *ast, quint32 *l, quint32 *c)
- {
- done = false;
- line = l;
- column = c;
- Node::accept(ast, this);
- }
-
- bool preVisit(Node *ast)
- {
- return ast->lastSourceLocation().startLine >= *line && !done;
- }
-
- //Case 1: Breakpoint is between sourceStart(exclusive) and
- // sourceEnd(inclusive) --> End tree walk.
- //Case 2: Breakpoint is on sourceStart --> Check for the start
- // of the first executable code. Set the line number and
- // column number. End tree walk.
- //Case 3: Breakpoint is on "unbreakable" code --> Find the next "breakable"
- // code and check for Case 2. End tree walk.
-
- //Add more types when suitable.
-
- bool visit(UiScriptBinding *ast)
- {
- if (!ast->statement)
- return true;
-
- quint32 sourceStartLine = ast->firstSourceLocation().startLine;
- quint32 statementStartLine;
- quint32 statementColumn;
-
- if (ast->statement->kind == Node::Kind_ExpressionStatement) {
- statementStartLine = ast->statement->firstSourceLocation().
- startLine;
- statementColumn = ast->statement->firstSourceLocation().startColumn;
-
- } else if (ast->statement->kind == Node::Kind_Block) {
- Block *block = static_cast<Block *>(ast->statement);
- if (!block || !block->statements)
- return true;
- statementStartLine = block->statements->firstSourceLocation().
- startLine;
- statementColumn = block->statements->firstSourceLocation().
- startColumn;
-
- } else {
- return true;
- }
-
-
- //Case 1
- //Check for possible relocation within the binding statement
-
- //Rewritten to (function <token>() { { }})
- //The offset 16 is position of inner lbrace without token length.
- const int offset = 16;
-
- //Case 2
- if (statementStartLine == *line) {
- if (sourceStartLine == *line)
- *column = offset + ast->qualifiedId->identifierToken.length;
- done = true;
- }
-
- //Case 3
- if (statementStartLine > *line) {
- *line = statementStartLine;
- if (sourceStartLine == *line)
- *column = offset + ast->qualifiedId->identifierToken.length;
- else
- *column = statementColumn;
- done = true;
- }
- return true;
- }
-
- bool visit(FunctionDeclaration *ast) {
- quint32 sourceStartLine = ast->firstSourceLocation().startLine;
- quint32 sourceStartColumn = ast->firstSourceLocation().startColumn;
- quint32 statementStartLine = ast->body->firstSourceLocation().startLine;
- quint32 statementColumn = ast->body->firstSourceLocation().startColumn;
-
- //Case 1
- //Check for possible relocation within the function declaration
-
- //Case 2
- if (statementStartLine == *line) {
- if (sourceStartLine == *line)
- *column = statementColumn - sourceStartColumn + 1;
- done = true;
- }
+ Continue,
+ StepIn,
+ StepOut,
+ Next
+};
- //Case 3
- if (statementStartLine > *line) {
- *line = statementStartLine;
- if (sourceStartLine == *line)
- *column = statementColumn - sourceStartColumn + 1;
- else
- *column = statementColumn;
- done = true;
- }
- return true;
- }
+struct QmlV8ObjectData
+{
+ int handle;
+ QByteArray name;
+ QByteArray type;
+ QVariant value;
+ QVariantList properties;
+};
- bool visit(EmptyStatement *ast)
- {
- *line = ast->lastSourceLocation().startLine + 1;
- return true;
- }
-
- bool visit(VariableStatement *ast) { test(ast); return true; }
- bool visit(VariableDeclarationList *ast) { test(ast); return true; }
- bool visit(VariableDeclaration *ast) { test(ast); return true; }
- bool visit(ExpressionStatement *ast) { test(ast); return true; }
- bool visit(IfStatement *ast) { test(ast); return true; }
- bool visit(DoWhileStatement *ast) { test(ast); return true; }
- bool visit(WhileStatement *ast) { test(ast); return true; }
- bool visit(ForStatement *ast) { test(ast); return true; }
- bool visit(LocalForStatement *ast) { test(ast); return true; }
- bool visit(ForEachStatement *ast) { test(ast); return true; }
- bool visit(LocalForEachStatement *ast) { test(ast); return true; }
- bool visit(ContinueStatement *ast) { test(ast); return true; }
- bool visit(BreakStatement *ast) { test(ast); return true; }
- bool visit(ReturnStatement *ast) { test(ast); return true; }
- bool visit(WithStatement *ast) { test(ast); return true; }
- bool visit(SwitchStatement *ast) { test(ast); return true; }
- bool visit(CaseBlock *ast) { test(ast); return true; }
- bool visit(CaseClauses *ast) { test(ast); return true; }
- bool visit(CaseClause *ast) { test(ast); return true; }
- bool visit(DefaultClause *ast) { test(ast); return true; }
- bool visit(LabelledStatement *ast) { test(ast); return true; }
- bool visit(ThrowStatement *ast) { test(ast); return true; }
- bool visit(TryStatement *ast) { test(ast); return true; }
- bool visit(Catch *ast) { test(ast); return true; }
- bool visit(Finally *ast) { test(ast); return true; }
- bool visit(FunctionExpression *ast) { test(ast); return true; }
- bool visit(DebuggerStatement *ast) { test(ast); return true; }
-
- void test(Node *ast)
- {
- quint32 statementStartLine = ast->firstSourceLocation().startLine;
- //Case 1/2
- if (statementStartLine <= *line &&
- *line <= ast->lastSourceLocation().startLine)
- done = true;
-
- //Case 3
- if (statementStartLine > *line) {
- *line = statementStartLine;
- *column = ast->firstSourceLocation().startColumn;
- done = true;
- }
- }
+class QmlEnginePrivate : QmlDebugClient
+{
+public:
+ QmlEnginePrivate(QmlEngine *engine_, QmlDebugConnection *connection_)
+ : QmlDebugClient(QLatin1String("V8Debugger"), connection_),
+ engine(engine_),
+ inspectorAdapter(engine, connection_),
+ connection(connection_)
+ {}
+
+ void sendMessage(const QByteArray &msg);
+ void messageReceived(const QByteArray &data);
+ void stateChanged(State state);
+
+ void connect();
+ void disconnect();
+
+ void continueDebugging(StepAction stepAction);
+
+ void evaluate(const QString expr, bool global = false, bool disableBreak = false,
+ int frame = -1, bool addContext = false);
+ void lookup(const QList<int> handles, bool includeSource = false);
+ void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false);
+ void frame(int number = -1);
+ void scope(int number = -1, int frameNumber = -1);
+ void scripts(int types = 4, const QList<int> ids = QList<int>(),
+ bool includeSource = false, const QVariant filter = QVariant());
+
+ void setBreakpoint(const QString type, const QString target,
+ bool enabled = true,int line = 0, int column = 0,
+ const QString condition = QString(), int ignoreCount = -1);
+ void clearBreakpoint(int breakpoint);
+ void setExceptionBreak(Exceptions type, bool enabled = false);
+
+ void version();
+ void clearCache();
+
+ void sendAndLogV8Request(const QJsonObject &request);
+
+ QByteArray packMessage(const QByteArray &type, const QByteArray &message = QByteArray());
+ QJsonObject initObject();
+
+ void expandObject(const QByteArray &iname, quint64 objectId);
+ void flushSendBuffer();
+
+ void updateStack(const QVariant &bodyVal, const QVariant &refsVal);
+ void expandLocalsAndWatchers(const QVariant &bodyVal, const QVariant &refsVal);
+ void updateEvaluationResult(int sequence, bool success, const QVariant &bodyVal, const QVariant &refsVal);
+ void setCurrentFrameDetails(const QVariant &bodyVal, const QVariant &refsVal);
+ void updateScope(const QVariant &bodyVal, const QVariant &refsVal);
+ void createWatchDataList(const WatchItem *parent, const QVariantList &properties, const QVariant &refsVal);
+ void highlightExceptionCode(int lineNumber, const QString &filePath, const QString &errorMessage);
+ StackFrame extractStackFrame(const QVariant &bodyVal, const QVariant &refsVal);
+
+ bool canEvaluateScript(const QString &script);
+ void updateScriptSource(const QString &fileName, int lineOffset, int columnOffset, const QString &source);
- bool done;
- quint32 *line;
- quint32 *column;
+public:
+ int sequence = -1;
+ QmlEngine *engine;
+ QHash<BreakpointModelId, int> breakpoints;
+ QHash<int, BreakpointModelId> breakpointsSync;
+ QList<int> breakpointsTemp;
+
+ QHash<int, QString> evaluatingExpression;
+ QHash<int, QByteArray> localsAndWatchers;
+ QList<int> updateLocalsAndWatchers;
+ QList<int> debuggerCommands;
+
+ //Cache
+ QList<int> currentFrameScopes;
+ QHash<int, int> stackIndexLookup;
+
+ StepAction previousStepAction = Continue;
+
+ QList<QByteArray> sendBuffer;
+
+ QHash<QString, QTextDocument*> sourceDocuments;
+ QHash<QString, QWeakPointer<BaseTextEditor> > sourceEditors;
+ InteractiveInterpreter interpreter;
+ ApplicationLauncher applicationLauncher;
+ QmlInspectorAdapter inspectorAdapter;
+ QmlOutputParser outputParser;
+
+ QTimer noDebugOutputTimer;
+ QHash<QString,Breakpoint> pendingBreakpoints;
+ QList<quint32> queryIds;
+ bool retryOnConnectFail = false;
+ bool automaticConnect = false;
+
+ QTimer connectionTimer;
+ QmlDebug::QmlDebugConnection *connection;
+ QmlDebug::QDebugMessageClient *msgClient = 0;
};
-ConsoleManagerInterface *qmlConsoleManager()
+static void updateDocument(IDocument *document, const QTextDocument *textDocument)
{
- return ConsoleManagerInterface::instance();
+ if (auto baseTextDocument = qobject_cast<TextDocument *>(document))
+ baseTextDocument->document()->setPlainText(textDocument->toPlainText());
}
+
///////////////////////////////////////////////////////////////////////
//
// QmlEngine
@@ -262,94 +226,97 @@ ConsoleManagerInterface *qmlConsoleManager()
///////////////////////////////////////////////////////////////////////
QmlEngine::QmlEngine(const DebuggerRunParameters &startParameters, DebuggerEngine *masterEngine)
- : DebuggerEngine(startParameters)
- , m_adapter(this)
- , m_inspectorAdapter(&m_adapter, this)
- , m_retryOnConnectFail(false)
- , m_automaticConnect(false)
+ : DebuggerEngine(startParameters),
+ d(new QmlEnginePrivate(this, new QmlDebugConnection(this)))
{
setObjectName(QLatin1String("QmlEngine"));
if (masterEngine)
setMasterEngine(masterEngine);
- connect(&m_adapter, SIGNAL(connectionError(QDebugSupport::Error)),
- SLOT(connectionError(QDebugSupport::Error)));
- connect(&m_adapter, SIGNAL(serviceConnectionError(QString)),
- SLOT(serviceConnectionError(QString)));
- connect(&m_adapter, SIGNAL(connected()),
- SLOT(connectionEstablished()));
- connect(&m_adapter, SIGNAL(connectionStartupFailed()),
- SLOT(connectionStartupFailed()));
-
- connect(stackHandler(), SIGNAL(stackChanged()),
- SLOT(updateCurrentContext()));
- connect(stackHandler(), SIGNAL(currentIndexChanged()),
- SLOT(updateCurrentContext()));
- connect(inspectorTreeView(), SIGNAL(currentIndexChanged(QModelIndex)),
+ connect(stackHandler(), &StackHandler::stackChanged,
+ this, &QmlEngine::updateCurrentContext);
+ connect(stackHandler(), &StackHandler::currentIndexChanged,
+ this, &QmlEngine::updateCurrentContext);
+ connect(inspectorView(), SIGNAL(currentIndexChanged(QModelIndex)),
SLOT(updateCurrentContext()));
- connect(m_inspectorAdapter.agent(), SIGNAL(
- expressionResult(quint32,QVariant)),
- SLOT(expressionEvaluated(quint32,QVariant)));
- connect(m_adapter.messageClient(),
- SIGNAL(message(QtMsgType,QString,
- QmlDebug::QDebugContextInfo)),
- SLOT(appendDebugOutput(QtMsgType,QString,
- QmlDebug::QDebugContextInfo)));
-
-
- connect(&m_applicationLauncher,
- SIGNAL(processExited(int,QProcess::ExitStatus)),
- SLOT(disconnected()));
- connect(&m_applicationLauncher,
- SIGNAL(appendMessage(QString,Utils::OutputFormat)),
- SLOT(appendMessage(QString,Utils::OutputFormat)));
- connect(&m_applicationLauncher,
- SIGNAL(processStarted()),
- &m_noDebugOutputTimer,
- SLOT(start()));
-
- m_outputParser.setNoOutputText(ProjectExplorer::ApplicationLauncher
- ::msgWinCannotRetrieveDebuggingOutput());
- connect(&m_outputParser, SIGNAL(waitingForConnectionOnPort(quint16)),
- this, SLOT(beginConnection(quint16)));
- connect(&m_outputParser, SIGNAL(noOutputMessage()),
- this, SLOT(tryToConnect()));
- connect(&m_outputParser, SIGNAL(errorMessage(QString)),
- this, SLOT(appStartupFailed(QString)));
+ connect(d->inspectorAdapter.agent(), &QmlInspectorAgent::expressionResult,
+ this, &QmlEngine::expressionEvaluated);
+
+ connect(&d->applicationLauncher, &ApplicationLauncher::processExited,
+ this, &QmlEngine::disconnected);
+ connect(&d->applicationLauncher, &ApplicationLauncher::appendMessage,
+ this, &QmlEngine::appendMessage);
+ connect(&d->applicationLauncher, &ApplicationLauncher::processStarted,
+ &d->noDebugOutputTimer, static_cast<void(QTimer::*)()>(&QTimer::start));
+
+ d->outputParser.setNoOutputText(ApplicationLauncher::msgWinCannotRetrieveDebuggingOutput());
+ connect(&d->outputParser, &QmlOutputParser::waitingForConnectionOnPort,
+ this, &QmlEngine::beginConnection);
+ connect(&d->outputParser, &QmlOutputParser::noOutputMessage,
+ this, [this] { tryToConnect(); });
+ connect(&d->outputParser, &QmlOutputParser::errorMessage,
+ this, &QmlEngine::appStartupFailed);
// Only wait 8 seconds for the 'Waiting for connection' on application output,
// then just try to connect (application output might be redirected / blocked)
- m_noDebugOutputTimer.setSingleShot(true);
- m_noDebugOutputTimer.setInterval(8000);
- connect(&m_noDebugOutputTimer, SIGNAL(timeout()), this, SLOT(tryToConnect()));
+ d->noDebugOutputTimer.setSingleShot(true);
+ d->noDebugOutputTimer.setInterval(8000);
+ connect(&d->noDebugOutputTimer, SIGNAL(timeout()), this, SLOT(tryToConnect()));
- ModelManagerInterface *mmIface = ModelManagerInterface::instance();
- if (mmIface) {
- connect(ModelManagerInterface::instance(), SIGNAL(documentUpdated(QmlJS::Document::Ptr)),
- this, SLOT(documentUpdated(QmlJS::Document::Ptr)));
+ if (auto mmIface = ModelManagerInterface::instance()) {
+ connect(mmIface, &ModelManagerInterface::documentUpdated,
+ this, &QmlEngine::documentUpdated);
}
// we won't get any debug output
if (startParameters.useTerminal) {
- m_noDebugOutputTimer.setInterval(0);
- m_retryOnConnectFail = true;
- m_automaticConnect = true;
+ d->noDebugOutputTimer.setInterval(0);
+ d->retryOnConnectFail = true;
+ d->automaticConnect = true;
}
- if (qmlConsoleManager())
- qmlConsoleManager()->setScriptEvaluator(this);
+
+ if (auto consoleManager = ConsoleManagerInterface::instance())
+ consoleManager->setScriptEvaluator(this);
+
+
+ d->connectionTimer.setInterval(4000);
+ d->connectionTimer.setSingleShot(true);
+ connect(&d->connectionTimer, &QTimer::timeout,
+ this, &QmlEngine::checkConnectionState);
+
+ connect(d->connection, &QmlDebugConnection::stateMessage,
+ this, &QmlEngine::showConnectionStateMessage);
+ connect(d->connection, &QmlDebugConnection::errorMessage,
+ this, &QmlEngine::showConnectionErrorMessage);
+ connect(d->connection, &QmlDebugConnection::error,
+ this, &QmlEngine::connectionErrorOccurred);
+ connect(d->connection, &QmlDebugConnection::opened,
+ &d->connectionTimer, &QTimer::stop);
+ connect(d->connection, &QmlDebugConnection::opened,
+ this, &QmlEngine::connectionEstablished);
+ connect(d->connection, &QmlDebugConnection::closed,
+ this, &QmlEngine::disconnected);
+
+ d->msgClient = new QDebugMessageClient(d->connection);
+ connect(d->msgClient, &QDebugMessageClient::newState,
+ this, &QmlEngine::clientStateChanged);
+ connect(d->msgClient, &QDebugMessageClient::message,
+ this, &appendDebugOutput);
}
QmlEngine::~QmlEngine()
{
- QSet<Core::IDocument *> documentsToClose;
+ QSet<IDocument *> documentsToClose;
- QHash<QString, QWeakPointer<TextEditor::BaseTextEditor> >::iterator iter;
- for (iter = m_sourceEditors.begin(); iter != m_sourceEditors.end(); ++iter) {
- QWeakPointer<TextEditor::BaseTextEditor> textEditPtr = iter.value();
+ QHash<QString, QWeakPointer<BaseTextEditor> >::iterator iter;
+ for (iter = d->sourceEditors.begin(); iter != d->sourceEditors.end(); ++iter) {
+ QWeakPointer<BaseTextEditor> textEditPtr = iter.value();
if (textEditPtr)
documentsToClose << textEditPtr.data()->document();
}
- Core::EditorManager::closeDocuments(documentsToClose.toList());
+ EditorManager::closeDocuments(documentsToClose.toList());
+
+ delete d;
}
void QmlEngine::setupInferior()
@@ -358,7 +325,7 @@ void QmlEngine::setupInferior()
notifyInferiorSetupOk();
- if (m_automaticConnect)
+ if (d->automaticConnect)
beginConnection();
}
@@ -373,7 +340,8 @@ void QmlEngine::connectionEstablished()
if (!watchHandler()->watcherNames().isEmpty())
synchronizeWatchers();
- connect(watchModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
+ connect(watchModel(), &QAbstractItemModel::layoutChanged,
+ this, &QmlEngine::synchronizeWatchers);
if (state() == EngineRunRequested)
notifyEngineRunAndInferiorRunOk();
@@ -382,13 +350,13 @@ void QmlEngine::connectionEstablished()
void QmlEngine::tryToConnect(quint16 port)
{
showMessage(QLatin1String("QML Debugger: No application output received in time, trying to connect ..."), LogStatus);
- m_retryOnConnectFail = true;
+ d->retryOnConnectFail = true;
if (state() == EngineRunRequested) {
if (isSlaveEngine()) {
// Probably cpp is being debugged and hence we did not get the output yet.
if (!masterEngine()->isDying()) {
- m_noDebugOutputTimer.setInterval(4000);
- m_noDebugOutputTimer.start();
+ d->noDebugOutputTimer.setInterval(4000);
+ d->noDebugOutputTimer.start();
}
else
appStartupFailed(tr("No application output received in time"));
@@ -396,15 +364,15 @@ void QmlEngine::tryToConnect(quint16 port)
beginConnection(port);
}
} else {
- m_automaticConnect = true;
+ d->automaticConnect = true;
}
}
void QmlEngine::beginConnection(quint16 port)
{
- m_noDebugOutputTimer.stop();
+ d->noDebugOutputTimer.stop();
- if (state() != EngineRunRequested && m_retryOnConnectFail)
+ if (state() != EngineRunRequested && d->retryOnConnectFail)
return;
QTC_ASSERT(state() == EngineRunRequested, return);
@@ -428,19 +396,24 @@ void QmlEngine::beginConnection(quint16 port)
if (runParameters().qmlServerPort > 0)
port = runParameters().qmlServerPort;
- m_adapter.beginConnectionTcp(host, port);
-}
+ if (!d->connection || d->connection->isOpen())
+ return;
+ d->connection->connectToHost(host, port);
+
+ //A timeout to check the connection state
+ d->connectionTimer.start();
+}
void QmlEngine::connectionStartupFailed()
{
- if (m_retryOnConnectFail) {
+ if (d->retryOnConnectFail) {
// retry after 3 seconds ...
QTimer::singleShot(3000, this, SLOT(beginConnection()));
return;
}
- QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow());
+ QMessageBox *infoBox = new QMessageBox(ICore::mainWindow());
infoBox->setIcon(QMessageBox::Critical);
infoBox->setWindowTitle(tr("Qt Creator"));
infoBox->setText(tr("Could not connect to the in-process QML debugger."
@@ -450,8 +423,8 @@ void QmlEngine::connectionStartupFailed()
infoBox->setDefaultButton(QMessageBox::Retry);
infoBox->setModal(true);
- connect(infoBox, SIGNAL(finished(int)),
- this, SLOT(errorMessageBoxFinished(int)));
+ connect(infoBox, &QDialog::finished,
+ this, &QmlEngine::errorMessageBoxFinished);
infoBox->show();
}
@@ -462,14 +435,14 @@ void QmlEngine::appStartupFailed(const QString &errorMessage)
"\n%1").arg(errorMessage);
if (isMasterEngine()) {
- QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow());
+ QMessageBox *infoBox = new QMessageBox(ICore::mainWindow());
infoBox->setIcon(QMessageBox::Critical);
infoBox->setWindowTitle(tr("Qt Creator"));
infoBox->setText(error);
infoBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Help);
infoBox->setDefaultButton(QMessageBox::Ok);
- connect(infoBox, SIGNAL(finished(int)),
- this, SLOT(errorMessageBoxFinished(int)));
+ connect(infoBox, &QDialog::finished,
+ this, &QmlEngine::errorMessageBoxFinished);
infoBox->show();
} else {
showMessage(error, StatusBar);
@@ -486,7 +459,7 @@ void QmlEngine::errorMessageBoxFinished(int result)
break;
}
case QMessageBox::Help: {
- Core::HelpManager::handleHelpRequest(QLatin1String("qthelp://org.qt-project.qtcreator/doc/creator-debugging-qml.html"));
+ HelpManager::handleHelpRequest(QLatin1String("qthelp://org.qt-project.qtcreator/doc/creator-debugging-qml.html"));
// fall through
}
default:
@@ -500,37 +473,15 @@ void QmlEngine::errorMessageBoxFinished(int result)
}
}
-void QmlEngine::connectionError(QDebugSupport::Error error)
+void QmlEngine::filterApplicationMessage(const QString &output, int /*channel*/) const
{
- if (error == QDebugSupport::RemoteClosedConnectionError)
- showMessage(tr("QML Debugger: Remote host closed connection."), StatusBar);
-
- if (!isSlaveEngine()) { // normal flow for slave engine when gdb exits
- notifyInferiorSpontaneousStop();
- notifyInferiorIll();
- }
-}
-
-void QmlEngine::serviceConnectionError(const QString &serviceName)
-{
- showMessage(tr("QML Debugger: Could not connect to service \"%1\".")
- .arg(serviceName), StatusBar);
-}
-
-bool QmlEngine::canDisplayTooltip() const
-{
- return false;
-}
-
-void QmlEngine::filterApplicationMessage(const QString &output, int /*channel*/)
-{
- m_outputParser.processOutput(output);
+ d->outputParser.processOutput(output);
}
void QmlEngine::showMessage(const QString &msg, int channel, int timeout) const
{
if (channel == AppOutput || channel == AppError)
- const_cast<QmlEngine*>(this)->filterApplicationMessage(msg, channel);
+ filterApplicationMessage(msg, channel);
DebuggerEngine::showMessage(msg, channel, timeout);
}
@@ -539,25 +490,23 @@ void QmlEngine::gotoLocation(const Location &location)
const QString fileName = location.fileName();
if (QUrl(fileName).isLocalFile()) {
// internal file from source files -> show generated .js
- QTC_ASSERT(m_sourceDocuments.contains(fileName), return);
+ QTC_ASSERT(d->sourceDocuments.contains(fileName), return);
QString titlePattern = tr("JS Source for %1").arg(fileName);
//Check if there are open documents with the same title
- foreach (Core::IDocument *document, Core::DocumentModel::openedDocuments()) {
+ foreach (IDocument *document, DocumentModel::openedDocuments()) {
if (document->displayName() == titlePattern) {
- Core::EditorManager::activateEditorForDocument(document);
+ EditorManager::activateEditorForDocument(document);
return;
}
}
- Core::IEditor *editor = Core::EditorManager::openEditorWithContents(
+ IEditor *editor = EditorManager::openEditorWithContents(
QmlJSEditor::Constants::C_QMLJSEDITOR_ID, &titlePattern);
if (editor) {
editor->document()->setProperty(Constants::OPENED_BY_DEBUGGER, true);
- QPlainTextEdit *plainTextEdit =
- qobject_cast<QPlainTextEdit *>(editor->widget());
- if (plainTextEdit)
+ if (auto plainTextEdit = qobject_cast<QPlainTextEdit *>(editor->widget()))
plainTextEdit->setReadOnly(true);
- updateDocument(editor->document(), m_sourceDocuments.value(fileName));
+ updateDocument(editor->document(), d->sourceDocuments.value(fileName));
}
} else {
DebuggerEngine::gotoLocation(location);
@@ -566,8 +515,15 @@ void QmlEngine::gotoLocation(const Location &location)
void QmlEngine::closeConnection()
{
- disconnect(watchModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
- m_adapter.closeConnection();
+ disconnect(watchModel(), &QAbstractItemModel::layoutChanged,
+ this, &QmlEngine::synchronizeWatchers);
+
+ if (d->connectionTimer.isActive()) {
+ d->connectionTimer.stop();
+ } else {
+ if (d->connection)
+ d->connection->close();
+ }
}
void QmlEngine::runEngine()
@@ -576,25 +532,25 @@ void QmlEngine::runEngine()
if (!isSlaveEngine()) {
if (runParameters().startMode == AttachToRemoteServer)
- m_noDebugOutputTimer.start();
+ d->noDebugOutputTimer.start();
else if (runParameters().startMode == AttachToRemoteProcess)
beginConnection();
else
startApplicationLauncher();
} else {
- m_noDebugOutputTimer.start();
+ d->noDebugOutputTimer.start();
}
}
void QmlEngine::startApplicationLauncher()
{
- if (!m_applicationLauncher.isRunning()) {
+ if (!d->applicationLauncher.isRunning()) {
appendMessage(tr("Starting %1 %2").arg(
QDir::toNativeSeparators(runParameters().executable),
runParameters().processArgs)
+ QLatin1Char('\n')
, Utils::NormalMessageFormat);
- m_applicationLauncher.start(ProjectExplorer::ApplicationLauncher::Gui,
+ d->applicationLauncher.start(ApplicationLauncher::Gui,
runParameters().executable,
runParameters().processArgs);
}
@@ -602,10 +558,10 @@ void QmlEngine::startApplicationLauncher()
void QmlEngine::stopApplicationLauncher()
{
- if (m_applicationLauncher.isRunning()) {
- disconnect(&m_applicationLauncher, SIGNAL(processExited(int,QProcess::ExitStatus)),
- this, SLOT(disconnected()));
- m_applicationLauncher.stop();
+ if (d->applicationLauncher.isRunning()) {
+ disconnect(&d->applicationLauncher, &ApplicationLauncher::processExited,
+ this, &QmlEngine::disconnected);
+ d->applicationLauncher.stop();
}
}
@@ -622,12 +578,12 @@ void QmlEngine::notifyEngineRemoteSetupFinished(const RemoteSetupResult &result)
// The remote setup can take while especialy with mixed debugging.
// Just waiting for 8 seconds is not enough. Increase the timeout
// to 60 s
- // In case we get an output the m_outputParser will start the connection.
- m_noDebugOutputTimer.setInterval(60000);
+ // In case we get an output the d->outputParser will start the connection.
+ d->noDebugOutputTimer.setInterval(60000);
}
else {
if (isMasterEngine())
- QMessageBox::critical(Core::ICore::dialogParent(), tr("Failed to start application"),
+ QMessageBox::critical(ICore::dialogParent(), tr("Failed to start application"),
tr("Application startup failed: %1").arg(result.reason));
notifyEngineSetupFailed();
}
@@ -646,16 +602,15 @@ void QmlEngine::notifyEngineRemoteServerRunning(const QByteArray &serverChannel,
notifyEngineSetupOk();
// The remote setup can take a while especially with mixed debugging.
- // Just waiting for 8 seconds is not enough. Increase the timeout
- // to 60 s
- // In case we get an output the m_outputParser will start the connection.
- m_noDebugOutputTimer.setInterval(60000);
+ // Just waiting for 8 seconds is not enough. Increase the timeout to 60 s.
+ // In case we get an output the d->outputParser will start the connection.
+ d->noDebugOutputTimer.setInterval(60000);
}
void QmlEngine::shutdownInferior()
{
- if (m_adapter.activeDebuggerClient())
- m_adapter.activeDebuggerClient()->endSession();
+ // End session.
+ d->disconnect();
if (isSlaveEngine())
resetLocation();
@@ -667,12 +622,11 @@ void QmlEngine::shutdownInferior()
void QmlEngine::shutdownEngine()
{
- if (m_adapter.activeDebuggerClient())
- m_adapter.activeDebuggerClient()->resetSession();
+ clearExceptionSelection();
- if (qmlConsoleManager())
- qmlConsoleManager()->setScriptEvaluator(0);
- m_noDebugOutputTimer.stop();
+ if (auto consoleManager = ConsoleManagerInterface::instance())
+ consoleManager->setScriptEvaluator(0);
+ d->noDebugOutputTimer.stop();
// double check (ill engine?):
stopApplicationLauncher();
@@ -688,12 +642,12 @@ void QmlEngine::setupEngine()
// we need to get the port first
notifyEngineRequestRemoteSetup();
} else {
- m_applicationLauncher.setEnvironment(runParameters().environment);
- m_applicationLauncher.setWorkingDirectory(runParameters().workingDirectory);
+ d->applicationLauncher.setEnvironment(runParameters().environment);
+ d->applicationLauncher.setWorkingDirectory(runParameters().workingDirectory);
// We can't do this in the constructore because runControl() isn't yet defined
- connect(&m_applicationLauncher, SIGNAL(bringToForegroundRequested(qint64)),
- runControl(), SLOT(bringApplicationToForeground(qint64)),
+ connect(&d->applicationLauncher, &ApplicationLauncher::bringToForegroundRequested,
+ runControl(), &RunControl::bringApplicationToForeground,
Qt::UniqueConnection);
notifyEngineSetupOk();
@@ -703,8 +657,8 @@ void QmlEngine::setupEngine()
void QmlEngine::continueInferior()
{
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
- if (m_adapter.activeDebuggerClient())
- m_adapter.activeDebuggerClient()->continueInferior();
+ clearExceptionSelection();
+ d->continueDebugging(Continue);
resetLocation();
notifyInferiorRunRequested();
notifyInferiorRunOk();
@@ -712,39 +666,39 @@ void QmlEngine::continueInferior()
void QmlEngine::interruptInferior()
{
- if (m_adapter.activeDebuggerClient())
- m_adapter.activeDebuggerClient()->interruptInferior();
+ showMessage(_(INTERRUPT), LogInput);
+ d->sendMessage(d->packMessage(INTERRUPT));
notifyInferiorStopOk();
}
void QmlEngine::executeStep()
{
- if (m_adapter.activeDebuggerClient())
- m_adapter.activeDebuggerClient()->executeStep();
+ clearExceptionSelection();
+ d->continueDebugging(StepIn);
notifyInferiorRunRequested();
notifyInferiorRunOk();
}
void QmlEngine::executeStepI()
{
- if (m_adapter.activeDebuggerClient())
- m_adapter.activeDebuggerClient()->executeStepI();
+ clearExceptionSelection();
+ d->continueDebugging(StepIn);
notifyInferiorRunRequested();
notifyInferiorRunOk();
}
void QmlEngine::executeStepOut()
{
- if (m_adapter.activeDebuggerClient())
- m_adapter.activeDebuggerClient()->executeStepOut();
+ clearExceptionSelection();
+ d->continueDebugging(StepOut);
notifyInferiorRunRequested();
notifyInferiorRunOk();
}
void QmlEngine::executeNext()
{
- if (m_adapter.activeDebuggerClient())
- m_adapter.activeDebuggerClient()->executeNext();
+ clearExceptionSelection();
+ d->continueDebugging(Next);
notifyInferiorRunRequested();
notifyInferiorRunOk();
}
@@ -765,8 +719,11 @@ void QmlEngine::executeRunToLine(const ContextData &data)
bool valid;
if (adjustBreakpointLineAndColumn(data.fileName, &line, &column, &valid))
modifiedData.lineNumber = line;
- if (m_adapter.activeDebuggerClient())
- m_adapter.activeDebuggerClient()->executeRunToLine(modifiedData);
+ d->setBreakpoint(QString(_(SCRIPTREGEXP)), modifiedData.fileName,
+ true, modifiedData.lineNumber);
+ clearExceptionSelection();
+ d->continueDebugging(Continue);
+
notifyInferiorRunRequested();
notifyInferiorRunOk();
}
@@ -788,8 +745,10 @@ void QmlEngine::activateFrame(int index)
if (state() != InferiorStopOk && state() != InferiorUnrunnable)
return;
- if (m_adapter.activeDebuggerClient())
- m_adapter.activeDebuggerClient()->activateFrame(index);
+ if (index != stackHandler()->currentIndex())
+ d->frame(d->stackIndexLookup.value(index));
+
+ stackHandler()->setCurrentIndex(index);
gotoLocation(stackHandler()->frames().value(index));
}
@@ -811,31 +770,39 @@ void QmlEngine::insertBreakpoint(Breakpoint bp)
bool valid = false;
if (!adjustBreakpointLineAndColumn(params.fileName, &line, &column,
&valid)) {
- pendingBreakpoints.insertMulti(params.fileName, bp);
+ d->pendingBreakpoints.insertMulti(params.fileName, bp);
return;
}
if (!valid)
return;
}
- if (m_adapter.activeDebuggerClient()) {
- m_adapter.activeDebuggerClient()->insertBreakpoint(bp, line, column);
- } else {
- foreach (QmlV8DebuggerClient *client, m_adapter.debuggerClients()) {
- client->insertBreakpoint(bp, line, column);
- }
+ if (params.type == BreakpointAtJavaScriptThrow) {
+ bp.notifyBreakpointInsertOk();
+ d->setExceptionBreak(AllExceptions, params.enabled);
+
+ } else if (params.type == BreakpointByFileAndLine) {
+ d->setBreakpoint(QString(_(SCRIPTREGEXP)), params.fileName,
+ params.enabled, line, column,
+ QLatin1String(params.condition), params.ignoreCount);
+
+ } else if (params.type == BreakpointOnQmlSignalEmit) {
+ d->setBreakpoint(QString(_(EVENT)), params.functionName, params.enabled);
+ bp.notifyBreakpointInsertOk();
}
+
+ d->breakpointsSync.insert(d->sequence, bp.id());
}
void QmlEngine::removeBreakpoint(Breakpoint bp)
{
const BreakpointParameters &params = bp.parameters();
if (params.type == BreakpointByFileAndLine &&
- pendingBreakpoints.contains(params.fileName)) {
- auto it = pendingBreakpoints.find(params.fileName);
- while (it != pendingBreakpoints.end() && it.key() == params.fileName) {
+ d->pendingBreakpoints.contains(params.fileName)) {
+ auto it = d->pendingBreakpoints.find(params.fileName);
+ while (it != d->pendingBreakpoints.end() && it.key() == params.fileName) {
if (it.value() == bp.id()) {
- pendingBreakpoints.erase(it);
+ d->pendingBreakpoints.erase(it);
return;
}
++it;
@@ -846,13 +813,15 @@ void QmlEngine::removeBreakpoint(Breakpoint bp)
QTC_ASSERT(state == BreakpointRemoveRequested, qDebug() << bp << this << state);
bp.notifyBreakpointRemoveProceeding();
- if (m_adapter.activeDebuggerClient()) {
- m_adapter.activeDebuggerClient()->removeBreakpoint(bp);
- } else {
- foreach (QmlV8DebuggerClient *client, m_adapter.debuggerClients()) {
- client->removeBreakpoint(bp);
- }
- }
+ int breakpoint = d->breakpoints.value(bp.id());
+ d->breakpoints.remove(bp.id());
+
+ if (params.type == BreakpointAtJavaScriptThrow)
+ d->setExceptionBreak(AllExceptions);
+ else if (params.type == BreakpointOnQmlSignalEmit)
+ d->setBreakpoint(QString(_(EVENT)), params.functionName, false);
+ else
+ d->clearBreakpoint(breakpoint);
if (bp.state() == BreakpointRemoveProceeding)
bp.notifyBreakpointRemoveOk();
@@ -864,12 +833,24 @@ void QmlEngine::changeBreakpoint(Breakpoint bp)
QTC_ASSERT(state == BreakpointChangeRequested, qDebug() << bp << this << state);
bp.notifyBreakpointChangeProceeding();
- if (m_adapter.activeDebuggerClient()) {
- m_adapter.activeDebuggerClient()->changeBreakpoint(bp);
+ const BreakpointParameters &params = bp.parameters();
+
+ BreakpointResponse br = bp.response();
+ if (params.type == BreakpointAtJavaScriptThrow) {
+ d->setExceptionBreak(AllExceptions, params.enabled);
+ br.enabled = params.enabled;
+ bp.setResponse(br);
+ } else if (params.type == BreakpointOnQmlSignalEmit) {
+ d->setBreakpoint(QString(_(EVENT)), params.functionName, params.enabled);
+ br.enabled = params.enabled;
+ bp.setResponse(br);
} else {
- foreach (QmlV8DebuggerClient *client, m_adapter.debuggerClients()) {
- client->changeBreakpoint(bp);
- }
+ //V8 supports only minimalistic changes in breakpoint
+ //Remove the breakpoint and add again
+ bp.notifyBreakpointChangeOk();
+ bp.removeBreakpoint();
+ BreakHandler *handler = d->engine->breakHandler();
+ handler->appendBreakpoint(params);
}
if (bp.state() == BreakpointChangeProceeding)
@@ -918,14 +899,6 @@ void QmlEngine::attemptBreakpointSynchronization()
}
DebuggerEngine::attemptBreakpointSynchronization();
-
- if (m_adapter.activeDebuggerClient()) {
- m_adapter.activeDebuggerClient()->synchronizeBreakpoints();
- } else {
- foreach (QmlV8DebuggerClient *client, m_adapter.debuggerClients()) {
- client->synchronizeBreakpoints();
- }
- }
}
bool QmlEngine::acceptsBreakpoint(Breakpoint bp) const
@@ -937,10 +910,10 @@ bool QmlEngine::acceptsBreakpoint(Breakpoint bp) const
//TODO: enable setting of breakpoints before start of debug session
//For now, the event breakpoint can be set after the activeDebuggerClient is known
//This is because the older client does not support BreakpointOnQmlSignalHandler
- bool acceptBreakpoint = false;
- if (m_adapter.activeDebuggerClient())
- acceptBreakpoint = m_adapter.activeDebuggerClient()->acceptsBreakpoint(bp);
- return acceptBreakpoint;
+ BreakpointType type = bp.type();
+ return type == BreakpointOnQmlSignalEmit
+ || type == BreakpointByFileAndLine
+ || type == BreakpointAtJavaScriptThrow;
}
void QmlEngine::loadSymbols(const QString &moduleName)
@@ -958,8 +931,7 @@ void QmlEngine::reloadModules()
void QmlEngine::reloadSourceFiles()
{
- if (m_adapter.activeDebuggerClient())
- m_adapter.activeDebuggerClient()->getSourceFiles();
+ d->scripts(4, QList<int>(), true, QVariant());
}
void QmlEngine::requestModuleSymbols(const QString &moduleName)
@@ -967,12 +939,6 @@ void QmlEngine::requestModuleSymbols(const QString &moduleName)
Q_UNUSED(moduleName)
}
-//////////////////////////////////////////////////////////////////////
-//
-// Tooltip specific stuff
-//
-//////////////////////////////////////////////////////////////////////
-
bool QmlEngine::canHandleToolTip(const DebuggerToolTipContext &) const
{
// This is processed by QML inspector, which has dependencies to
@@ -981,20 +947,23 @@ bool QmlEngine::canHandleToolTip(const DebuggerToolTipContext &) const
return true;
}
-//////////////////////////////////////////////////////////////////////
-//
-// Watch specific stuff
-//
-//////////////////////////////////////////////////////////////////////
-
void QmlEngine::assignValueInDebugger(WatchItem *item,
const QString &expression, const QVariant &valueV)
{
if (!expression.isEmpty()) {
- if (item->isInspect() && m_inspectorAdapter.agent())
- m_inspectorAdapter.agent()->assignValue(item, expression, valueV);
- else if (m_adapter.activeDebuggerClient())
- m_adapter.activeDebuggerClient()->assignValueInDebugger(item, expression, valueV);
+ if (item->isInspect() && d->inspectorAdapter.agent()) {
+ d->inspectorAdapter.agent()->assignValue(item, expression, valueV);
+ } else {
+ StackHandler *handler = stackHandler();
+ QString expression = QString(_("%1 = %2;")).arg(expression).arg(valueV.toString());
+ if (handler->isContentsValid() && handler->currentFrame().isUsable()) {
+ d->evaluate(expression, false, false, handler->currentIndex());
+ d->updateLocalsAndWatchers.append(d->sequence);
+ } else {
+ showMessage(QString(_("Cannot evaluate %1 in current stack frame")).arg(
+ expression), ConsoleOutput);
+ }
+ }
}
}
@@ -1008,14 +977,11 @@ void QmlEngine::updateWatchData(const QByteArray &iname)
return;
if (item->isInspect()) {
- m_inspectorAdapter.agent()->updateWatchData(*item);
+ d->inspectorAdapter.agent()->updateWatchData(*item);
} else {
- if (!item->name.isEmpty() && m_adapter.activeDebuggerClient()) {
- if (item->isValueNeeded())
- m_adapter.activeDebuggerClient()->updateWatchData(*item);
- if (item->isChildrenNeeded() && watchHandler()->isExpandedIName(item->iname)) {
- m_adapter.activeDebuggerClient()->expandObject(item->iname, item->id);
- }
+ if (!item->name.isEmpty()) {
+ if (item->isChildrenNeeded() && watchHandler()->isExpandedIName(item->iname))
+ d->expandObject(item->iname, item->id);
}
synchronizeWatchers();
}
@@ -1025,26 +991,30 @@ void QmlEngine::selectWatchData(const QByteArray &iname)
{
const WatchItem *item = watchHandler()->findItem(iname);
if (item && item->isInspect())
- m_inspectorAdapter.agent()->watchDataSelected(item->id);
+ d->inspectorAdapter.agent()->watchDataSelected(item->id);
}
void QmlEngine::synchronizeWatchers()
{
- QStringList watchedExpressions = watchHandler()->watchedExpressions();
+ if (state() != InferiorStopOk)
+ return;
+
+ QStringList watchers = watchHandler()->watchedExpressions();
+
// send watchers list
- if (m_adapter.activeDebuggerClient()) {
- m_adapter.activeDebuggerClient()->synchronizeWatchers(watchedExpressions);
- } else {
- foreach (QmlV8DebuggerClient *client, m_adapter.debuggerClients())
- client->synchronizeWatchers(watchedExpressions);
+ foreach (const QString &exp, watchers) {
+ StackHandler *handler = stackHandler();
+ if (handler->isContentsValid() && handler->currentFrame().isUsable()) {
+ d->evaluate(exp, false, false, handler->currentIndex());
+ d->evaluatingExpression.insert(d->sequence, exp);
+ }
}
}
-ConsoleItem *constructLogItemTree(ConsoleItem *parent,
- const QVariant &result,
- const QString &key = QString())
+static ConsoleItem *constructLogItemTree(ConsoleItem *parent,
+ const QVariant &result,
+ const QString &key = QString())
{
- using namespace QmlJS;
bool sorted = boolSetting(SortStructMembers);
if (!result.isValid())
return 0;
@@ -1086,13 +1056,10 @@ ConsoleItem *constructLogItemTree(ConsoleItem *parent,
void QmlEngine::expressionEvaluated(quint32 queryId, const QVariant &result)
{
- if (queryIds.contains(queryId)) {
- queryIds.removeOne(queryId);
- using namespace QmlJS;
- ConsoleManagerInterface *consoleManager = qmlConsoleManager();
- if (consoleManager) {
- ConsoleItem *item = constructLogItemTree(consoleManager->rootItem(), result);
- if (item)
+ if (d->queryIds.contains(queryId)) {
+ d->queryIds.removeOne(queryId);
+ if (auto consoleManager = ConsoleManagerInterface::instance()) {
+ if (ConsoleItem *item = constructLogItemTree(consoleManager->rootItem(), result))
consoleManager->printToConsolePane(item);
}
}
@@ -1116,18 +1083,12 @@ bool QmlEngine::hasCapability(unsigned cap) const
void QmlEngine::quitDebugger()
{
- m_noDebugOutputTimer.stop();
- m_automaticConnect = false;
- m_retryOnConnectFail = false;
+ d->noDebugOutputTimer.stop();
+ d->automaticConnect = false;
+ d->retryOnConnectFail = false;
DebuggerEngine::quitDebugger();
}
-void QmlEngine::inferiorSpontaneousStop()
-{
- if (state() == InferiorRunOk)
- notifyInferiorSpontaneousStop();
-}
-
void QmlEngine::disconnected()
{
showMessage(tr("QML Debugger disconnected."), StatusBar);
@@ -1137,9 +1098,9 @@ void QmlEngine::disconnected()
void QmlEngine::documentUpdated(Document::Ptr doc)
{
QString fileName = doc->fileName();
- if (pendingBreakpoints.contains(fileName)) {
- QList<Breakpoint> bps = pendingBreakpoints.values(fileName);
- pendingBreakpoints.remove(fileName);
+ if (d->pendingBreakpoints.contains(fileName)) {
+ QList<Breakpoint> bps = d->pendingBreakpoints.values(fileName);
+ d->pendingBreakpoints.remove(fileName);
foreach (const Breakpoint bp, bps)
insertBreakpoint(bp);
}
@@ -1151,13 +1112,12 @@ void QmlEngine::updateCurrentContext()
if (state() == InferiorStopOk) {
context = stackHandler()->currentFrame().function;
} else {
- QModelIndex currentIndex = inspectorTreeView()->currentIndex();
+ QModelIndex currentIndex = inspectorView()->currentIndex();
const WatchData *currentData = watchHandler()->watchItem(currentIndex);
if (!currentData)
return;
const WatchData *parentData = watchHandler()->watchItem(currentIndex.parent());
- const WatchData *grandParentData = watchHandler()->watchItem(
- currentIndex.parent().parent());
+ const WatchData *grandParentData = watchHandler()->watchItem(currentIndex.parent().parent());
if (currentData->id != parentData->id)
context = currentData->name;
else if (parentData->id != grandParentData->id)
@@ -1168,44 +1128,24 @@ void QmlEngine::updateCurrentContext()
synchronizeWatchers();
- ConsoleManagerInterface *consoleManager = qmlConsoleManager();
- if (consoleManager)
+ if (auto consoleManager = ConsoleManagerInterface::instance())
consoleManager->setContext(tr("Context:") + QLatin1Char(' ') + context);
}
-void QmlEngine::appendDebugOutput(QtMsgType type, const QString &message,
- const QmlDebug::QDebugContextInfo &info)
+void QmlEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages languages)
{
- using namespace QmlJS;
- ConsoleItem::ItemType itemType;
- switch (type) {
- case QtDebugMsg:
- itemType = ConsoleItem::DebugType;
- break;
- case QtWarningMsg:
- itemType = ConsoleItem::WarningType;
- break;
- case QtCriticalMsg:
- case QtFatalMsg:
- itemType = ConsoleItem::ErrorType;
- break;
- default:
- //This case is not possible
+ if (!(languages & QmlLanguage))
return;
- }
- ConsoleManagerInterface *consoleManager = qmlConsoleManager();
- if (consoleManager) {
- ConsoleItem *item = new ConsoleItem(consoleManager->rootItem(), itemType, message);
- item->file = info.file;
- item->line = info.line;
- consoleManager->printToConsolePane(item);
- }
-}
-void QmlEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages languages)
-{
- if ((languages & QmlLanguage) && m_adapter.activeDebuggerClient())
- m_adapter.activeDebuggerClient()->executeDebuggerCommand(command);
+ StackHandler *handler = stackHandler();
+ if (handler->isContentsValid() && handler->currentFrame().isUsable()) {
+ d->evaluate(command, false, false, handler->currentIndex());
+ d->debuggerCommands.append(d->sequence);
+ } else {
+ //Currently cannot evaluate if not in a javascript break
+ d->engine->showMessage(QString(_("Cannot evaluate %1 in current stack frame")).arg(
+ command), ConsoleOutput);
+ }
}
bool QmlEngine::evaluateScript(const QString &expression)
@@ -1214,17 +1154,15 @@ bool QmlEngine::evaluateScript(const QString &expression)
// Evaluate expression based on engine state
// When engine->state() == InferiorStopOk, the expression is sent to debuggerClient.
if (state() != InferiorStopOk) {
- QModelIndex currentIndex = inspectorTreeView()->currentIndex();
- QmlInspectorAgent *agent = m_inspectorAdapter.agent();
+ QModelIndex currentIndex = inspectorView()->currentIndex();
+ QmlInspectorAgent *agent = d->inspectorAdapter.agent();
quint32 queryId = agent->queryExpressionResult(watchHandler()->watchItem(currentIndex)->id,
expression);
if (queryId) {
- queryIds << queryId;
+ d->queryIds.append(queryId);
} else {
didEvaluate = false;
- using namespace QmlJS;
- ConsoleManagerInterface *consoleManager = qmlConsoleManager();
- if (consoleManager) {
+ if (auto consoleManager = ConsoleManagerInterface::instance()) {
consoleManager->printToConsolePane(ConsoleItem::ErrorType,
_("Error evaluating expression."));
}
@@ -1235,42 +1173,15 @@ bool QmlEngine::evaluateScript(const QString &expression)
return didEvaluate;
}
-QString QmlEngine::qmlImportPath() const
-{
- return runParameters().environment.value(QLatin1String("QML_IMPORT_PATH"));
-}
-
-void QmlEngine::logMessage(const QString &service, LogDirection direction, const QString &message)
-{
- QString msg = service;
- msg += direction == LogSend ? QLatin1String(": sending ") : QLatin1String(": receiving ");
- msg += message;
- showMessage(msg, LogDebug);
-}
-
-void QmlEngine::setSourceFiles(const QStringList &fileNames)
-{
- QMap<QString,QString> files;
- foreach (const QString &file, fileNames) {
- QString shortName = file;
- QString fullName = toFileInProject(file);
- files.insert(shortName, fullName);
- }
-
- sourceFilesHandler()->setSourceFiles(files);
- //update open editors
-
-}
-
-void QmlEngine::updateScriptSource(const QString &fileName, int lineOffset, int columnOffset,
- const QString &source)
+void QmlEnginePrivate::updateScriptSource(const QString &fileName, int lineOffset, int columnOffset,
+ const QString &source)
{
QTextDocument *document = 0;
- if (m_sourceDocuments.contains(fileName)) {
- document = m_sourceDocuments.value(fileName);
+ if (sourceDocuments.contains(fileName)) {
+ document = sourceDocuments.value(fileName);
} else {
document = new QTextDocument(this);
- m_sourceDocuments.insert(fileName, document);
+ sourceDocuments.insert(fileName, document);
}
// We're getting an unordered set of snippets that can even interleave
@@ -1307,7 +1218,7 @@ void QmlEngine::updateScriptSource(const QString &fileName, int lineOffset, int
//update open editors
QString titlePattern = tr("JS Source for %1").arg(fileName);
//Check if there are open editors with the same title
- foreach (Core::IDocument *doc, Core::DocumentModel::openedDocuments()) {
+ foreach (IDocument *doc, DocumentModel::openedDocuments()) {
if (doc->displayName() == titlePattern) {
updateDocument(doc, document);
break;
@@ -1315,45 +1226,1488 @@ void QmlEngine::updateScriptSource(const QString &fileName, int lineOffset, int
}
}
-void QmlEngine::updateDocument(Core::IDocument *document, const QTextDocument *textDocument)
+bool QmlEnginePrivate::canEvaluateScript(const QString &script)
{
- TextEditor::TextDocument *baseTextDocument
- = qobject_cast<TextEditor::TextDocument *>(document);
- if (!baseTextDocument)
- return;
+ interpreter.clearText();
+ interpreter.appendText(script);
+ return interpreter.canEvaluate();
+}
+
+void QmlEngine::connectionErrorOccurred(QDebugSupport::Error error)
+{
+ // this is only an error if we are already connected and something goes wrong.
+ if (isConnected()) {
+ if (error == QDebugSupport::RemoteClosedConnectionError)
+ showMessage(tr("QML Debugger: Remote host closed connection."), StatusBar);
+
+ if (!isSlaveEngine()) { // normal flow for slave engine when gdb exits
+ notifyInferiorSpontaneousStop();
+ notifyInferiorIll();
+ }
+ } else {
+ d->connectionTimer.stop();
+ connectionStartupFailed();
+ }
+}
+
+void QmlEngine::clientStateChanged(QmlDebugClient::State state)
+{
+ QString serviceName;
+ float version = 0;
+ if (QmlDebugClient *client = qobject_cast<QmlDebugClient*>(sender())) {
+ serviceName = client->name();
+ version = client->remoteVersion();
+ }
+
+ logServiceStateChange(serviceName, version, state);
+}
+
+void QmlEngine::checkConnectionState()
+{
+ if (!isConnected()) {
+ closeConnection();
+ connectionStartupFailed();
+ }
+}
+
+bool QmlEngine::isConnected() const
+{
+ return d->connection->isOpen();
+}
+
+void QmlEngine::showConnectionStateMessage(const QString &message)
+{
+ showMessage(_("QML Debugger: ") + message, LogStatus);
+}
+
+void QmlEngine::showConnectionErrorMessage(const QString &message)
+{
+ showMessage(_("QML Debugger: ") + message, LogError);
+}
+
+void QmlEngine::logServiceStateChange(const QString &service, float version,
+ QmlDebugClient::State newState)
+{
+ switch (newState) {
+ case QmlDebugClient::Unavailable: {
+ showConnectionStateMessage(_("Status of \"%1\" Version: %2 changed to 'unavailable'.").
+ arg(service).arg(QString::number(version)));
+ break;
+ }
+ case QmlDebugClient::Enabled: {
+ showConnectionStateMessage(_("Status of \"%1\" Version: %2 changed to 'enabled'.").
+ arg(service).arg(QString::number(version)));
+ break;
+ }
+
+ case QmlDebugClient::NotConnected: {
+ showConnectionStateMessage(_("Status of \"%1\" Version: %2 changed to 'not connected'.").
+ arg(service).arg(QString::number(version)));
+ break;
+ }
+ }
+}
+
+void QmlEngine::logServiceActivity(const QString &service, const QString &logMessage)
+{
+ showMessage(service + QLatin1Char(' ') + logMessage, LogDebug);
+}
+
+void QmlEnginePrivate::connect()
+{
+ engine->showMessage(_(CONNECT), LogInput);
+ sendMessage(packMessage(CONNECT));
+}
+
+void QmlEnginePrivate::disconnect()
+{
+ // { "seq" : <number>,
+ // "type" : "request",
+ // "command" : "disconnect",
+ // }
+ QJsonObject jsonVal = initObject();
+ jsonVal.insert(_(COMMAND), _(DISCONNECT));
+
+ const QByteArray msg = QJsonDocument(jsonVal).toJson(QJsonDocument::Compact);
+ engine->showMessage(QString::fromUtf8(msg), LogInput);
+ sendMessage(packMessage(DISCONNECT, msg));
+}
+
+void QmlEnginePrivate::continueDebugging(StepAction action)
+{
+ // { "seq" : <number>,
+ // "type" : "request",
+ // "command" : "continue",
+ // "arguments" : { "stepaction" : <"in", "next" or "out">,
+ // "stepcount" : <number of steps (default 1)>
+ // }
+ // }
+ QJsonObject jsonVal = initObject();
+ jsonVal.insert(_(COMMAND), _(CONTINEDEBUGGING));
+
+ if (action != Continue) {
+ QJsonObject args;
+ switch (action) {
+ case StepIn:
+ args.insert(_(STEPACTION), _(IN));
+ break;
+ case StepOut:
+ args.insert(_(STEPACTION), _(OUT));
+ break;
+ case Next:
+ args.insert(_(STEPACTION), _(NEXT));
+ break;
+ default:break;
+ }
+
+ jsonVal.insert(_(ARGUMENTS), args);
+ }
+ sendAndLogV8Request(jsonVal);
+ previousStepAction = action;
+}
+
+void QmlEnginePrivate::evaluate(const QString expr, bool global,
+ bool disableBreak, int frame, bool addContext)
+{
+ // { "seq" : <number>,
+ // "type" : "request",
+ // "command" : "evaluate",
+ // "arguments" : { "expression" : <expression to evaluate>,
+ // "frame" : <number>,
+ // "global" : <boolean>,
+ // "disable_break" : <boolean>,
+ // "additional_context" : [
+ // { "name" : <name1>, "handle" : <handle1> },
+ // { "name" : <name2>, "handle" : <handle2> },
+ // ...
+ // ]
+ // }
+ // }
+ QJsonObject jsonVal = initObject();
+ jsonVal.insert(_(COMMAND), _(EVALUATE));
+
+ QJsonObject args {
+ { _(EXPRESSION), expr }
+ };
+
+ if (frame != -1)
+ args.insert(_(FRAME), frame);
+
+ if (global)
+ args.insert(_(GLOBAL), global);
+
+ if (disableBreak)
+ args.insert(_(DISABLE_BREAK), disableBreak);
+
+ if (addContext) {
+ WatchHandler *watchHandler = engine->watchHandler();
+ QAbstractItemModel *watchModel = watchHandler->model();
+ int rowCount = watchModel->rowCount();
+
+ QJsonArray ctxtList;
+ while (rowCount) {
+ QModelIndex index = watchModel->index(--rowCount, 0);
+ const WatchData *data = watchHandler->watchItem(index);
+ const QJsonObject ctxt {
+ { _(NAME), data->name },
+ { _(HANDLE), int(data->id) }
+ };
+
+ ctxtList.push_front(ctxt);
+ }
+
+ args.insert(_(ADDITIONAL_CONTEXT), ctxtList);
+ }
+
+ jsonVal.insert(_(ARGUMENTS), args);
+
+ sendAndLogV8Request(jsonVal);
+}
+
+void QmlEnginePrivate::lookup(QList<int> handles, bool includeSource)
+{
+ // { "seq" : <number>,
+ // "type" : "request",
+ // "command" : "lookup",
+ // "arguments" : { "handles" : <array of handles>,
+ // "includeSource" : <boolean indicating whether
+ // the source will be included when
+ // script objects are returned>,
+ // }
+ // }
+ QJsonObject jsonVal = initObject();
+ jsonVal.insert(_(COMMAND), _(LOOKUP));
+
+ QJsonObject args;
+
+ QJsonArray array;
+ foreach (int handle, handles)
+ array.push_back(handle);
+ args.insert(_(HANDLES), array);
+
+ if (includeSource)
+ args.insert(_(INCLUDESOURCE), includeSource);
+
+ jsonVal.insert(_(ARGUMENTS), args);
+
+ sendAndLogV8Request(jsonVal);
+}
+
+void QmlEnginePrivate::backtrace(int fromFrame, int toFrame, bool bottom)
+{
+ // { "seq" : <number>,
+ // "type" : "request",
+ // "command" : "backtrace",
+ // "arguments" : { "fromFrame" : <number>
+ // "toFrame" : <number>
+ // "bottom" : <boolean, set to true if the bottom of the
+ // stack is requested>
+ // }
+ // }
+ QJsonObject jsonVal = initObject();
+ jsonVal.insert(_(COMMAND), _(BACKTRACE));
+
+ QJsonObject args;
+
+ if (fromFrame != -1)
+ args.insert(_(FROMFRAME), fromFrame);
+
+ if (toFrame != -1)
+ args.insert(_(TOFRAME), toFrame);
+
+ if (bottom)
+ args.insert(_(BOTTOM), bottom);
+
+ jsonVal.insert(_(ARGUMENTS), args);
+
+ sendAndLogV8Request(jsonVal);
+}
+
+void QmlEnginePrivate::frame(int number)
+{
+ // { "seq" : <number>,
+ // "type" : "request",
+ // "command" : "frame",
+ // "arguments" : { "number" : <frame number>
+ // }
+ // }
+ QJsonObject jsonVal = initObject();
+ jsonVal.insert(_(COMMAND), _(FRAME));
+
+ if (number != -1) {
+ const QJsonObject args {
+ { _(NUMBER), number }
+ };
+
+ jsonVal.insert(_(ARGUMENTS), args);
+ }
+
+ sendAndLogV8Request(jsonVal);
+}
+
+void QmlEnginePrivate::scope(int number, int frameNumber)
+{
+ // { "seq" : <number>,
+ // "type" : "request",
+ // "command" : "scope",
+ // "arguments" : { "number" : <scope number>
+ // "frameNumber" : <frame number, optional uses selected
+ // frame if missing>
+ // }
+ // }
+ QJsonObject jsonVal = initObject();
+ jsonVal.insert(_(COMMAND), _(SCOPE));
+
+ if (number != -1) {
+ QJsonObject args {
+ { _(NUMBER), number }
+ };
+
+ if (frameNumber != -1)
+ args.insert(_(FRAMENUMBER), frameNumber);
+
+ jsonVal.insert(_(ARGUMENTS), args);
+ }
+
+ sendAndLogV8Request(jsonVal);
+}
+
+void QmlEnginePrivate::scripts(int types, const QList<int> ids, bool includeSource,
+ const QVariant filter)
+{
+ // { "seq" : <number>,
+ // "type" : "request",
+ // "command" : "scripts",
+ // "arguments" : { "types" : <types of scripts to retrieve
+ // set bit 0 for native scripts
+ // set bit 1 for extension scripts
+ // set bit 2 for normal scripts
+ // (default is 4 for normal scripts)>
+ // "ids" : <array of id's of scripts to return. If this is not specified all scripts are requrned>
+ // "includeSource" : <boolean indicating whether the source code should be included for the scripts returned>
+ // "filter" : <string or number: filter string or script id.
+ // If a number is specified, then only the script with the same number as its script id will be retrieved.
+ // If a string is specified, then only scripts whose names contain the filter string will be retrieved.>
+ // }
+ // }
+ QJsonObject jsonVal = initObject();
+ jsonVal.insert(_(COMMAND), _(SCRIPTS));
+
+ QJsonObject args {
+ { _(TYPES), types }
+ };
+
+ if (ids.count()) {
+ QJsonArray array;
+ foreach (int id, ids) {
+ array.push_back(id);
+ }
+ args.insert(_(IDS), array);
+ }
+
+ if (includeSource)
+ args.insert(_(INCLUDESOURCE), includeSource);
+
+ QJsonValue filterValue;
+ if (filter.type() == QVariant::String)
+ filterValue = filter.toString();
+ else if (filter.type() == QVariant::Int)
+ filterValue = filter.toInt();
+ else
+ QTC_CHECK(!filter.isValid());
+
+ args.insert(_(FILTER), filterValue);
+
+ jsonVal.insert(_(ARGUMENTS), args);
+
+ sendAndLogV8Request(jsonVal);
+}
+
+void QmlEnginePrivate::setBreakpoint(const QString type, const QString target,
+ bool enabled, int line, int column,
+ const QString condition, int ignoreCount)
+{
+ // { "seq" : <number>,
+ // "type" : "request",
+ // "command" : "setbreakpoint",
+ // "arguments" : { "type" : <"function" or "script" or "scriptId" or "scriptRegExp">
+ // "target" : <function expression or script identification>
+ // "line" : <line in script or function>
+ // "column" : <character position within the line>
+ // "enabled" : <initial enabled state. True or false, default is true>
+ // "condition" : <string with break point condition>
+ // "ignoreCount" : <number specifying the number of break point hits to ignore, default value is 0>
+ // }
+ // }
+ if (type == _(EVENT)) {
+ QByteArray params;
+ QmlDebugStream rs(&params, QIODevice::WriteOnly);
+ rs << target.toUtf8() << enabled;
+ engine->showMessage(QString(_("%1 %2 %3")).arg(_(BREAKONSIGNAL), target, enabled ? _("enabled") : _("disabled")), LogInput);
+ sendMessage(packMessage(BREAKONSIGNAL, params));
+
+ } else {
+ QJsonObject jsonVal = initObject();
+ jsonVal.insert(_(COMMAND), _(SETBREAKPOINT));
+
+ QJsonObject args {
+ { _(TYPE), type },
+ { _(ENABLED), enabled }
+ };
+ if (type == _(SCRIPTREGEXP))
+ args.insert(_(TARGET), Utils::FileName::fromString(target).fileName());
+ else
+ args.insert(_(TARGET), target);
+
+ if (line)
+ args.insert(_(LINE), line - 1);
+
+ if (column)
+ args.insert(_(COLUMN), column - 1);
+
+ if (!condition.isEmpty())
+ args.insert(_(CONDITION), condition);
+
+ if (ignoreCount != -1)
+ args.insert(_(IGNORECOUNT), ignoreCount);
+
+ jsonVal.insert(_(ARGUMENTS), args);
+
+ sendAndLogV8Request(jsonVal);
+ }
+}
+
+void QmlEnginePrivate::clearBreakpoint(int breakpoint)
+{
+ // { "seq" : <number>,
+ // "type" : "request",
+ // "command" : "clearbreakpoint",
+ // "arguments" : { "breakpoint" : <number of the break point to clear>
+ // }
+ // }
+ QJsonObject jsonVal = initObject();
+ jsonVal.insert(_(COMMAND), _(CLEARBREAKPOINT));
+
+ QJsonObject args {
+ { _(BREAKPOINT), breakpoint }
+ };
+
+ jsonVal.insert(_(ARGUMENTS), args);
+
+ sendAndLogV8Request(jsonVal);
+}
+
+void QmlEnginePrivate::setExceptionBreak(Exceptions type, bool enabled)
+{
+ // { "seq" : <number>,
+ // "type" : "request",
+ // "command" : "setexceptionbreak",
+ // "arguments" : { "type" : <string: "all", or "uncaught">,
+ // "enabled" : <optional bool: enables the break type if true>
+ // }
+ // }
+ QJsonObject jsonVal = initObject();
+ jsonVal.insert(_(COMMAND), _(SETEXCEPTIONBREAK));
+
+ QJsonObject args;
+
+ if (type == AllExceptions)
+ args.insert(_(TYPE), _(ALL));
+ //Not Supported
+ // else if (type == UncaughtExceptions)
+ // args.setProperty(_(TYPE),QScriptValue(_(UNCAUGHT)));
+
+ if (enabled)
+ args.insert(_(ENABLED), enabled);
+
+ jsonVal.insert(_(ARGUMENTS), args);
+
+ sendAndLogV8Request(jsonVal);
+}
+
+void QmlEnginePrivate::version()
+{
+ // { "seq" : <number>,
+ // "type" : "request",
+ // "command" : "version",
+ // }
+ QJsonObject jsonVal = initObject();
+ jsonVal.insert(_(COMMAND), _(VERSION));
+
+ sendAndLogV8Request(jsonVal);
+}
+
+QVariant valueFromRef(int handle, const QVariant &refsVal, bool *success)
+{
+ *success = false;
+ QVariant variant;
+ const QVariantList refs = refsVal.toList();
+ foreach (const QVariant &ref, refs) {
+ const QVariantMap refData = ref.toMap();
+ if (refData.value(_(HANDLE)).toInt() == handle) {
+ variant = refData;
+ *success = true;
+ break;
+ }
+ }
+ return variant;
+}
+
+QmlV8ObjectData extractData(const QVariant &data, const QVariant &refsVal)
+{
+ // { "handle" : <handle>,
+ // "type" : <"undefined", "null", "boolean", "number", "string", "object", "function" or "frame">
+ // }
+
+ // {"handle":<handle>,"type":"undefined"}
+
+ // {"handle":<handle>,"type":"null"}
+
+ // { "handle":<handle>,
+ // "type" : <"boolean", "number" or "string">
+ // "value" : <JSON encoded value>
+ // }
+
+ // {"handle":7,"type":"boolean","value":true}
+
+ // {"handle":8,"type":"number","value":42}
+
+ // { "handle" : <handle>,
+ // "type" : "object",
+ // "className" : <Class name, ECMA-262 property [[Class]]>,
+ // "constructorFunction" : {"ref":<handle>},
+ // "protoObject" : {"ref":<handle>},
+ // "prototypeObject" : {"ref":<handle>},
+ // "properties" : [ {"name" : <name>,
+ // "ref" : <handle>
+ // },
+ // ...
+ // ]
+ // }
+
+ // { "handle" : <handle>,
+ // "type" : "function",
+ // "className" : "Function",
+ // "constructorFunction" : {"ref":<handle>},
+ // "protoObject" : {"ref":<handle>},
+ // "prototypeObject" : {"ref":<handle>},
+ // "name" : <function name>,
+ // "inferredName" : <inferred function name for anonymous functions>
+ // "source" : <function source>,
+ // "script" : <reference to function script>,
+ // "scriptId" : <id of function script>,
+ // "position" : <function begin position in script>,
+ // "line" : <function begin source line in script>,
+ // "column" : <function begin source column in script>,
+ // "properties" : [ {"name" : <name>,
+ // "ref" : <handle>
+ // },
+ // ...
+ // ]
+ // }
+
+ QmlV8ObjectData objectData;
+ const QVariantMap dataMap = data.toMap();
+
+ objectData.name = dataMap.value(_(NAME)).toByteArray();
+
+ if (dataMap.contains(_(REF))) {
+ objectData.handle = dataMap.value(_(REF)).toInt();
+ bool success;
+ QVariant dataFromRef = valueFromRef(objectData.handle, refsVal, &success);
+ if (success) {
+ QmlV8ObjectData data = extractData(dataFromRef, refsVal);
+ objectData.type = data.type;
+ objectData.value = data.value;
+ objectData.properties = data.properties;
+ }
+ } else {
+ objectData.handle = dataMap.value(_(HANDLE)).toInt();
+ QString type = dataMap.value(_(TYPE)).toString();
+
+ if (type == _("undefined")) {
+ objectData.type = QByteArray("undefined");
+ objectData.value = QVariant(_("undefined"));
+
+ } else if (type == _("null")) {
+ objectData.type = QByteArray("null");
+ objectData.value= QVariant(_("null"));
+
+ } else if (type == _("boolean")) {
+ objectData.type = QByteArray("boolean");
+ objectData.value = dataMap.value(_(VALUE));
+
+ } else if (type == _("number")) {
+ objectData.type = QByteArray("number");
+ objectData.value = dataMap.value(_(VALUE));
+
+ } else if (type == _("string")) {
+ objectData.type = QByteArray("string");
+ objectData.value = dataMap.value(_(VALUE));
+
+ } else if (type == _("object")) {
+ objectData.type = QByteArray("object");
+ objectData.value = dataMap.value(_("className"));
+ objectData.properties = dataMap.value(_("properties")).toList();
+
+ } else if (type == _("function")) {
+ objectData.type = QByteArray("function");
+ objectData.value = dataMap.value(_(NAME));
+ objectData.properties = dataMap.value(_("properties")).toList();
+
+ } else if (type == _("script")) {
+ objectData.type = QByteArray("script");
+ objectData.value = dataMap.value(_(NAME));
+ }
+ }
+
+ return objectData;
+}
+
+void QmlEnginePrivate::clearCache()
+{
+ currentFrameScopes.clear();
+ updateLocalsAndWatchers.clear();
+}
+
+QByteArray QmlEnginePrivate::packMessage(const QByteArray &type, const QByteArray &message)
+{
+ SDEBUG(message);
+ QByteArray request;
+ QmlDebugStream rs(&request, QIODevice::WriteOnly);
+ QByteArray cmd = V8DEBUG;
+ rs << cmd << type << message;
+ return request;
+}
- baseTextDocument->document()->setPlainText(textDocument->toPlainText());
+QJsonObject QmlEnginePrivate::initObject()
+{
+ return QJsonObject {
+ {_(SEQ), ++sequence},
+ {_(TYPE), _(REQUEST)}
+ };
}
-bool QmlEngine::canEvaluateScript(const QString &script)
+void QmlEnginePrivate::sendAndLogV8Request(const QJsonObject &request)
{
- m_interpreter.clearText();
- m_interpreter.appendText(script);
- return m_interpreter.canEvaluate();
+ const QByteArray msg = QJsonDocument(request).toJson(QJsonDocument::Compact);
+ engine->showMessage(QString::fromLatin1("%1 %2").arg(_(V8REQUEST), QString::fromUtf8(msg)), LogInput);
+ sendMessage(packMessage(V8REQUEST, msg));
+}
+
+void QmlEnginePrivate::expandObject(const QByteArray &iname, quint64 objectId)
+{
+ if (objectId == 0) {
+ //We may have got the global object
+ const WatchItem *watch = engine->watchHandler()->findItem(iname);
+ if (watch->value == QLatin1String("global")) {
+ StackHandler *stackHandler = engine->stackHandler();
+ if (stackHandler->isContentsValid() && stackHandler->currentFrame().isUsable()) {
+ evaluate(watch->name, false, false, stackHandler->currentIndex());
+ evaluatingExpression.insert(sequence, QLatin1String(iname));
+ }
+ return;
+ }
+ }
+ localsAndWatchers.insertMulti(objectId, iname);
+ lookup(QList<int>() << objectId);
+}
+
+void QmlEnginePrivate::messageReceived(const QByteArray &data)
+{
+ QmlDebugStream ds(data);
+ QByteArray command;
+ ds >> command;
+
+ if (command == V8DEBUG) {
+ QByteArray type;
+ QByteArray response;
+ ds >> type >> response;
+
+ engine->showMessage(QLatin1String(type), LogOutput);
+ if (type == CONNECT) {
+ //debugging session started
+
+ } else if (type == INTERRUPT) {
+ //debug break requested
+
+ } else if (type == BREAKONSIGNAL) {
+ //break on signal handler requested
+
+ } else if (type == V8MESSAGE) {
+ const QString responseString = QLatin1String(response);
+ SDEBUG(responseString);
+ engine->showMessage(QLatin1String(V8MESSAGE) + QLatin1Char(' ') + responseString, LogOutput);
+
+ const QVariantMap resp =
+ QJsonDocument::fromJson(responseString.toUtf8()).toVariant().toMap();
+
+ const QString type(resp.value(_(TYPE)).toString());
+
+ if (type == _("response")) {
+
+ bool success = resp.value(_("success")).toBool();
+ if (!success) {
+ SDEBUG("Request was unsuccessful");
+ }
+
+ const QString debugCommand(resp.value(_(COMMAND)).toString());
+
+ if (debugCommand == _(DISCONNECT)) {
+ //debugging session ended
+
+ } else if (debugCommand == _(CONTINEDEBUGGING)) {
+ //do nothing, wait for next break
+
+ } else if (debugCommand == _(BACKTRACE)) {
+ if (success)
+ updateStack(resp.value(_(BODY)), resp.value(_(REFS)));
+
+ } else if (debugCommand == _(LOOKUP)) {
+ if (success)
+ expandLocalsAndWatchers(resp.value(_(BODY)), resp.value(_(REFS)));
+
+ } else if (debugCommand == _(EVALUATE)) {
+ int seq = resp.value(_("request_seq")).toInt();
+ if (success) {
+ updateEvaluationResult(seq, success, resp.value(_(BODY)), resp.value(_(REFS)));
+ } else {
+ QVariantMap map;
+ map.insert(_(TYPE), QVariant(_("string")));
+ map.insert(_(VALUE), resp.value(_("message")));
+ updateEvaluationResult(seq, success, QVariant(map), QVariant());
+ }
+
+ } else if (debugCommand == _(SETBREAKPOINT)) {
+ // { "seq" : <number>,
+ // "type" : "response",
+ // "request_seq" : <number>,
+ // "command" : "setbreakpoint",
+ // "body" : { "type" : <"function" or "script">
+ // "breakpoint" : <break point number of the new break point>
+ // }
+ // "running" : <is the VM running after sending this response>
+ // "success" : true
+ // }
+
+ int seq = resp.value(_("request_seq")).toInt();
+ const QVariantMap breakpointData = resp.value(_(BODY)).toMap();
+ int index = breakpointData.value(_("breakpoint")).toInt();
+
+ if (breakpointsSync.contains(seq)) {
+ BreakpointModelId id = breakpointsSync.take(seq);
+ breakpoints.insert(id, index);
+
+ //Is actual position info present? Then breakpoint was
+ //accepted
+ const QVariantList actualLocations =
+ breakpointData.value(_("actual_locations")).toList();
+ if (actualLocations.count()) {
+ //The breakpoint requested line should be same as
+ //actual line
+ BreakHandler *handler = engine->breakHandler();
+ Breakpoint bp = handler->breakpointById(id);
+ if (bp.state() != BreakpointInserted) {
+ BreakpointResponse br = bp.response();
+ br.lineNumber = breakpointData.value(_("line")).toInt() + 1;
+ bp.setResponse(br);
+ bp.notifyBreakpointInsertOk();
+ }
+ }
+
+
+ } else {
+ breakpointsTemp.append(index);
+ }
+
+
+ } else if (debugCommand == _(CLEARBREAKPOINT)) {
+ // DO NOTHING
+
+ } else if (debugCommand == _(SETEXCEPTIONBREAK)) {
+ // { "seq" : <number>,
+ // "type" : "response",
+ // "request_seq" : <number>,
+ // "command" : "setexceptionbreak",
+ // "body" : { "type" : <string: "all" or "uncaught" corresponding to the request.>,
+ // "enabled" : <bool: true if the break type is currently enabled as a result of the request>
+ // }
+ // "running" : true
+ // "success" : true
+ // }
+
+
+ } else if (debugCommand == _(FRAME)) {
+ if (success)
+ setCurrentFrameDetails(resp.value(_(BODY)), resp.value(_(REFS)));
+
+ } else if (debugCommand == _(SCOPE)) {
+ if (success)
+ updateScope(resp.value(_(BODY)), resp.value(_(REFS)));
+
+ } else if (debugCommand == _(SCRIPTS)) {
+ // { "seq" : <number>,
+ // "type" : "response",
+ // "request_seq" : <number>,
+ // "command" : "scripts",
+ // "body" : [ { "name" : <name of the script>,
+ // "id" : <id of the script>
+ // "lineOffset" : <line offset within the containing resource>
+ // "columnOffset" : <column offset within the containing resource>
+ // "lineCount" : <number of lines in the script>
+ // "data" : <optional data object added through the API>
+ // "source" : <source of the script if includeSource was specified in the request>
+ // "sourceStart" : <first 80 characters of the script if includeSource was not specified in the request>
+ // "sourceLength" : <total length of the script in characters>
+ // "scriptType" : <script type (see request for values)>
+ // "compilationType" : < How was this script compiled:
+ // 0 if script was compiled through the API
+ // 1 if script was compiled through eval
+ // >
+ // "evalFromScript" : <if "compilationType" is 1 this is the script from where eval was called>
+ // "evalFromLocation" : { line : < if "compilationType" is 1 this is the line in the script from where eval was called>
+ // column : < if "compilationType" is 1 this is the column in the script from where eval was called>
+ // ]
+ // "running" : <is the VM running after sending this response>
+ // "success" : true
+ // }
+
+ if (success) {
+ const QVariantList body = resp.value(_(BODY)).toList();
+
+ QStringList sourceFiles;
+ for (int i = 0; i < body.size(); ++i) {
+ const QVariantMap entryMap = body.at(i).toMap();
+ const int lineOffset = entryMap.value(QLatin1String("lineOffset")).toInt();
+ const int columnOffset = entryMap.value(QLatin1String("columnOffset")).toInt();
+ const QString name = entryMap.value(QLatin1String("name")).toString();
+ const QString source = entryMap.value(QLatin1String("source")).toString();
+
+ if (name.isEmpty())
+ continue;
+
+ if (!sourceFiles.contains(name))
+ sourceFiles << name;
+
+ updateScriptSource(name, lineOffset, columnOffset, source);
+ }
+
+ QMap<QString,QString> files;
+ foreach (const QString &file, sourceFiles) {
+ QString shortName = file;
+ QString fullName = engine->toFileInProject(file);
+ files.insert(shortName, fullName);
+ }
+
+ engine->sourceFilesHandler()->setSourceFiles(files);
+ //update open editors
+ }
+ } else if (debugCommand == _(VERSION)) {
+ engine->showMessage(QString(_("Using V8 Version: %1")).arg(
+ resp.value(_(BODY)).toMap().
+ value(_("V8Version")).toString()), LogOutput);
+
+ } else {
+ // DO NOTHING
+ }
+
+ } else if (type == _(EVENT)) {
+ const QString eventType(resp.value(_(EVENT)).toString());
+
+ if (eventType == _("break")) {
+ const QVariantMap breakData = resp.value(_(BODY)).toMap();
+ const QString invocationText = breakData.value(_("invocationText")).toString();
+ const QString scriptUrl = breakData.value(_("script")).toMap().value(_("name")).toString();
+ const QString sourceLineText = breakData.value(_("sourceLineText")).toString();
+
+ bool inferiorStop = true;
+
+ QList<int> v8BreakpointIds;
+ {
+ const QVariantList v8BreakpointIdList = breakData.value(_("breakpoints")).toList();
+ foreach (const QVariant &breakpointId, v8BreakpointIdList)
+ v8BreakpointIds << breakpointId.toInt();
+ }
+
+ if (!v8BreakpointIds.isEmpty() && invocationText.startsWith(_("[anonymous]()"))
+ && scriptUrl.endsWith(_(".qml"))
+ && sourceLineText.trimmed().startsWith(QLatin1Char('('))) {
+
+ // we hit most likely the anonymous wrapper function automatically generated for bindings
+ // -> relocate the breakpoint to column: 1 and continue
+
+ int newColumn = sourceLineText.indexOf(QLatin1Char('(')) + 1;
+ BreakHandler *handler = engine->breakHandler();
+
+ foreach (int v8Id, v8BreakpointIds) {
+ const BreakpointModelId id = breakpoints.key(v8Id);
+ Breakpoint bp = handler->breakpointById(id);
+ if (bp.isValid()) {
+ const BreakpointParameters &params = bp.parameters();
+
+ clearBreakpoint(v8Id);
+ setBreakpoint(QString(_(SCRIPTREGEXP)),
+ params.fileName,
+ params.enabled,
+ params.lineNumber,
+ newColumn,
+ QString(QString::fromLatin1(params.condition)),
+ params.ignoreCount);
+ breakpointsSync.insert(sequence, id);
+ }
+ }
+ continueDebugging(Continue);
+ inferiorStop = false;
+ }
+
+ //Skip debug break if this is an internal function
+ if (sourceLineText == _(INTERNAL_FUNCTION)) {
+ continueDebugging(previousStepAction);
+ inferiorStop = false;
+ }
+
+ if (inferiorStop) {
+ //Update breakpoint data
+ BreakHandler *handler = engine->breakHandler();
+ foreach (int v8Id, v8BreakpointIds) {
+ const BreakpointModelId id = breakpoints.key(v8Id);
+ Breakpoint bp = handler->breakpointById(id);
+ if (bp) {
+ BreakpointResponse br = bp.response();
+ if (br.functionName.isEmpty()) {
+ br.functionName = invocationText;
+ bp.setResponse(br);
+ }
+ if (bp.state() != BreakpointInserted) {
+ br.lineNumber = breakData.value(
+ _("sourceLine")).toInt() + 1;
+ bp.setResponse(br);
+ bp.notifyBreakpointInsertOk();
+ }
+ }
+ }
+
+ if (engine->state() == InferiorRunOk) {
+ foreach (const QVariant &breakpointId, v8BreakpointIds) {
+ if (breakpointsTemp.contains(breakpointId.toInt()))
+ clearBreakpoint(breakpointId.toInt());
+ }
+ engine->notifyInferiorSpontaneousStop();
+ backtrace();
+ } else if (engine->state() == InferiorStopOk) {
+ backtrace();
+ }
+ }
+
+ } else if (eventType == _("exception")) {
+ const QVariantMap body = resp.value(_(BODY)).toMap();
+ int lineNumber = body.value(_("sourceLine")).toInt() + 1;
+
+ const QVariantMap script = body.value(_("script")).toMap();
+ QUrl fileUrl(script.value(_(NAME)).toString());
+ QString filePath = engine->toFileInProject(fileUrl);
+
+ const QVariantMap exception = body.value(_("exception")).toMap();
+ QString errorMessage = exception.value(_("text")).toString();
+
+ highlightExceptionCode(lineNumber, filePath, errorMessage);
+
+ if (engine->state() == InferiorRunOk) {
+ engine->notifyInferiorSpontaneousStop();
+ backtrace();
+ }
+
+ if (engine->state() == InferiorStopOk)
+ backtrace();
+
+ } else if (eventType == _("afterCompile")) {
+ //Currently break point relocation is disabled.
+ //Uncomment the line below when it will be enabled.
+// listBreakpoints();
+ }
+
+ //Sometimes we do not get event type!
+ //This is most probably due to a wrong eval expression.
+ //Redirect output to console.
+ if (eventType.isEmpty()) {
+ bool success = resp.value(_("success")).toBool();
+ QVariantMap map;
+ map.insert(_(TYPE), QVariant(_("string")));
+ map.insert(_(VALUE), resp.value(_("message")));
+ //Since there is no sequence value, best estimate is
+ //last sequence value
+ updateEvaluationResult(sequence, success, QVariant(map), QVariant());
+ }
+
+ } //EVENT
+ } //V8MESSAGE
+
+ } else {
+ //DO NOTHING
+ }
}
-bool QmlEngine::adjustBreakpointLineAndColumn(
- const QString &filePath, quint32 *line, quint32 *column, bool *valid)
+void QmlEnginePrivate::updateStack(const QVariant &bodyVal, const QVariant &refsVal)
{
- bool success = false;
- //check if file is in the latest snapshot
- //ignoring documentChangedOnDisk
- //TODO:: update breakpoints if document is changed.
- ModelManagerInterface *mmIface = ModelManagerInterface::instance();
- if (mmIface) {
- Document::Ptr doc = mmIface->newestSnapshot().
- document(filePath);
- if (doc.isNull()) {
- ModelManagerInterface::instance()->updateSourceFiles(
- QStringList() << filePath, false);
+ // { "seq" : <number>,
+ // "type" : "response",
+ // "request_seq" : <number>,
+ // "command" : "backtrace",
+ // "body" : { "fromFrame" : <number>
+ // "toFrame" : <number>
+ // "totalFrames" : <number>
+ // "frames" : <array of frames - see frame request for details>
+ // }
+ // "running" : <is the VM running after sending this response>
+ // "success" : true
+ // }
+
+ const QVariantMap body = bodyVal.toMap();
+ const QVariantList frames = body.value(_("frames")).toList();
+
+ int fromFrameIndex = body.value(_("fromFrame")).toInt();
+
+ QTC_ASSERT(0 == fromFrameIndex, return);
+
+ StackHandler *stackHandler = engine->stackHandler();
+ StackFrames stackFrames;
+ int i = 0;
+ stackIndexLookup.clear();
+ foreach (const QVariant &frame, frames) {
+ StackFrame stackFrame = extractStackFrame(frame, refsVal);
+ if (stackFrame.level < 0)
+ continue;
+ stackIndexLookup.insert(i, stackFrame.level);
+ stackFrame.level = i;
+ stackFrames << stackFrame;
+ i++;
+ }
+ stackHandler->setFrames(stackFrames);
+
+ //Populate locals and watchers wrt top frame
+ //Update all Locals visible in current scope
+ //Traverse the scope chain and store the local properties
+ //in a list and show them in the Locals Window.
+ setCurrentFrameDetails(frames.value(0), refsVal);
+}
+
+StackFrame QmlEnginePrivate::extractStackFrame(const QVariant &bodyVal, const QVariant &refsVal)
+{
+ // { "seq" : <number>,
+ // "type" : "response",
+ // "request_seq" : <number>,
+ // "command" : "frame",
+ // "body" : { "index" : <frame number>,
+ // "receiver" : <frame receiver>,
+ // "func" : <function invoked>,
+ // "script" : <script for the function>,
+ // "constructCall" : <boolean indicating whether the function was called as constructor>,
+ // "debuggerFrame" : <boolean indicating whether this is an internal debugger frame>,
+ // "arguments" : [ { name: <name of the argument - missing of anonymous argument>,
+ // value: <value of the argument>
+ // },
+ // ... <the array contains all the arguments>
+ // ],
+ // "locals" : [ { name: <name of the local variable>,
+ // value: <value of the local variable>
+ // },
+ // ... <the array contains all the locals>
+ // ],
+ // "position" : <source position>,
+ // "line" : <source line>,
+ // "column" : <source column within the line>,
+ // "sourceLineText" : <text for current source line>,
+ // "scopes" : [ <array of scopes, see scope request below for format> ],
+
+ // }
+ // "running" : <is the VM running after sending this response>
+ // "success" : true
+ // }
+
+ const QVariantMap body = bodyVal.toMap();
+
+ StackFrame stackFrame;
+ stackFrame.level = body.value(_("index")).toInt();
+ //Do not insert the frame corresponding to the internal function
+ if (body.value(QLatin1String("sourceLineText")) == QLatin1String(INTERNAL_FUNCTION)) {
+ stackFrame.level = -1;
+ return stackFrame;
+ }
+
+ QmlV8ObjectData objectData = extractData(body.value(_("func")), refsVal);
+ QString functionName = objectData.value.toString();
+ if (functionName.isEmpty())
+ functionName = tr("Anonymous Function");
+ stackFrame.function = functionName;
+
+ objectData = extractData(body.value(_("script")), refsVal);
+ stackFrame.file = engine->toFileInProject(objectData.value.toString());
+ stackFrame.usable = QFileInfo(stackFrame.file).isReadable();
+
+ objectData = extractData(body.value(_("receiver")), refsVal);
+ stackFrame.to = objectData.value.toString();
+
+ stackFrame.line = body.value(_("line")).toInt() + 1;
+
+ return stackFrame;
+}
+
+void QmlEnginePrivate::setCurrentFrameDetails(const QVariant &bodyVal, const QVariant &refsVal)
+{
+ // { "seq" : <number>,
+ // "type" : "response",
+ // "request_seq" : <number>,
+ // "command" : "frame",
+ // "body" : { "index" : <frame number>,
+ // "receiver" : <frame receiver>,
+ // "func" : <function invoked>,
+ // "script" : <script for the function>,
+ // "constructCall" : <boolean indicating whether the function was called as constructor>,
+ // "debuggerFrame" : <boolean indicating whether this is an internal debugger frame>,
+ // "arguments" : [ { name: <name of the argument - missing of anonymous argument>,
+ // value: <value of the argument>
+ // },
+ // ... <the array contains all the arguments>
+ // ],
+ // "locals" : [ { name: <name of the local variable>,
+ // value: <value of the local variable>
+ // },
+ // ... <the array contains all the locals>
+ // ],
+ // "position" : <source position>,
+ // "line" : <source line>,
+ // "column" : <source column within the line>,
+ // "sourceLineText" : <text for current source line>,
+ // "scopes" : [ <array of scopes, see scope request below for format> ],
+
+ // }
+ // "running" : <is the VM running after sending this response>
+ // "success" : true
+ // }
+ QVariantMap currentFrame = bodyVal.toMap();
+
+ StackHandler *stackHandler = engine->stackHandler();
+ WatchHandler * watchHandler = engine->watchHandler();
+ watchHandler->notifyUpdateStarted();
+ clearCache();
+
+ const int frameIndex = stackHandler->currentIndex();
+ QSet<QByteArray> expandedInames = watchHandler->expandedINames();
+ QHash<quint64, QByteArray> handlesToLookup;
+ // Store handles of all expanded watch data
+ foreach (const QByteArray &iname, expandedInames) {
+ const WatchItem *item = watchHandler->findItem(iname);
+ if (item && item->isLocal())
+ handlesToLookup.insert(item->id, iname);
+ }
+ if (frameIndex < 0)
+ return;
+ const StackFrame frame = stackHandler->currentFrame();
+ if (!frame.isUsable())
+ return;
+
+ //Set "this" variable
+ {
+ auto item = new WatchItem("local.this", QLatin1String("this"));
+ QmlV8ObjectData objectData = extractData(currentFrame.value(_("receiver")), refsVal);
+ item->id = objectData.handle;
+ item->type = objectData.type;
+ item->value = objectData.value.toString();
+ item->setHasChildren(objectData.properties.count());
+ // In case of global object, we do not get children
+ // Set children nevertheless and query later.
+ if (item->value == QLatin1String("global")) {
+ item->setHasChildren(true);
+ item->id = 0;
+ }
+ watchHandler->insertItem(item);
+ }
+
+ const QVariantList scopes = currentFrame.value(_("scopes")).toList();
+ foreach (const QVariant &scope, scopes) {
+ //Do not query for global types (0)
+ //Showing global properties increases clutter.
+ if (scope.toMap().value(_("type")).toInt() == 0)
+ continue;
+ int scopeIndex = scope.toMap().value(_("index")).toInt();
+ currentFrameScopes.append(scopeIndex);
+ this->scope(scopeIndex);
+ }
+ engine->gotoLocation(stackHandler->currentFrame());
+
+ // Expand watch data that were previously expanded
+ QHash<quint64, QByteArray>::const_iterator itEnd = handlesToLookup.constEnd();
+ for (QHash<quint64, QByteArray>::const_iterator it = handlesToLookup.constBegin(); it != itEnd; ++it)
+ expandObject(it.value(), it.key());
+ engine->stackFrameCompleted();
+}
+
+void QmlEnginePrivate::updateScope(const QVariant &bodyVal, const QVariant &refsVal)
+{
+ // { "seq" : <number>,
+ // "type" : "response",
+ // "request_seq" : <number>,
+ // "command" : "scope",
+ // "body" : { "index" : <index of this scope in the scope chain. Index 0 is the top scope
+ // and the global scope will always have the highest index for a
+ // frame>,
+ // "frameIndex" : <index of the frame>,
+ // "type" : <type of the scope:
+ // 0: Global
+ // 1: Local
+ // 2: With
+ // 3: Closure
+ // 4: Catch >,
+ // "object" : <the scope object defining the content of the scope.
+ // For local and closure scopes this is transient objects,
+ // which has a negative handle value>
+ // }
+ // "running" : <is the VM running after sending this response>
+ // "success" : true
+ // }
+ QVariantMap bodyMap = bodyVal.toMap();
+
+ //Check if the frameIndex is same as current Stack Index
+ StackHandler *stackHandler = engine->stackHandler();
+ if (bodyMap.value(_("frameIndex")).toInt() != stackHandler->currentIndex())
+ return;
+
+ QmlV8ObjectData objectData = extractData(bodyMap.value(_("object")), refsVal);
+
+ QList<int> handlesToLookup;
+ foreach (const QVariant &property, objectData.properties) {
+ QmlV8ObjectData localData = extractData(property, refsVal);
+ auto item = new WatchItem;
+ item->exp = localData.name;
+ //Check for v8 specific local data
+ if (item->exp.startsWith('.') || item->exp.isEmpty())
+ continue;
+
+ item->name = QLatin1String(item->exp);
+ item->iname = QByteArray("local.") + item->exp;
+
+ int handle = localData.handle;
+ if (localData.value.isValid()) {
+ item->id = handle;
+ item->type = localData.type;
+ item->value = localData.value.toString();
+ item->setHasChildren(localData.properties.count());
+ engine->watchHandler()->insertItem(item);
} else {
- ASTWalker walker;
- walker(doc->ast(), line, column);
- *valid = walker.done;
- success = true;
+ handlesToLookup << handle;
+ localsAndWatchers.insertMulti(handle, item->exp);
}
}
- return success;
+
+ if (!handlesToLookup.isEmpty())
+ lookup(handlesToLookup);
+ else
+ engine->watchHandler()->notifyUpdateFinished();
+}
+
+ConsoleItem *constructLogItemTree(ConsoleItem *parent,
+ const QmlV8ObjectData &objectData,
+ const QVariant &refsVal)
+{
+ bool sorted = boolSetting(SortStructMembers);
+ if (!objectData.value.isValid())
+ return 0;
+
+ QString text;
+ if (objectData.name.isEmpty())
+ text = objectData.value.toString();
+ else
+ text = QString(_("%1: %2")).arg(QString::fromLatin1(objectData.name))
+ .arg(objectData.value.toString());
+
+ ConsoleItem *item = new ConsoleItem(parent, ConsoleItem::UndefinedType, text);
+
+ QSet<QString> childrenFetched;
+ foreach (const QVariant &property, objectData.properties) {
+ const QmlV8ObjectData childObjectData = extractData(property, refsVal);
+ if (childObjectData.handle == objectData.handle)
+ continue;
+ ConsoleItem *child = constructLogItemTree(item, childObjectData, refsVal);
+ if (child) {
+ const QString text = child->text();
+ if (childrenFetched.contains(text))
+ continue;
+ childrenFetched.insert(text);
+ item->insertChild(child, sorted);
+ }
+ }
+
+ return item;
+}
+
+void QmlEnginePrivate::updateEvaluationResult(int sequence, bool success,
+ const QVariant &bodyVal, const QVariant &refsVal)
+{
+ // { "seq" : <number>,
+ // "type" : "response",
+ // "request_seq" : <number>,
+ // "command" : "evaluate",
+ // "body" : ...
+ // "running" : <is the VM running after sending this response>
+ // "success" : true
+ // }
+ WatchHandler *watchHandler = engine->watchHandler();
+ if (updateLocalsAndWatchers.contains(sequence)) {
+ updateLocalsAndWatchers.removeOne(sequence);
+ //Update the locals
+ foreach (int index, currentFrameScopes)
+ scope(index);
+ //Also update "this"
+ QByteArray iname("local.this");
+ const WatchItem *parent = watchHandler->findItem(iname);
+ localsAndWatchers.insertMulti(parent->id, iname);
+ lookup(QList<int>() << parent->id);
+
+ } else if (debuggerCommands.contains(sequence)) {
+ updateLocalsAndWatchers.removeOne(sequence);
+ QmlV8ObjectData body = extractData(bodyVal, refsVal);
+ if (auto consoleManager = ConsoleManagerInterface::instance()) {
+ if (ConsoleItem *item = constructLogItemTree(consoleManager->rootItem(), body, refsVal))
+ consoleManager->printToConsolePane(item);
+ }
+ //Update the locals
+ foreach (int index, currentFrameScopes)
+ scope(index);
+
+ } else {
+ QmlV8ObjectData body = extractData(bodyVal, refsVal);
+ if (evaluatingExpression.contains(sequence)) {
+ QString exp = evaluatingExpression.take(sequence);
+ //Do we have request to evaluate a local?
+ if (exp.startsWith(QLatin1String("local."))) {
+ const WatchItem *item = watchHandler->findItem(exp.toLatin1());
+ createWatchDataList(item, body.properties, refsVal);
+ } else {
+ QByteArray iname = watchHandler->watcherName(exp.toLatin1());
+ SDEBUG(QString(iname));
+
+ auto item = new WatchItem(iname, exp);
+ item->exp = exp.toLatin1();
+ item->id = body.handle;
+ if (success) {
+ item->type = body.type;
+ item->value = body.value.toString();
+ item->wantsChildren = body.properties.count();
+ } else {
+ //Do not set type since it is unknown
+ item->setError(body.value.toString());
+ }
+ watchHandler->insertItem(item);
+ createWatchDataList(item, body.properties, refsVal);
+ }
+ //Insert the newly evaluated expression to the Watchers Window
+ }
+ }
+}
+
+void QmlEnginePrivate::expandLocalsAndWatchers(const QVariant &bodyVal, const QVariant &refsVal)
+{
+ // { "seq" : <number>,
+ // "type" : "response",
+ // "request_seq" : <number>,
+ // "command" : "lookup",
+ // "body" : <array of serialized objects indexed using their handle>
+ // "running" : <is the VM running after sending this response>
+ // "success" : true
+ // }
+ const QVariantMap body = bodyVal.toMap();
+
+ QStringList handlesList = body.keys();
+ WatchHandler *watchHandler = engine->watchHandler();
+ foreach (const QString &handle, handlesList) {
+ QmlV8ObjectData bodyObjectData = extractData(body.value(handle), refsVal);
+ QByteArray prepend = localsAndWatchers.take(handle.toInt());
+
+ if (prepend.startsWith("local.") || prepend.startsWith("watch.")) {
+ // Data for expanded local/watch.
+ // Could be an object or function.
+ const WatchItem *parent = watchHandler->findItem(prepend);
+ createWatchDataList(parent, bodyObjectData.properties, refsVal);
+ } else {
+ //rest
+ auto item = new WatchItem;
+ item->exp = prepend;
+ item->name = QLatin1String(item->exp);
+ item->iname = QByteArray("local.") + item->exp;
+ item->id = handle.toInt();
+
+ item->type = bodyObjectData.type;
+ item->value = bodyObjectData.value.toString();
+
+ item->setHasChildren(bodyObjectData.properties.count());
+
+ engine->watchHandler()->insertItem(item);
+ }
+ }
+ engine->watchHandler()->notifyUpdateFinished();
+}
+
+void QmlEnginePrivate::createWatchDataList(const WatchItem *parent,
+ const QVariantList &properties,
+ const QVariant &refsVal)
+{
+ if (properties.count()) {
+ QTC_ASSERT(parent, return);
+ foreach (const QVariant &property, properties) {
+ QmlV8ObjectData propertyData = extractData(property, refsVal);
+ auto item = new WatchItem;
+ item->name = QString::fromUtf8(propertyData.name);
+
+ //Check for v8 specific local data
+ if (item->name.startsWith(QLatin1Char('.')) || item->name.isEmpty())
+ continue;
+ if (parent->type == "object") {
+ if (parent->value == _("Array"))
+ item->exp = parent->exp + '[' + item->name.toLatin1() + ']';
+ else if (parent->value == _("Object"))
+ item->exp = parent->exp + '.' + item->name.toLatin1();
+ } else {
+ item->exp = item->name.toLatin1();
+ }
+
+ item->iname = parent->iname + '.' + item->name.toLatin1();
+ item->id = propertyData.handle;
+ item->type = propertyData.type;
+ item->value = propertyData.value.toString();
+ item->setHasChildren(propertyData.properties.count());
+ engine->watchHandler()->insertItem(item);
+ }
+ }
+}
+
+void QmlEnginePrivate::highlightExceptionCode(int lineNumber,
+ const QString &filePath,
+ const QString &errorMessage)
+{
+ QList<IEditor *> editors = DocumentModel::editorsForFilePath(filePath);
+
+ // set up the format for the errors
+ QTextCharFormat errorFormat;
+ errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
+ errorFormat.setUnderlineColor(Qt::red);
+
+ foreach (IEditor *editor, editors) {
+ TextEditorWidget *ed = qobject_cast<TextEditorWidget *>(editor->widget());
+ if (!ed)
+ continue;
+
+ QList<QTextEdit::ExtraSelection> selections;
+ QTextEdit::ExtraSelection sel;
+ sel.format = errorFormat;
+ QTextCursor c(ed->document()->findBlockByNumber(lineNumber - 1));
+ const QString text = c.block().text();
+ for (int i = 0; i < text.size(); ++i) {
+ if (! text.at(i).isSpace()) {
+ c.setPosition(c.position() + i);
+ break;
+ }
+ }
+ c.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
+ sel.cursor = c;
+
+ sel.format.setToolTip(errorMessage);
+
+ selections.append(sel);
+ ed->setExtraSelections(TextEditorWidget::DebuggerExceptionSelection, selections);
+
+ QString message = QString(_("%1: %2: %3")).arg(filePath).arg(lineNumber).arg(errorMessage);
+ engine->showMessage(message, ConsoleOutput);
+ }
+}
+
+void QmlEnginePrivate::stateChanged(State state)
+{
+ engine->clientStateChanged(state);
+
+ if (state == QmlDebugClient::Enabled) {
+ /// Start session.
+ flushSendBuffer();
+ connect();
+ //Query for the V8 version. This is
+ //only for logging to the debuggerlog
+ version();
+ }
+}
+
+void QmlEnginePrivate::sendMessage(const QByteArray &msg)
+{
+ if (state() == Enabled)
+ QmlDebugClient::sendMessage(msg);
+ else
+ sendBuffer.append(msg);
+}
+
+void QmlEnginePrivate::flushSendBuffer()
+{
+ QTC_ASSERT(state() == Enabled, return);
+ foreach (const QByteArray &msg, sendBuffer)
+ QmlDebugClient::sendMessage(msg);
+ sendBuffer.clear();
}
DebuggerEngine *createQmlEngine(const DebuggerRunParameters &sp)
@@ -1361,6 +2715,5 @@ DebuggerEngine *createQmlEngine(const DebuggerRunParameters &sp)
return new QmlEngine(sp);
}
-} // namespace Internal
-} // namespace Debugger
-
+} // Internal
+} // Debugger
diff --git a/src/plugins/debugger/qml/qmlengine.h b/src/plugins/debugger/qml/qmlengine.h
index 35517e9c4b..2fe073aa49 100644
--- a/src/plugins/debugger/qml/qmlengine.h
+++ b/src/plugins/debugger/qml/qmlengine.h
@@ -31,28 +31,21 @@
#ifndef QMLENGINE_H
#define QMLENGINE_H
-#include "interactiveinterpreter.h"
-#include "qmladapter.h"
-#include "qmlinspectoradapter.h"
#include <debugger/debuggerengine.h>
-#include <projectexplorer/applicationlauncher.h>
#include <qmldebug/qdebugmessageclient.h>
+#include <qmldebug/qmldebugclient.h>
#include <qmldebug/qmloutputparser.h>
#include <qmljs/iscriptevaluator.h>
#include <qmljs/qmljsdocument.h>
-QT_FORWARD_DECLARE_CLASS(QTextDocument)
-
-namespace Core { class IDocument; }
-
-namespace TextEditor { class BaseTextEditor; }
-
namespace Debugger {
namespace Internal {
+class WatchData;
+class WatchItem;
+class QmlEnginePrivate;
class QmlAdapter;
-class WatchTreeView;
class QmlEngine : public DebuggerEngine, QmlJS::IScriptEvaluator
{
@@ -63,31 +56,11 @@ public:
DebuggerEngine *masterEngine = 0);
~QmlEngine();
- void notifyEngineRemoteServerRunning(const QByteArray &, int pid);
- void notifyEngineRemoteSetupFinished(const RemoteSetupResult &result);
-
- bool canDisplayTooltip() const;
-
- void showMessage(const QString &msg, int channel = LogDebug,
- int timeout = -1) const;
- void gotoLocation(const Internal::Location &location);
-
- void filterApplicationMessage(const QString &msg, int channel);
- void inferiorSpontaneousStop();
-
- enum LogDirection {
- LogSend,
- LogReceive
- };
-
- void logMessage(const QString &service, LogDirection direction,
- const QString &str);
+ void filterApplicationMessage(const QString &msg, int channel) const;
- void setSourceFiles(const QStringList &fileNames);
- void updateScriptSource(const QString &fileName, int lineOffset,
- int columnOffset, const QString &source);
-
- void insertBreakpoint(Breakpoint bp);
+ void logServiceStateChange(const QString &service, float version,
+ QmlDebug::QmlDebugClient::State newState);
+ void logServiceActivity(const QString &service, const QString &logMessage);
private slots:
void disconnected();
@@ -96,23 +69,28 @@ private slots:
void errorMessageBoxFinished(int result);
void updateCurrentContext();
- void appendDebugOutput(QtMsgType type, const QString &message,
- const QmlDebug::QDebugContextInfo &info);
void tryToConnect(quint16 port = 0);
void beginConnection(quint16 port = 0);
void connectionEstablished();
void connectionStartupFailed();
void appStartupFailed(const QString &errorMessage);
- void connectionError(QDebugSupport::Error error);
- void serviceConnectionError(const QString &service);
void appendMessage(const QString &msg, Utils::OutputFormat);
void synchronizeWatchers();
private:
- // DebuggerEngine implementation.
+ void notifyEngineRemoteServerRunning(const QByteArray &, int pid);
+ void notifyEngineRemoteSetupFinished(const RemoteSetupResult &result);
+
+ void showMessage(const QString &msg, int channel = LogDebug,
+ int timeout = -1) const;
+ void gotoLocation(const Internal::Location &location);
+ void insertBreakpoint(Breakpoint bp);
+
bool isSynchronous() const { return false; }
+ bool canDisplayTooltip() const { return false; }
+
void executeStep();
void executeStepOut();
void executeNext();
@@ -153,7 +131,6 @@ private:
void reloadSourceFiles();
void reloadFullStack() {}
- bool supportsThreads() const { return false; }
void updateWatchData(const QByteArray &iname);
void selectWatchData(const QByteArray &iname);
void executeDebuggerCommand(const QString &command, DebuggerLanguages languages);
@@ -162,36 +139,21 @@ private:
bool hasCapability(unsigned) const;
void quitDebugger();
-private:
void closeConnection();
void startApplicationLauncher();
void stopApplicationLauncher();
- bool isShadowBuildProject() const;
- QString fromShadowBuildFilename(const QString &filename) const;
- QString mangleFilenamePaths(const QString &filename,
- const QString &oldBasePath, const QString &newBasePath) const;
- QString qmlImportPath() const;
-
- void updateDocument(Core::IDocument *document, const QTextDocument *textDocument);
- bool canEvaluateScript(const QString &script);
- bool adjustBreakpointLineAndColumn(const QString &filePath, quint32 *line,
- quint32 *column, bool *valid);
-
- QmlAdapter m_adapter;
- QmlInspectorAdapter m_inspectorAdapter;
- ProjectExplorer::ApplicationLauncher m_applicationLauncher;
- QTimer m_noDebugOutputTimer;
- QmlDebug::QmlOutputParser m_outputParser;
- QHash<QString, QTextDocument*> m_sourceDocuments;
- QHash<QString, QWeakPointer<TextEditor::BaseTextEditor> > m_sourceEditors;
- InteractiveInterpreter m_interpreter;
- QHash<QString,Breakpoint> pendingBreakpoints;
- QList<quint32> queryIds;
- bool m_retryOnConnectFail;
- bool m_automaticConnect;
+ void connectionErrorOccurred(QDebugSupport::Error socketError);
+ void clientStateChanged(QmlDebug::QmlDebugClient::State state);
+ void checkConnectionState();
+ void showConnectionStateMessage(const QString &message);
+ void showConnectionErrorMessage(const QString &message);
+ bool isConnected() const;
+private:
friend class QmlCppEngine;
+ friend class QmlEnginePrivate;
+ QmlEnginePrivate *d;
};
} // namespace Internal
diff --git a/src/plugins/debugger/qml/qmlengineutils.cpp b/src/plugins/debugger/qml/qmlengineutils.cpp
new file mode 100644
index 0000000000..a657a7fb23
--- /dev/null
+++ b/src/plugins/debugger/qml/qmlengineutils.cpp
@@ -0,0 +1,270 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms and
+** conditions see http://www.qt.io/terms-conditions. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlengine.h"
+
+#include <qmljs/parser/qmljsast_p.h>
+#include <qmljs/qmljsmodelmanagerinterface.h>
+#include <qmljs/consolemanagerinterface.h>
+
+#include <coreplugin/editormanager/documentmodel.h>
+
+#include <texteditor/textdocument.h>
+#include <texteditor/texteditor.h>
+
+using namespace Core;
+using namespace QmlDebug;
+using namespace QmlJS;
+using namespace QmlJS::AST;
+using namespace TextEditor;
+
+namespace Debugger {
+namespace Internal {
+
+class ASTWalker : public Visitor
+{
+public:
+ void operator()(Node *ast, quint32 *l, quint32 *c)
+ {
+ done = false;
+ line = l;
+ column = c;
+ Node::accept(ast, this);
+ }
+
+ bool preVisit(Node *ast)
+ {
+ return !done && ast->lastSourceLocation().startLine >= *line;
+ }
+
+ //Case 1: Breakpoint is between sourceStart(exclusive) and
+ // sourceEnd(inclusive) --> End tree walk.
+ //Case 2: Breakpoint is on sourceStart --> Check for the start
+ // of the first executable code. Set the line number and
+ // column number. End tree walk.
+ //Case 3: Breakpoint is on "unbreakable" code --> Find the next "breakable"
+ // code and check for Case 2. End tree walk.
+
+ //Add more types when suitable.
+
+ bool visit(UiScriptBinding *ast)
+ {
+ if (!ast->statement)
+ return true;
+
+ quint32 sourceStartLine = ast->firstSourceLocation().startLine;
+ quint32 statementStartLine;
+ quint32 statementColumn;
+
+ if (ast->statement->kind == Node::Kind_ExpressionStatement) {
+ statementStartLine = ast->statement->firstSourceLocation().startLine;
+ statementColumn = ast->statement->firstSourceLocation().startColumn;
+
+ } else if (ast->statement->kind == Node::Kind_Block) {
+ Block *block = static_cast<Block *>(ast->statement);
+ if (!block || !block->statements)
+ return true;
+ statementStartLine = block->statements->firstSourceLocation().startLine;
+ statementColumn = block->statements->firstSourceLocation().startColumn;
+
+ } else {
+ return true;
+ }
+
+
+ //Case 1
+ //Check for possible relocation within the binding statement
+
+ //Rewritten to (function <token>() { { }})
+ //The offset 16 is position of inner lbrace without token length.
+ const int offset = 16;
+
+ //Case 2
+ if (statementStartLine == *line) {
+ if (sourceStartLine == *line)
+ *column = offset + ast->qualifiedId->identifierToken.length;
+ done = true;
+ }
+
+ //Case 3
+ if (statementStartLine > *line) {
+ *line = statementStartLine;
+ if (sourceStartLine == *line)
+ *column = offset + ast->qualifiedId->identifierToken.length;
+ else
+ *column = statementColumn;
+ done = true;
+ }
+ return true;
+ }
+
+ bool visit(FunctionDeclaration *ast) {
+ quint32 sourceStartLine = ast->firstSourceLocation().startLine;
+ quint32 sourceStartColumn = ast->firstSourceLocation().startColumn;
+ quint32 statementStartLine = ast->body->firstSourceLocation().startLine;
+ quint32 statementColumn = ast->body->firstSourceLocation().startColumn;
+
+ //Case 1
+ //Check for possible relocation within the function declaration
+
+ //Case 2
+ if (statementStartLine == *line) {
+ if (sourceStartLine == *line)
+ *column = statementColumn - sourceStartColumn + 1;
+ done = true;
+ }
+
+ //Case 3
+ if (statementStartLine > *line) {
+ *line = statementStartLine;
+ if (sourceStartLine == *line)
+ *column = statementColumn - sourceStartColumn + 1;
+ else
+ *column = statementColumn;
+ done = true;
+ }
+ return true;
+ }
+
+ bool visit(EmptyStatement *ast)
+ {
+ *line = ast->lastSourceLocation().startLine + 1;
+ return true;
+ }
+
+ bool visit(VariableStatement *ast) { test(ast); return true; }
+ bool visit(VariableDeclarationList *ast) { test(ast); return true; }
+ bool visit(VariableDeclaration *ast) { test(ast); return true; }
+ bool visit(ExpressionStatement *ast) { test(ast); return true; }
+ bool visit(IfStatement *ast) { test(ast); return true; }
+ bool visit(DoWhileStatement *ast) { test(ast); return true; }
+ bool visit(WhileStatement *ast) { test(ast); return true; }
+ bool visit(ForStatement *ast) { test(ast); return true; }
+ bool visit(LocalForStatement *ast) { test(ast); return true; }
+ bool visit(ForEachStatement *ast) { test(ast); return true; }
+ bool visit(LocalForEachStatement *ast) { test(ast); return true; }
+ bool visit(ContinueStatement *ast) { test(ast); return true; }
+ bool visit(BreakStatement *ast) { test(ast); return true; }
+ bool visit(ReturnStatement *ast) { test(ast); return true; }
+ bool visit(WithStatement *ast) { test(ast); return true; }
+ bool visit(SwitchStatement *ast) { test(ast); return true; }
+ bool visit(CaseBlock *ast) { test(ast); return true; }
+ bool visit(CaseClauses *ast) { test(ast); return true; }
+ bool visit(CaseClause *ast) { test(ast); return true; }
+ bool visit(DefaultClause *ast) { test(ast); return true; }
+ bool visit(LabelledStatement *ast) { test(ast); return true; }
+ bool visit(ThrowStatement *ast) { test(ast); return true; }
+ bool visit(TryStatement *ast) { test(ast); return true; }
+ bool visit(Catch *ast) { test(ast); return true; }
+ bool visit(Finally *ast) { test(ast); return true; }
+ bool visit(FunctionExpression *ast) { test(ast); return true; }
+ bool visit(DebuggerStatement *ast) { test(ast); return true; }
+
+ void test(Node *ast)
+ {
+ quint32 statementStartLine = ast->firstSourceLocation().startLine;
+ //Case 1/2
+ if (statementStartLine <= *line && *line <= ast->lastSourceLocation().startLine)
+ done = true;
+
+ //Case 3
+ if (statementStartLine > *line) {
+ *line = statementStartLine;
+ *column = ast->firstSourceLocation().startColumn;
+ done = true;
+ }
+ }
+
+ bool done;
+ quint32 *line;
+ quint32 *column;
+};
+
+bool adjustBreakpointLineAndColumn(const QString &filePath, quint32 *line, quint32 *column, bool *valid)
+{
+ bool success = false;
+ //check if file is in the latest snapshot
+ //ignoring documentChangedOnDisk
+ //TODO:: update breakpoints if document is changed.
+ ModelManagerInterface *mmIface = ModelManagerInterface::instance();
+ if (mmIface) {
+ Document::Ptr doc = mmIface->newestSnapshot().document(filePath);
+ if (doc.isNull()) {
+ ModelManagerInterface::instance()->updateSourceFiles(
+ QStringList() << filePath, false);
+ } else {
+ ASTWalker walker;
+ walker(doc->ast(), line, column);
+ *valid = walker.done;
+ success = true;
+ }
+ }
+ return success;
+}
+
+void appendDebugOutput(QtMsgType type, const QString &message, const QDebugContextInfo &info)
+{
+ ConsoleItem::ItemType itemType;
+ switch (type) {
+ case QtDebugMsg:
+ itemType = ConsoleItem::DebugType;
+ break;
+ case QtWarningMsg:
+ itemType = ConsoleItem::WarningType;
+ break;
+ case QtCriticalMsg:
+ case QtFatalMsg:
+ itemType = ConsoleItem::ErrorType;
+ break;
+ default:
+ //This case is not possible
+ return;
+ }
+
+ if (auto consoleManager = ConsoleManagerInterface::instance()) {
+ ConsoleItem *item = new ConsoleItem(consoleManager->rootItem(), itemType, message);
+ item->file = info.file;
+ item->line = info.line;
+ consoleManager->printToConsolePane(item);
+ }
+}
+
+void clearExceptionSelection()
+{
+ QList<QTextEdit::ExtraSelection> selections;
+
+ foreach (IEditor *editor, DocumentModel::editorsForOpenedDocuments()) {
+ if (auto ed = qobject_cast<TextEditorWidget *>(editor->widget()))
+ ed->setExtraSelections(TextEditorWidget::DebuggerExceptionSelection, selections);
+ }
+}
+
+} // Internal
+} // Debugger
diff --git a/src/plugins/debugger/qml/qmlengineutils.h b/src/plugins/debugger/qml/qmlengineutils.h
new file mode 100644
index 0000000000..0812ee374b
--- /dev/null
+++ b/src/plugins/debugger/qml/qmlengineutils.h
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms and
+** conditions see http://www.qt.io/terms-conditions. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLENGINEUTILS_H
+#define QMLENGINEUTILS_H
+
+#include <qmldebug/qdebugmessageclient.h>
+#include <qmldebug/qmloutputparser.h>
+
+namespace Debugger {
+namespace Internal {
+
+bool adjustBreakpointLineAndColumn(const QString &filePath, quint32 *line, quint32 *column, bool *valid);
+void appendDebugOutput(QtMsgType type, const QString &message, const QmlDebug::QDebugContextInfo &info);
+void clearExceptionSelection();
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // QMLENGINEUTILS_H
diff --git a/src/plugins/debugger/qml/qmlinspectoradapter.cpp b/src/plugins/debugger/qml/qmlinspectoradapter.cpp
index f98d73959e..43f305b5ff 100644
--- a/src/plugins/debugger/qml/qmlinspectoradapter.cpp
+++ b/src/plugins/debugger/qml/qmlinspectoradapter.cpp
@@ -30,8 +30,9 @@
#include "qmlinspectoradapter.h"
-#include "qmladapter.h"
+#include "qmlengine.h"
#include "qmlinspectoragent.h"
+
#include <debugger/debuggeractions.h>
#include <debugger/debuggercore.h>
#include <debugger/debuggerstringutils.h>
@@ -60,15 +61,12 @@ namespace Internal {
* QmlInspectorAdapter manages the clients for the inspector, and the
* integration with the text editor.
*/
-QmlInspectorAdapter::QmlInspectorAdapter(QmlAdapter *debugAdapter,
- DebuggerEngine *engine,
- QObject *parent)
- : QObject(parent)
- , m_debugAdapter(debugAdapter)
- , m_engine(engine)
+QmlInspectorAdapter::QmlInspectorAdapter(QmlEngine *engine, QmlDebugConnection *connection)
+ : m_qmlEngine(engine)
+ , m_masterEngine(engine)
, m_engineClient(0)
, m_toolsClient(0)
- , m_agent(new QmlInspectorAgent(engine, this))
+ , m_agent(new QmlInspectorAgent(engine))
, m_targetToSync(NoTarget)
, m_debugIdToSelect(-1)
, m_currentSelectedDebugId(-1)
@@ -79,16 +77,15 @@ QmlInspectorAdapter::QmlInspectorAdapter(QmlAdapter *debugAdapter,
, m_showAppOnTopAction(action(ShowAppOnTop))
, m_engineClientConnected(false)
{
- if (!m_engine->isMasterEngine())
- m_engine = m_engine->masterEngine();
- connect(m_engine, &DebuggerEngine::stateChanged,
+ if (!m_masterEngine->isMasterEngine())
+ m_masterEngine = m_masterEngine->masterEngine();
+ connect(m_masterEngine, &DebuggerEngine::stateChanged,
this, &QmlInspectorAdapter::onEngineStateChanged);
connect(m_agent, &QmlInspectorAgent::objectFetched,
this, &QmlInspectorAdapter::onObjectFetched);
connect(m_agent, &QmlInspectorAgent::jumpToObjectDefinition,
this, &QmlInspectorAdapter::jumpToObjectDefinitionInEditor);
- QmlDebugConnection *connection = m_debugAdapter->connection();
auto engineClient1 = new DeclarativeEngineDebugClient(connection);
connect(engineClient1, &BaseEngineDebugClient::newState,
this, &QmlInspectorAdapter::clientStateChanged);
@@ -186,7 +183,7 @@ void QmlInspectorAdapter::clientStateChanged(QmlDebugClient::State state)
version = client->remoteVersion();
}
- m_debugAdapter->logServiceStateChange(serviceName, version, state);
+ m_qmlEngine->logServiceStateChange(serviceName, version, state);
}
void QmlInspectorAdapter::toolsClientStateChanged(QmlDebugClient::State state)
@@ -199,7 +196,7 @@ void QmlInspectorAdapter::toolsClientStateChanged(QmlDebugClient::State state)
connect(client, &BaseToolsClient::currentObjectsChanged,
this, &QmlInspectorAdapter::selectObjectsFromToolsClient);
connect(client, &BaseToolsClient::logActivity,
- m_debugAdapter, &QmlAdapter::logServiceActivity);
+ m_qmlEngine, &QmlEngine::logServiceActivity);
connect(client, &BaseToolsClient::reloaded, this, &QmlInspectorAdapter::onReloaded);
// register actions here
@@ -217,15 +214,15 @@ void QmlInspectorAdapter::toolsClientStateChanged(QmlDebugClient::State state)
Core::ICore::addAdditionalContext(m_inspectorToolsContext);
m_toolsClientConnected = true;
- onEngineStateChanged(m_engine->state());
+ onEngineStateChanged(m_masterEngine->state());
if (m_showAppOnTopAction->isChecked())
m_toolsClient->showAppOnTop(true);
} else if (m_toolsClientConnected && client == m_toolsClient) {
- disconnect(client, SIGNAL(currentObjectsChanged(QList<int>)),
- this, SLOT(selectObjectsFromToolsClient(QList<int>)));
- disconnect(client, SIGNAL(logActivity(QString,QString)),
- m_debugAdapter, SLOT(logServiceActivity(QString,QString)));
+ disconnect(client, &BaseToolsClient::currentObjectsChanged,
+ this, &QmlInspectorAdapter::selectObjectsFromToolsClient);
+ disconnect(client, &BaseToolsClient::logActivity,
+ m_qmlEngine, &QmlEngine::logServiceActivity);
Core::ActionManager::unregisterAction(m_selectAction, Core::Id(Constants::QML_SELECTTOOL));
Core::ActionManager::unregisterAction(m_zoomAction, Core::Id(Constants::QML_ZOOMTOOL));
@@ -316,13 +313,13 @@ void QmlInspectorAdapter::setActiveEngineClient(BaseEngineDebugClient *client)
void QmlInspectorAdapter::showConnectionStateMessage(const QString &message)
{
- m_engine->showMessage(_("QML Inspector: ") + message, LogStatus);
+ m_masterEngine->showMessage(_("QML Inspector: ") + message, LogStatus);
}
void QmlInspectorAdapter::jumpToObjectDefinitionInEditor(
const FileReference &objSource, int debugId)
{
- const QString fileName = m_engine->toFileInProject(objSource.url());
+ const QString fileName = m_masterEngine->toFileInProject(objSource.url());
Core::EditorManager::openEditorAt(fileName, objSource.lineNumber());
if (debugId != -1 && debugId != m_currentSelectedDebugId) {
diff --git a/src/plugins/debugger/qml/qmlinspectoradapter.h b/src/plugins/debugger/qml/qmlinspectoradapter.h
index d448a3cc7a..961aa11957 100644
--- a/src/plugins/debugger/qml/qmlinspectoradapter.h
+++ b/src/plugins/debugger/qml/qmlinspectoradapter.h
@@ -41,13 +41,14 @@ class BaseEngineDebugClient;
class BaseToolsClient;
class ObjectReference;
class FileReference;
+class QmlDebugConnection;
}
namespace Debugger {
namespace Internal {
class DebuggerEngine;
-class QmlAdapter;
+class QmlEngine;
class QmlInspectorAgent;
class QmlInspectorAdapter : public QObject
@@ -55,8 +56,7 @@ class QmlInspectorAdapter : public QObject
Q_OBJECT
public:
- QmlInspectorAdapter(QmlAdapter *debugAdapter, DebuggerEngine *engine,
- QObject *parent = 0);
+ QmlInspectorAdapter(QmlEngine *engine, QmlDebug::QmlDebugConnection *connection);
~QmlInspectorAdapter();
QmlDebug::BaseEngineDebugClient *engineClient() const;
@@ -96,8 +96,8 @@ private:
void enableTools(const bool enable);
- QmlAdapter *m_debugAdapter;
- DebuggerEngine *m_engine; // Master Engine
+ QmlEngine *m_qmlEngine;
+ DebuggerEngine *m_masterEngine;
QmlDebug::BaseEngineDebugClient *m_engineClient;
QHash<QString, QmlDebug::BaseEngineDebugClient*> m_engineClients;
QmlDebug::BaseToolsClient *m_toolsClient;
diff --git a/src/plugins/debugger/qml/qmlv8debuggerclient.cpp b/src/plugins/debugger/qml/qmlv8debuggerclient.cpp
deleted file mode 100644
index 33577f5257..0000000000
--- a/src/plugins/debugger/qml/qmlv8debuggerclient.cpp
+++ /dev/null
@@ -1,1822 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms and
-** conditions see http://www.qt.io/terms-conditions. For further information
-** use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-****************************************************************************/
-
-#include "qmlv8debuggerclient.h"
-#include "qmlv8debuggerclientconstants.h"
-#include "qmlengine.h"
-
-#include <debugger/debuggerstringutils.h>
-#include <debugger/watchhandler.h>
-#include <debugger/breakhandler.h>
-#include <debugger/stackhandler.h>
-#include <debugger/debuggercore.h>
-#include <debugger/debuggeractions.h>
-
-#include <utils/qtcassert.h>
-#include <texteditor/texteditor.h>
-
-#include <coreplugin/editormanager/documentmodel.h>
-
-#include <qmljs/consolemanagerinterface.h>
-
-#include <QTextBlock>
-#include <QFileInfo>
-#include <QJsonArray>
-#include <QJsonDocument>
-#include <QJsonObject>
-
-#define DEBUG_QML 0
-#if DEBUG_QML
-# define SDEBUG(s) qDebug() << s << '\n'
-#else
-# define SDEBUG(s)
-#endif
-
-using namespace Core;
-using QmlDebug::QmlDebugStream;
-
-namespace Debugger {
-namespace Internal {
-
-typedef QPair<QByteArray, QByteArray> WatchDataPair;
-
-struct QmlV8ObjectData {
- int handle;
- QByteArray name;
- QByteArray type;
- QVariant value;
- QVariantList properties;
-};
-
-class QmlV8DebuggerClientPrivate
-{
-public:
- explicit QmlV8DebuggerClientPrivate(QmlV8DebuggerClient *q) :
- q(q),
- sequence(-1),
- engine(0),
- previousStepAction(QmlV8DebuggerClient::Continue)
- {
- }
-
- void connect();
- void disconnect();
-
- void interrupt();
- void continueDebugging(QmlV8DebuggerClient::StepAction stepAction);
-
- void evaluate(const QString expr, bool global = false, bool disableBreak = false,
- int frame = -1, bool addContext = false);
- void lookup(const QList<int> handles, bool includeSource = false);
- void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false);
- void frame(int number = -1);
- void scope(int number = -1, int frameNumber = -1);
- void scripts(int types = 4, const QList<int> ids = QList<int>(),
- bool includeSource = false, const QVariant filter = QVariant());
-
- void setBreakpoint(const QString type, const QString target,
- bool enabled = true,int line = 0, int column = 0,
- const QString condition = QString(), int ignoreCount = -1);
- void clearBreakpoint(int breakpoint);
- void setExceptionBreak(QmlV8DebuggerClient::Exceptions type, bool enabled = false);
-
- void version();
- //void profile(ProfileCommand command); //NOT SUPPORTED
- void clearCache();
-
- void sendAndLogV8Request(const QJsonObject &request);
- void logSendMessage(const QString &msg) const;
- void logReceiveMessage(const QString &msg) const;
-
-private:
- QByteArray packMessage(const QByteArray &type, const QByteArray &message = QByteArray());
- QJsonObject initObject();
-
-public:
- QmlV8DebuggerClient *q;
-
- int sequence;
- QmlEngine *engine;
- QHash<BreakpointModelId, int> breakpoints;
- QHash<int, BreakpointModelId> breakpointsSync;
- QList<int> breakpointsTemp;
-
- QHash<int, QString> evaluatingExpression;
- QHash<int, QByteArray> localsAndWatchers;
- QList<int> updateLocalsAndWatchers;
- QList<int> debuggerCommands;
-
- //Cache
- QList<int> currentFrameScopes;
- QHash<int, int> stackIndexLookup;
-
- QmlV8DebuggerClient::StepAction previousStepAction;
-
- QList<QByteArray> sendBuffer;
-};
-
-///////////////////////////////////////////////////////////////////////
-//
-// QmlV8DebuggerClientPrivate
-//
-///////////////////////////////////////////////////////////////////////
-
-void QmlV8DebuggerClientPrivate::connect()
-{
- logSendMessage(QString(_("%1 %2")).arg(_(V8DEBUG), _(CONNECT)));
- q->sendMessage(packMessage(CONNECT));
-}
-
-void QmlV8DebuggerClientPrivate::disconnect()
-{
- // { "seq" : <number>,
- // "type" : "request",
- // "command" : "disconnect",
- // }
- QJsonObject jsonVal = initObject();
- jsonVal.insert(_(COMMAND), _(DISCONNECT));
-
- const QJsonDocument jsonMessage(jsonVal);
- logSendMessage(QString::fromLatin1("%1 %2")
- .arg(_(V8DEBUG),
- QString::fromUtf8(jsonMessage.toJson(QJsonDocument::Compact))));
- q->sendMessage(packMessage(DISCONNECT, jsonMessage.toJson(QJsonDocument::Compact)));
-}
-
-void QmlV8DebuggerClientPrivate::interrupt()
-{
- logSendMessage(QString(_("%1 %2")).arg(_(V8DEBUG), _(INTERRUPT)));
- q->sendMessage(packMessage(INTERRUPT));
-}
-
-void QmlV8DebuggerClientPrivate::continueDebugging(QmlV8DebuggerClient::StepAction action)
-{
- // { "seq" : <number>,
- // "type" : "request",
- // "command" : "continue",
- // "arguments" : { "stepaction" : <"in", "next" or "out">,
- // "stepcount" : <number of steps (default 1)>
- // }
- // }
- QJsonObject jsonVal = initObject();
- jsonVal.insert(_(COMMAND), _(CONTINEDEBUGGING));
-
- if (action != QmlV8DebuggerClient::Continue) {
- QJsonObject args;
- switch (action) {
- case QmlV8DebuggerClient::In:
- args.insert(_(STEPACTION), _(IN));
- break;
- case QmlV8DebuggerClient::Out:
- args.insert(_(STEPACTION), _(OUT));
- break;
- case QmlV8DebuggerClient::Next:
- args.insert(_(STEPACTION), _(NEXT));
- break;
- default:break;
- }
-
- jsonVal.insert(_(ARGUMENTS), args);
- }
- sendAndLogV8Request(jsonVal);
- previousStepAction = action;
-}
-
-void QmlV8DebuggerClientPrivate::evaluate(const QString expr, bool global,
- bool disableBreak, int frame,
- bool addContext)
-{
- // { "seq" : <number>,
- // "type" : "request",
- // "command" : "evaluate",
- // "arguments" : { "expression" : <expression to evaluate>,
- // "frame" : <number>,
- // "global" : <boolean>,
- // "disable_break" : <boolean>,
- // "additional_context" : [
- // { "name" : <name1>, "handle" : <handle1> },
- // { "name" : <name2>, "handle" : <handle2> },
- // ...
- // ]
- // }
- // }
- QJsonObject jsonVal = initObject();
- jsonVal.insert(_(COMMAND), _(EVALUATE));
-
- QJsonObject args {
- { _(EXPRESSION), expr }
- };
-
- if (frame != -1)
- args.insert(_(FRAME), frame);
-
- if (global)
- args.insert(_(GLOBAL), global);
-
- if (disableBreak)
- args.insert(_(DISABLE_BREAK), disableBreak);
-
- if (addContext) {
- WatchHandler *watchHandler = engine->watchHandler();
- QAbstractItemModel *watchModel = watchHandler->model();
- int rowCount = watchModel->rowCount();
-
- QJsonArray ctxtList;
- while (rowCount) {
- QModelIndex index = watchModel->index(--rowCount, 0);
- const WatchData *data = watchHandler->watchItem(index);
- const QJsonObject ctxt {
- { _(NAME), data->name },
- { _(HANDLE), int(data->id) }
- };
-
- ctxtList.push_front(ctxt);
- }
-
- args.insert(_(ADDITIONAL_CONTEXT), ctxtList);
- }
-
- jsonVal.insert(_(ARGUMENTS), args);
-
- sendAndLogV8Request(jsonVal);
-}
-
-void QmlV8DebuggerClientPrivate::lookup(QList<int> handles, bool includeSource)
-{
- // { "seq" : <number>,
- // "type" : "request",
- // "command" : "lookup",
- // "arguments" : { "handles" : <array of handles>,
- // "includeSource" : <boolean indicating whether
- // the source will be included when
- // script objects are returned>,
- // }
- // }
- QJsonObject jsonVal = initObject();
- jsonVal.insert(_(COMMAND), _(LOOKUP));
-
- QJsonObject args;
-
- QJsonArray array;
- foreach (int handle, handles)
- array.push_back(handle);
- args.insert(_(HANDLES), array);
-
- if (includeSource)
- args.insert(_(INCLUDESOURCE), includeSource);
-
- jsonVal.insert(_(ARGUMENTS), args);
-
- sendAndLogV8Request(jsonVal);
-}
-
-void QmlV8DebuggerClientPrivate::backtrace(int fromFrame, int toFrame, bool bottom)
-{
- // { "seq" : <number>,
- // "type" : "request",
- // "command" : "backtrace",
- // "arguments" : { "fromFrame" : <number>
- // "toFrame" : <number>
- // "bottom" : <boolean, set to true if the bottom of the
- // stack is requested>
- // }
- // }
- QJsonObject jsonVal = initObject();
- jsonVal.insert(_(COMMAND), _(BACKTRACE));
-
- QJsonObject args;
-
- if (fromFrame != -1)
- args.insert(_(FROMFRAME), fromFrame);
-
- if (toFrame != -1)
- args.insert(_(TOFRAME), toFrame);
-
- if (bottom)
- args.insert(_(BOTTOM), bottom);
-
- jsonVal.insert(_(ARGUMENTS), args);
-
- sendAndLogV8Request(jsonVal);
-}
-
-void QmlV8DebuggerClientPrivate::frame(int number)
-{
- // { "seq" : <number>,
- // "type" : "request",
- // "command" : "frame",
- // "arguments" : { "number" : <frame number>
- // }
- // }
- QJsonObject jsonVal = initObject();
- jsonVal.insert(_(COMMAND), _(FRAME));
-
- if (number != -1) {
- const QJsonObject args {
- { _(NUMBER), number }
- };
-
- jsonVal.insert(_(ARGUMENTS), args);
- }
-
- sendAndLogV8Request(jsonVal);
-}
-
-void QmlV8DebuggerClientPrivate::scope(int number, int frameNumber)
-{
- // { "seq" : <number>,
- // "type" : "request",
- // "command" : "scope",
- // "arguments" : { "number" : <scope number>
- // "frameNumber" : <frame number, optional uses selected
- // frame if missing>
- // }
- // }
- QJsonObject jsonVal = initObject();
- jsonVal.insert(_(COMMAND), _(SCOPE));
-
- if (number != -1) {
- QJsonObject args {
- { _(NUMBER), number }
- };
-
- if (frameNumber != -1)
- args.insert(_(FRAMENUMBER), frameNumber);
-
- jsonVal.insert(_(ARGUMENTS), args);
- }
-
- sendAndLogV8Request(jsonVal);
-}
-
-void QmlV8DebuggerClientPrivate::scripts(int types, const QList<int> ids, bool includeSource,
- const QVariant filter)
-{
- // { "seq" : <number>,
- // "type" : "request",
- // "command" : "scripts",
- // "arguments" : { "types" : <types of scripts to retrieve
- // set bit 0 for native scripts
- // set bit 1 for extension scripts
- // set bit 2 for normal scripts
- // (default is 4 for normal scripts)>
- // "ids" : <array of id's of scripts to return. If this is not specified all scripts are requrned>
- // "includeSource" : <boolean indicating whether the source code should be included for the scripts returned>
- // "filter" : <string or number: filter string or script id.
- // If a number is specified, then only the script with the same number as its script id will be retrieved.
- // If a string is specified, then only scripts whose names contain the filter string will be retrieved.>
- // }
- // }
- QJsonObject jsonVal = initObject();
- jsonVal.insert(_(COMMAND), _(SCRIPTS));
-
- QJsonObject args {
- { _(TYPES), types }
- };
-
- if (ids.count()) {
- QJsonArray array;
- foreach (int id, ids) {
- array.push_back(id);
- }
- args.insert(_(IDS), array);
- }
-
- if (includeSource)
- args.insert(_(INCLUDESOURCE), includeSource);
-
- QJsonValue filterValue;
- if (filter.type() == QVariant::String)
- filterValue = filter.toString();
- else if (filter.type() == QVariant::Int)
- filterValue = filter.toInt();
- else
- QTC_CHECK(!filter.isValid());
-
- args.insert(_(FILTER), filterValue);
-
- jsonVal.insert(_(ARGUMENTS), args);
-
- sendAndLogV8Request(jsonVal);
-}
-
-void QmlV8DebuggerClientPrivate::setBreakpoint(const QString type, const QString target,
- bool enabled, int line, int column,
- const QString condition, int ignoreCount)
-{
- // { "seq" : <number>,
- // "type" : "request",
- // "command" : "setbreakpoint",
- // "arguments" : { "type" : <"function" or "script" or "scriptId" or "scriptRegExp">
- // "target" : <function expression or script identification>
- // "line" : <line in script or function>
- // "column" : <character position within the line>
- // "enabled" : <initial enabled state. True or false, default is true>
- // "condition" : <string with break point condition>
- // "ignoreCount" : <number specifying the number of break point hits to ignore, default value is 0>
- // }
- // }
- if (type == _(EVENT)) {
- QByteArray params;
- QmlDebugStream rs(&params, QIODevice::WriteOnly);
- rs << target.toUtf8() << enabled;
- logSendMessage(QString(_("%1 %2 %3 %4")).arg(_(V8DEBUG), _(BREAKONSIGNAL), target, enabled?_("enabled"):_("disabled")));
- q->sendMessage(packMessage(BREAKONSIGNAL, params));
-
- } else {
- QJsonObject jsonVal = initObject();
- jsonVal.insert(_(COMMAND), _(SETBREAKPOINT));
-
- QJsonObject args {
- { _(TYPE), type },
- { _(ENABLED), enabled }
- };
- if (type == _(SCRIPTREGEXP))
- args.insert(_(TARGET),
- Utils::FileName::fromString(target).fileName());
- else
- args.insert(_(TARGET), target);
-
- if (line)
- args.insert(_(LINE), line - 1);
-
- if (column)
- args.insert(_(COLUMN), column - 1);
-
- if (!condition.isEmpty())
- args.insert(_(CONDITION), condition);
-
- if (ignoreCount != -1)
- args.insert(_(IGNORECOUNT), ignoreCount);
-
- jsonVal.insert(_(ARGUMENTS), args);
-
- sendAndLogV8Request(jsonVal);
- }
-}
-
-void QmlV8DebuggerClientPrivate::clearBreakpoint(int breakpoint)
-{
- // { "seq" : <number>,
- // "type" : "request",
- // "command" : "clearbreakpoint",
- // "arguments" : { "breakpoint" : <number of the break point to clear>
- // }
- // }
- QJsonObject jsonVal = initObject();
- jsonVal.insert(_(COMMAND), _(CLEARBREAKPOINT));
-
- QJsonObject args {
- { _(BREAKPOINT), breakpoint }
- };
-
- jsonVal.insert(_(ARGUMENTS), args);
-
- sendAndLogV8Request(jsonVal);
-}
-
-void QmlV8DebuggerClientPrivate::setExceptionBreak(QmlV8DebuggerClient::Exceptions type,
- bool enabled)
-{
- // { "seq" : <number>,
- // "type" : "request",
- // "command" : "setexceptionbreak",
- // "arguments" : { "type" : <string: "all", or "uncaught">,
- // "enabled" : <optional bool: enables the break type if true>
- // }
- // }
- QJsonObject jsonVal = initObject();
- jsonVal.insert(_(COMMAND), _(SETEXCEPTIONBREAK));
-
- QJsonObject args;
-
- if (type == QmlV8DebuggerClient::AllExceptions)
- args.insert(_(TYPE), _(ALL));
- //Not Supported
- // else if (type == QmlV8DebuggerClient::UncaughtExceptions)
- // args.setProperty(_(TYPE),QScriptValue(_(UNCAUGHT)));
-
- if (enabled)
- args.insert(_(ENABLED), enabled);
-
- jsonVal.insert(_(ARGUMENTS), args);
-
- sendAndLogV8Request(jsonVal);
-}
-
-void QmlV8DebuggerClientPrivate::version()
-{
- // { "seq" : <number>,
- // "type" : "request",
- // "command" : "version",
- // }
- QJsonObject jsonVal = initObject();
- jsonVal.insert(_(COMMAND), _(VERSION));
-
- sendAndLogV8Request(jsonVal);
-}
-
-//void QmlV8DebuggerClientPrivate::profile(ProfileCommand command)
-//{
-//// { "seq" : <number>,
-//// "type" : "request",
-//// "command" : "profile",
-//// "arguments" : { "command" : "resume" or "pause" }
-//// }
-// QScriptValue jsonVal = initObject();
-// jsonVal.setProperty(_(COMMAND), QScriptValue(_(PROFILE)));
-
-// QScriptValue args = m_parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT)));
-
-// if (command == Resume)
-// args.setProperty(_(COMMAND), QScriptValue(_(RESUME)));
-// else
-// args.setProperty(_(COMMAND), QScriptValue(_(PAUSE)));
-
-// args.setProperty(_("modules"), QScriptValue(1));
-// jsonVal.setProperty(_(ARGUMENTS), args);
-
-// const QScriptValue jsonMessage = m_stringifier.call(QScriptValue(), QScriptValueList() << jsonVal);
-// logSendMessage(QString(_("%1 %2 %3")).arg(_(V8DEBUG), _(V8REQUEST), jsonMessage.toString()));
-// q->sendMessage(packMessage(V8REQUEST, jsonMessage.toString().toUtf8()));
-//}
-
-QVariant valueFromRef(int handle, const QVariant &refsVal, bool *success)
-{
- *success = false;
- QVariant variant;
- const QVariantList refs = refsVal.toList();
- foreach (const QVariant &ref, refs) {
- const QVariantMap refData = ref.toMap();
- if (refData.value(_(HANDLE)).toInt() == handle) {
- variant = refData;
- *success = true;
- break;
- }
- }
- return variant;
-}
-
-QmlV8ObjectData extractData(const QVariant &data, const QVariant &refsVal)
-{
- // { "handle" : <handle>,
- // "type" : <"undefined", "null", "boolean", "number", "string", "object", "function" or "frame">
- // }
-
- // {"handle":<handle>,"type":"undefined"}
-
- // {"handle":<handle>,"type":"null"}
-
- // { "handle":<handle>,
- // "type" : <"boolean", "number" or "string">
- // "value" : <JSON encoded value>
- // }
-
- // {"handle":7,"type":"boolean","value":true}
-
- // {"handle":8,"type":"number","value":42}
-
- // { "handle" : <handle>,
- // "type" : "object",
- // "className" : <Class name, ECMA-262 property [[Class]]>,
- // "constructorFunction" : {"ref":<handle>},
- // "protoObject" : {"ref":<handle>},
- // "prototypeObject" : {"ref":<handle>},
- // "properties" : [ {"name" : <name>,
- // "ref" : <handle>
- // },
- // ...
- // ]
- // }
-
- // { "handle" : <handle>,
- // "type" : "function",
- // "className" : "Function",
- // "constructorFunction" : {"ref":<handle>},
- // "protoObject" : {"ref":<handle>},
- // "prototypeObject" : {"ref":<handle>},
- // "name" : <function name>,
- // "inferredName" : <inferred function name for anonymous functions>
- // "source" : <function source>,
- // "script" : <reference to function script>,
- // "scriptId" : <id of function script>,
- // "position" : <function begin position in script>,
- // "line" : <function begin source line in script>,
- // "column" : <function begin source column in script>,
- // "properties" : [ {"name" : <name>,
- // "ref" : <handle>
- // },
- // ...
- // ]
- // }
-
- QmlV8ObjectData objectData;
- const QVariantMap dataMap = data.toMap();
-
- objectData.name = dataMap.value(_(NAME)).toByteArray();
-
- if (dataMap.contains(_(REF))) {
- objectData.handle = dataMap.value(_(REF)).toInt();
- bool success;
- QVariant dataFromRef = valueFromRef(objectData.handle, refsVal, &success);
- if (success) {
- QmlV8ObjectData data = extractData(dataFromRef, refsVal);
- objectData.type = data.type;
- objectData.value = data.value;
- objectData.properties = data.properties;
- }
- } else {
- objectData.handle = dataMap.value(_(HANDLE)).toInt();
- QString type = dataMap.value(_(TYPE)).toString();
-
- if (type == _("undefined")) {
- objectData.type = QByteArray("undefined");
- objectData.value = QVariant(_("undefined"));
-
- } else if (type == _("null")) {
- objectData.type = QByteArray("null");
- objectData.value= QVariant(_("null"));
-
- } else if (type == _("boolean")) {
- objectData.type = QByteArray("boolean");
- objectData.value = dataMap.value(_(VALUE));
-
- } else if (type == _("number")) {
- objectData.type = QByteArray("number");
- objectData.value = dataMap.value(_(VALUE));
-
- } else if (type == _("string")) {
- objectData.type = QByteArray("string");
- objectData.value = dataMap.value(_(VALUE));
-
- } else if (type == _("object")) {
- objectData.type = QByteArray("object");
- objectData.value = dataMap.value(_("className"));
- objectData.properties = dataMap.value(_("properties")).toList();
-
- } else if (type == _("function")) {
- objectData.type = QByteArray("function");
- objectData.value = dataMap.value(_(NAME));
- objectData.properties = dataMap.value(_("properties")).toList();
-
- } else if (type == _("script")) {
- objectData.type = QByteArray("script");
- objectData.value = dataMap.value(_(NAME));
- }
- }
-
- return objectData;
-}
-
-void QmlV8DebuggerClientPrivate::clearCache()
-{
- currentFrameScopes.clear();
- updateLocalsAndWatchers.clear();
-}
-
-QByteArray QmlV8DebuggerClientPrivate::packMessage(const QByteArray &type, const QByteArray &message)
-{
- SDEBUG(message);
- QByteArray request;
- QmlDebugStream rs(&request, QIODevice::WriteOnly);
- QByteArray cmd = V8DEBUG;
- rs << cmd << type << message;
- return request;
-}
-
-QJsonObject QmlV8DebuggerClientPrivate::initObject()
-{
- return QJsonObject {
- {_(SEQ), ++sequence},
- {_(TYPE), _(REQUEST)}
- };
-}
-
-void QmlV8DebuggerClientPrivate::sendAndLogV8Request(const QJsonObject &request)
-{
- const QJsonDocument jsonMessage(request);
- logSendMessage(QString::fromLatin1("%1 %2 %3")
- .arg(_(V8DEBUG), _(V8REQUEST),
- QString::fromUtf8(jsonMessage.toJson(QJsonDocument::Compact))));
- q->sendMessage(packMessage(V8REQUEST, jsonMessage.toJson(QJsonDocument::Compact)));
-}
-
-void QmlV8DebuggerClientPrivate::logSendMessage(const QString &msg) const
-{
- if (engine)
- engine->logMessage(QLatin1String("V8DebuggerClient"), QmlEngine::LogSend, msg);
-}
-
-void QmlV8DebuggerClientPrivate::logReceiveMessage(const QString &msg) const
-{
- if (engine)
- engine->logMessage(QLatin1String("V8DebuggerClient"), QmlEngine::LogReceive, msg);
-}
-
-///////////////////////////////////////////////////////////////////////
-//
-// QmlV8DebuggerClient
-//
-///////////////////////////////////////////////////////////////////////
-
-QmlV8DebuggerClient::QmlV8DebuggerClient(QmlDebug::QmlDebugConnection *client)
- : QmlDebugClient(QLatin1String("V8Debugger"), client),
- d(new QmlV8DebuggerClientPrivate(this))
-{
-}
-
-QmlV8DebuggerClient::~QmlV8DebuggerClient()
-{
- delete d;
-}
-
-void QmlV8DebuggerClient::startSession()
-{
- flushSendBuffer();
- d->connect();
- //Query for the V8 version. This is
- //only for logging to the debuggerlog
- d->version();
-}
-
-void QmlV8DebuggerClient::endSession()
-{
- d->disconnect();
-}
-
-void QmlV8DebuggerClient::resetSession()
-{
- clearExceptionSelection();
-}
-
-void QmlV8DebuggerClient::executeStep()
-{
- clearExceptionSelection();
- d->continueDebugging(In);
-}
-
-void QmlV8DebuggerClient::executeStepOut()
-{
- clearExceptionSelection();
- d->continueDebugging(Out);
-}
-
-void QmlV8DebuggerClient::executeNext()
-{
- clearExceptionSelection();
- d->continueDebugging(Next);
-}
-
-void QmlV8DebuggerClient::executeStepI()
-{
- clearExceptionSelection();
- d->continueDebugging(In);
-}
-
-void QmlV8DebuggerClient::executeRunToLine(const ContextData &data)
-{
- d->setBreakpoint(QString(_(SCRIPTREGEXP)), data.fileName,
- true, data.lineNumber);
- clearExceptionSelection();
- d->continueDebugging(Continue);
-}
-
-void QmlV8DebuggerClient::continueInferior()
-{
- clearExceptionSelection();
- d->continueDebugging(Continue);
-}
-
-void QmlV8DebuggerClient::interruptInferior()
-{
- d->interrupt();
-}
-
-void QmlV8DebuggerClient::activateFrame(int index)
-{
- if (index != d->engine->stackHandler()->currentIndex())
- d->frame(d->stackIndexLookup.value(index));
- d->engine->stackHandler()->setCurrentIndex(index);
-}
-
-bool QmlV8DebuggerClient::acceptsBreakpoint(Breakpoint bp)
-{
- BreakpointType type = bp.type();
- return (type == BreakpointOnQmlSignalEmit
- || type == BreakpointByFileAndLine
- || type == BreakpointAtJavaScriptThrow);
-}
-
-void QmlV8DebuggerClient::insertBreakpoint(Breakpoint bp,
- int adjustedLine,
- int adjustedColumn)
-{
- const BreakpointParameters &params = bp.parameters();
-
- if (params.type == BreakpointAtJavaScriptThrow) {
- bp.notifyBreakpointInsertOk();
- d->setExceptionBreak(AllExceptions, params.enabled);
-
- } else if (params.type == BreakpointByFileAndLine) {
- d->setBreakpoint(QString(_(SCRIPTREGEXP)), params.fileName,
- params.enabled, adjustedLine, adjustedColumn,
- QLatin1String(params.condition), params.ignoreCount);
-
- } else if (params.type == BreakpointOnQmlSignalEmit) {
- d->setBreakpoint(QString(_(EVENT)), params.functionName, params.enabled);
- bp.notifyBreakpointInsertOk();
- }
-
- d->breakpointsSync.insert(d->sequence, bp.id());
-}
-
-void QmlV8DebuggerClient::removeBreakpoint(Breakpoint bp)
-{
- const BreakpointParameters &params = bp.parameters();
-
- int breakpoint = d->breakpoints.value(bp.id());
- d->breakpoints.remove(bp.id());
-
- if (params.type == BreakpointAtJavaScriptThrow)
- d->setExceptionBreak(AllExceptions);
- else if (params.type == BreakpointOnQmlSignalEmit)
- d->setBreakpoint(QString(_(EVENT)), params.functionName, false);
- else
- d->clearBreakpoint(breakpoint);
-}
-
-void QmlV8DebuggerClient::changeBreakpoint(Breakpoint bp)
-{
- const BreakpointParameters &params = bp.parameters();
-
- BreakpointResponse br = bp.response();
- if (params.type == BreakpointAtJavaScriptThrow) {
- d->setExceptionBreak(AllExceptions, params.enabled);
- br.enabled = params.enabled;
- bp.setResponse(br);
- } else if (params.type == BreakpointOnQmlSignalEmit) {
- d->setBreakpoint(QString(_(EVENT)), params.functionName, params.enabled);
- br.enabled = params.enabled;
- bp.setResponse(br);
- } else {
- //V8 supports only minimalistic changes in breakpoint
- //Remove the breakpoint and add again
- bp.notifyBreakpointChangeOk();
- bp.removeBreakpoint();
- BreakHandler *handler = d->engine->breakHandler();
- handler->appendBreakpoint(params);
- }
-}
-
-void QmlV8DebuggerClient::synchronizeBreakpoints()
-{
- //NOT USED
-}
-
-void QmlV8DebuggerClient::assignValueInDebugger(const WatchData * /*data*/,
- const QString &expr,
- const QVariant &valueV)
-{
- StackHandler *stackHandler = d->engine->stackHandler();
- QString expression = QString(_("%1 = %2;")).arg(expr).arg(valueV.toString());
- if (stackHandler->isContentsValid() && stackHandler->currentFrame().isUsable()) {
- d->evaluate(expression, false, false, stackHandler->currentIndex());
- d->updateLocalsAndWatchers.append(d->sequence);
- } else {
- d->engine->showMessage(QString(_("Cannot evaluate %1 in current stack frame")).arg(
- expression), ConsoleOutput);
- }
-}
-
-void QmlV8DebuggerClient::updateWatchData(const WatchData &/*data*/)
-{
- //NOT USED
-}
-
-void QmlV8DebuggerClient::executeDebuggerCommand(const QString &command)
-{
- StackHandler *stackHandler = d->engine->stackHandler();
- if (stackHandler->isContentsValid() && stackHandler->currentFrame().isUsable()) {
- d->evaluate(command, false, false, stackHandler->currentIndex());
- d->debuggerCommands.append(d->sequence);
- } else {
- //Currently cannot evaluate if not in a javascript break
- d->engine->showMessage(QString(_("Cannot evaluate %1 in current stack frame")).arg(
- command), ConsoleOutput);
- }
-}
-
-void QmlV8DebuggerClient::synchronizeWatchers(const QStringList &watchers)
-{
- SDEBUG(watchers);
- if (d->engine->state() != InferiorStopOk)
- return;
-
- foreach (const QString &exp, watchers) {
- StackHandler *stackHandler = d->engine->stackHandler();
- if (stackHandler->isContentsValid() && stackHandler->currentFrame().isUsable()) {
- d->evaluate(exp, false, false, stackHandler->currentIndex());
- d->evaluatingExpression.insert(d->sequence, exp);
- }
- }
-}
-
-void QmlV8DebuggerClient::expandObject(const QByteArray &iname, quint64 objectId)
-{
- if (objectId == 0) {
- //We may have got the global object
- const WatchItem *watch = d->engine->watchHandler()->findItem(iname);
- if (watch->value == QLatin1String("global")) {
- StackHandler *stackHandler = d->engine->stackHandler();
- if (stackHandler->isContentsValid() && stackHandler->currentFrame().isUsable()) {
- d->evaluate(watch->name, false, false, stackHandler->currentIndex());
- d->evaluatingExpression.insert(d->sequence, QLatin1String(iname));
- }
- return;
- }
- }
- d->localsAndWatchers.insertMulti(objectId, iname);
- d->lookup(QList<int>() << objectId);
-}
-
-void QmlV8DebuggerClient::setEngine(QmlEngine *engine)
-{
- d->engine = engine;
- connect(this, &QmlV8DebuggerClient::stackFrameCompleted,
- engine, &QmlEngine::stackFrameCompleted);
-}
-
-void QmlV8DebuggerClient::getSourceFiles()
-{
- d->scripts(4, QList<int>(), true, QVariant());
-}
-
-void QmlV8DebuggerClient::messageReceived(const QByteArray &data)
-{
- QmlDebugStream ds(data);
- QByteArray command;
- ds >> command;
-
- if (command == V8DEBUG) {
- QByteArray type;
- QByteArray response;
- ds >> type >> response;
-
- d->logReceiveMessage(_(V8DEBUG) + QLatin1Char(' ') + QLatin1String(type));
- if (type == CONNECT) {
- //debugging session started
-
- } else if (type == INTERRUPT) {
- //debug break requested
-
- } else if (type == BREAKONSIGNAL) {
- //break on signal handler requested
-
- } else if (type == V8MESSAGE) {
- const QString responseString = QLatin1String(response);
- SDEBUG(responseString);
- d->logReceiveMessage(QLatin1String(V8MESSAGE) + QLatin1Char(' ') + responseString);
-
- const QVariantMap resp =
- QJsonDocument::fromJson(responseString.toUtf8()).toVariant().toMap();
-
- const QString type(resp.value(_(TYPE)).toString());
-
- if (type == _("response")) {
-
- bool success = resp.value(_("success")).toBool();
- if (!success) {
- SDEBUG("Request was unsuccessful");
- }
-
- const QString debugCommand(resp.value(_(COMMAND)).toString());
-
- if (debugCommand == _(DISCONNECT)) {
- //debugging session ended
-
- } else if (debugCommand == _(CONTINEDEBUGGING)) {
- //do nothing, wait for next break
-
- } else if (debugCommand == _(BACKTRACE)) {
- if (success)
- updateStack(resp.value(_(BODY)), resp.value(_(REFS)));
-
- } else if (debugCommand == _(LOOKUP)) {
- if (success)
- expandLocalsAndWatchers(resp.value(_(BODY)), resp.value(_(REFS)));
-
- } else if (debugCommand == _(EVALUATE)) {
- int seq = resp.value(_("request_seq")).toInt();
- if (success) {
- updateEvaluationResult(seq, success, resp.value(_(BODY)), resp.value(_(REFS)));
- } else {
- QVariantMap map;
- map.insert(_(TYPE), QVariant(_("string")));
- map.insert(_(VALUE), resp.value(_("message")));
- updateEvaluationResult(seq, success, QVariant(map), QVariant());
- }
-
- } else if (debugCommand == _(SETBREAKPOINT)) {
- // { "seq" : <number>,
- // "type" : "response",
- // "request_seq" : <number>,
- // "command" : "setbreakpoint",
- // "body" : { "type" : <"function" or "script">
- // "breakpoint" : <break point number of the new break point>
- // }
- // "running" : <is the VM running after sending this response>
- // "success" : true
- // }
-
- int seq = resp.value(_("request_seq")).toInt();
- const QVariantMap breakpointData = resp.value(_(BODY)).toMap();
- int index = breakpointData.value(_("breakpoint")).toInt();
-
- if (d->breakpointsSync.contains(seq)) {
- BreakpointModelId id = d->breakpointsSync.take(seq);
- d->breakpoints.insert(id, index);
-
- //Is actual position info present? Then breakpoint was
- //accepted
- const QVariantList actualLocations =
- breakpointData.value(
- _("actual_locations")).toList();
- if (actualLocations.count()) {
- //The breakpoint requested line should be same as
- //actual line
- BreakHandler *handler = d->engine->breakHandler();
- Breakpoint bp = handler->breakpointById(id);
- if (bp.state() != BreakpointInserted) {
- BreakpointResponse br = bp.response();
- br.lineNumber = breakpointData.value(_("line")).toInt() + 1;
- bp.setResponse(br);
- bp.notifyBreakpointInsertOk();
- }
- }
-
-
- } else {
- d->breakpointsTemp.append(index);
- }
-
-
- } else if (debugCommand == _(CLEARBREAKPOINT)) {
- // DO NOTHING
-
- } else if (debugCommand == _(SETEXCEPTIONBREAK)) {
- // { "seq" : <number>,
- // "type" : "response",
- // "request_seq" : <number>,
- // "command" : "setexceptionbreak",
- // "body" : { "type" : <string: "all" or "uncaught" corresponding to the request.>,
- // "enabled" : <bool: true if the break type is currently enabled as a result of the request>
- // }
- // "running" : true
- // "success" : true
- // }
-
-
- } else if (debugCommand == _(FRAME)) {
- if (success)
- setCurrentFrameDetails(resp.value(_(BODY)), resp.value(_(REFS)));
-
- } else if (debugCommand == _(SCOPE)) {
- if (success)
- updateScope(resp.value(_(BODY)), resp.value(_(REFS)));
-
- } else if (debugCommand == _(SCRIPTS)) {
- // { "seq" : <number>,
- // "type" : "response",
- // "request_seq" : <number>,
- // "command" : "scripts",
- // "body" : [ { "name" : <name of the script>,
- // "id" : <id of the script>
- // "lineOffset" : <line offset within the containing resource>
- // "columnOffset" : <column offset within the containing resource>
- // "lineCount" : <number of lines in the script>
- // "data" : <optional data object added through the API>
- // "source" : <source of the script if includeSource was specified in the request>
- // "sourceStart" : <first 80 characters of the script if includeSource was not specified in the request>
- // "sourceLength" : <total length of the script in characters>
- // "scriptType" : <script type (see request for values)>
- // "compilationType" : < How was this script compiled:
- // 0 if script was compiled through the API
- // 1 if script was compiled through eval
- // >
- // "evalFromScript" : <if "compilationType" is 1 this is the script from where eval was called>
- // "evalFromLocation" : { line : < if "compilationType" is 1 this is the line in the script from where eval was called>
- // column : < if "compilationType" is 1 this is the column in the script from where eval was called>
- // ]
- // "running" : <is the VM running after sending this response>
- // "success" : true
- // }
-
- if (success) {
- const QVariantList body = resp.value(_(BODY)).toList();
-
- QStringList sourceFiles;
- for (int i = 0; i < body.size(); ++i) {
- const QVariantMap entryMap = body.at(i).toMap();
- const int lineOffset = entryMap.value(QLatin1String("lineOffset")).toInt();
- const int columnOffset = entryMap.value(QLatin1String("columnOffset")).toInt();
- const QString name = entryMap.value(QLatin1String("name")).toString();
- const QString source = entryMap.value(QLatin1String("source")).toString();
-
- if (name.isEmpty())
- continue;
-
- if (!sourceFiles.contains(name))
- sourceFiles << name;
-
- d->engine->updateScriptSource(name, lineOffset, columnOffset, source);
- }
- d->engine->setSourceFiles(sourceFiles);
- }
- } else if (debugCommand == _(VERSION)) {
- d->logReceiveMessage(QString(_("Using V8 Version: %1")).arg(
- resp.value(_(BODY)).toMap().
- value(_("V8Version")).toString()));
-
- } else {
- // DO NOTHING
- }
-
- } else if (type == _(EVENT)) {
- const QString eventType(resp.value(_(EVENT)).toString());
-
- if (eventType == _("break")) {
- const QVariantMap breakData = resp.value(_(BODY)).toMap();
- const QString invocationText = breakData.value(_("invocationText")).toString();
- const QString scriptUrl = breakData.value(_("script")).toMap().value(_("name")).toString();
- const QString sourceLineText = breakData.value(_("sourceLineText")).toString();
-
- bool inferiorStop = true;
-
- QList<int> v8BreakpointIds;
- {
- const QVariantList v8BreakpointIdList = breakData.value(_("breakpoints")).toList();
- foreach (const QVariant &breakpointId, v8BreakpointIdList)
- v8BreakpointIds << breakpointId.toInt();
- }
-
- if (!v8BreakpointIds.isEmpty() && invocationText.startsWith(_("[anonymous]()"))
- && scriptUrl.endsWith(_(".qml"))
- && sourceLineText.trimmed().startsWith(QLatin1Char('('))) {
-
- // we hit most likely the anonymous wrapper function automatically generated for bindings
- // -> relocate the breakpoint to column: 1 and continue
-
- int newColumn = sourceLineText.indexOf(QLatin1Char('(')) + 1;
- BreakHandler *handler = d->engine->breakHandler();
-
- foreach (int v8Id, v8BreakpointIds) {
- const BreakpointModelId id = d->breakpoints.key(v8Id);
- Breakpoint bp = handler->breakpointById(id);
- if (bp.isValid()) {
- const BreakpointParameters &params = bp.parameters();
-
- d->clearBreakpoint(v8Id);
- d->setBreakpoint(QString(_(SCRIPTREGEXP)),
- params.fileName,
- params.enabled,
- params.lineNumber,
- newColumn,
- QString(QString::fromLatin1(params.condition)),
- params.ignoreCount);
- d->breakpointsSync.insert(d->sequence, id);
- }
- }
- d->continueDebugging(Continue);
- inferiorStop = false;
- }
-
- //Skip debug break if this is an internal function
- if (sourceLineText == _(INTERNAL_FUNCTION)) {
- d->continueDebugging(d->previousStepAction);
- inferiorStop = false;
- }
-
- if (inferiorStop) {
- //Update breakpoint data
- BreakHandler *handler = d->engine->breakHandler();
- foreach (int v8Id, v8BreakpointIds) {
- const BreakpointModelId id = d->breakpoints.key(v8Id);
- Breakpoint bp = handler->breakpointById(id);
- if (bp) {
- BreakpointResponse br = bp.response();
- if (br.functionName.isEmpty()) {
- br.functionName = invocationText;
- bp.setResponse(br);
- }
- if (bp.state() != BreakpointInserted) {
- br.lineNumber = breakData.value(
- _("sourceLine")).toInt() + 1;
- bp.setResponse(br);
- bp.notifyBreakpointInsertOk();
- }
- }
- }
-
- if (d->engine->state() == InferiorRunOk) {
- foreach (const QVariant &breakpointId, v8BreakpointIds) {
- if (d->breakpointsTemp.contains(breakpointId.toInt()))
- d->clearBreakpoint(breakpointId.toInt());
- }
- d->engine->inferiorSpontaneousStop();
- d->backtrace();
- } else if (d->engine->state() == InferiorStopOk) {
- d->backtrace();
- }
- }
-
- } else if (eventType == _("exception")) {
- const QVariantMap body = resp.value(_(BODY)).toMap();
- int lineNumber = body.value(_("sourceLine")).toInt() + 1;
-
- const QVariantMap script = body.value(_("script")).toMap();
- QUrl fileUrl(script.value(_(NAME)).toString());
- QString filePath = d->engine->toFileInProject(fileUrl);
-
- const QVariantMap exception = body.value(_("exception")).toMap();
- QString errorMessage = exception.value(_("text")).toString();
-
- highlightExceptionCode(lineNumber, filePath, errorMessage);
-
- if (d->engine->state() == InferiorRunOk) {
- d->engine->inferiorSpontaneousStop();
- d->backtrace();
- }
-
- if (d->engine->state() == InferiorStopOk)
- d->backtrace();
-
- } else if (eventType == _("afterCompile")) {
- //Currently break point relocation is disabled.
- //Uncomment the line below when it will be enabled.
-// d->listBreakpoints();
- }
-
- //Sometimes we do not get event type!
- //This is most probably due to a wrong eval expression.
- //Redirect output to console.
- if (eventType.isEmpty()) {
- bool success = resp.value(_("success")).toBool();
- QVariantMap map;
- map.insert(_(TYPE), QVariant(_("string")));
- map.insert(_(VALUE), resp.value(_("message")));
- //Since there is no sequence value, best estimate is
- //last sequence value
- updateEvaluationResult(d->sequence, success, QVariant(map), QVariant());
- }
-
- } //EVENT
- } //V8MESSAGE
-
- } else {
- //DO NOTHING
- }
-}
-
-void QmlV8DebuggerClient::updateStack(const QVariant &bodyVal, const QVariant &refsVal)
-{
- // { "seq" : <number>,
- // "type" : "response",
- // "request_seq" : <number>,
- // "command" : "backtrace",
- // "body" : { "fromFrame" : <number>
- // "toFrame" : <number>
- // "totalFrames" : <number>
- // "frames" : <array of frames - see frame request for details>
- // }
- // "running" : <is the VM running after sending this response>
- // "success" : true
- // }
-
- const QVariantMap body = bodyVal.toMap();
- const QVariantList frames = body.value(_("frames")).toList();
-
- int fromFrameIndex = body.value(_("fromFrame")).toInt();
-
- QTC_ASSERT(0 == fromFrameIndex, return);
-
- StackHandler *stackHandler = d->engine->stackHandler();
- StackFrames stackFrames;
- int i = 0;
- d->stackIndexLookup.clear();
- foreach (const QVariant &frame, frames) {
- StackFrame stackFrame = extractStackFrame(frame, refsVal);
- if (stackFrame.level < 0)
- continue;
- d->stackIndexLookup.insert(i, stackFrame.level);
- stackFrame.level = i;
- stackFrames << stackFrame;
- i++;
- }
- stackHandler->setFrames(stackFrames);
-
- //Populate locals and watchers wrt top frame
- //Update all Locals visible in current scope
- //Traverse the scope chain and store the local properties
- //in a list and show them in the Locals Window.
- setCurrentFrameDetails(frames.value(0), refsVal);
-}
-
-StackFrame QmlV8DebuggerClient::extractStackFrame(const QVariant &bodyVal, const QVariant &refsVal)
-{
- // { "seq" : <number>,
- // "type" : "response",
- // "request_seq" : <number>,
- // "command" : "frame",
- // "body" : { "index" : <frame number>,
- // "receiver" : <frame receiver>,
- // "func" : <function invoked>,
- // "script" : <script for the function>,
- // "constructCall" : <boolean indicating whether the function was called as constructor>,
- // "debuggerFrame" : <boolean indicating whether this is an internal debugger frame>,
- // "arguments" : [ { name: <name of the argument - missing of anonymous argument>,
- // value: <value of the argument>
- // },
- // ... <the array contains all the arguments>
- // ],
- // "locals" : [ { name: <name of the local variable>,
- // value: <value of the local variable>
- // },
- // ... <the array contains all the locals>
- // ],
- // "position" : <source position>,
- // "line" : <source line>,
- // "column" : <source column within the line>,
- // "sourceLineText" : <text for current source line>,
- // "scopes" : [ <array of scopes, see scope request below for format> ],
-
- // }
- // "running" : <is the VM running after sending this response>
- // "success" : true
- // }
-
- const QVariantMap body = bodyVal.toMap();
-
- StackFrame stackFrame;
- stackFrame.level = body.value(_("index")).toInt();
- //Do not insert the frame corresponding to the internal function
- if (body.value(QLatin1String("sourceLineText")) == QLatin1String(INTERNAL_FUNCTION)) {
- stackFrame.level = -1;
- return stackFrame;
- }
-
- QmlV8ObjectData objectData = extractData(body.value(_("func")), refsVal);
- QString functionName = objectData.value.toString();
- if (functionName.isEmpty())
- functionName = tr("Anonymous Function");
- stackFrame.function = functionName;
-
- objectData = extractData(body.value(_("script")), refsVal);
- stackFrame.file = d->engine->toFileInProject(objectData.value.toString());
- stackFrame.usable = QFileInfo(stackFrame.file).isReadable();
-
- objectData = extractData(body.value(_("receiver")), refsVal);
- stackFrame.to = objectData.value.toString();
-
- stackFrame.line = body.value(_("line")).toInt() + 1;
-
- return stackFrame;
-}
-
-void QmlV8DebuggerClient::setCurrentFrameDetails(const QVariant &bodyVal, const QVariant &refsVal)
-{
- // { "seq" : <number>,
- // "type" : "response",
- // "request_seq" : <number>,
- // "command" : "frame",
- // "body" : { "index" : <frame number>,
- // "receiver" : <frame receiver>,
- // "func" : <function invoked>,
- // "script" : <script for the function>,
- // "constructCall" : <boolean indicating whether the function was called as constructor>,
- // "debuggerFrame" : <boolean indicating whether this is an internal debugger frame>,
- // "arguments" : [ { name: <name of the argument - missing of anonymous argument>,
- // value: <value of the argument>
- // },
- // ... <the array contains all the arguments>
- // ],
- // "locals" : [ { name: <name of the local variable>,
- // value: <value of the local variable>
- // },
- // ... <the array contains all the locals>
- // ],
- // "position" : <source position>,
- // "line" : <source line>,
- // "column" : <source column within the line>,
- // "sourceLineText" : <text for current source line>,
- // "scopes" : [ <array of scopes, see scope request below for format> ],
-
- // }
- // "running" : <is the VM running after sending this response>
- // "success" : true
- // }
- QVariantMap currentFrame = bodyVal.toMap();
-
- StackHandler *stackHandler = d->engine->stackHandler();
- WatchHandler * watchHandler = d->engine->watchHandler();
- watchHandler->notifyUpdateStarted();
- d->clearCache();
-
- const int frameIndex = stackHandler->currentIndex();
- QSet<QByteArray> expandedInames = watchHandler->expandedINames();
- QHash<quint64, QByteArray> handlesToLookup;
- // Store handles of all expanded watch data
- foreach (const QByteArray &iname, expandedInames) {
- const WatchItem *item = watchHandler->findItem(iname);
- if (item && item->isLocal())
- handlesToLookup.insert(item->id, iname);
- }
- if (frameIndex < 0)
- return;
- const StackFrame frame = stackHandler->currentFrame();
- if (!frame.isUsable())
- return;
-
- //Set "this" variable
- {
- auto item = new WatchItem("local.this", QLatin1String("this"));
- QmlV8ObjectData objectData = extractData(currentFrame.value(_("receiver")), refsVal);
- item->id = objectData.handle;
- item->type = objectData.type;
- item->value = objectData.value.toString();
- item->setHasChildren(objectData.properties.count());
- //Incase of global object, we do not get children
- //Set children nevertheless and query later
- if (item->value == QLatin1String("global")) {
- item->setHasChildren(true);
- item->id = 0;
- }
- watchHandler->insertItem(item);
- }
-
- const QVariantList currentFrameScopes = currentFrame.value(_("scopes")).toList();
- foreach (const QVariant &scope, currentFrameScopes) {
- //Do not query for global types (0)
- //Showing global properties increases clutter.
- if (scope.toMap().value(_("type")).toInt() == 0)
- continue;
- int scopeIndex = scope.toMap().value(_("index")).toInt();
- d->currentFrameScopes.append(scopeIndex);
- d->scope(scopeIndex);
- }
- d->engine->gotoLocation(stackHandler->currentFrame());
-
- // Expand watch data that were previously expanded
- QHash<quint64, QByteArray>::const_iterator itEnd = handlesToLookup.constEnd();
- for (QHash<quint64, QByteArray>::const_iterator it = handlesToLookup.constBegin(); it != itEnd; ++it)
- expandObject(it.value(), it.key());
- emit stackFrameCompleted();
-}
-
-void QmlV8DebuggerClient::updateScope(const QVariant &bodyVal, const QVariant &refsVal)
-{
-// { "seq" : <number>,
-// "type" : "response",
-// "request_seq" : <number>,
-// "command" : "scope",
-// "body" : { "index" : <index of this scope in the scope chain. Index 0 is the top scope
-// and the global scope will always have the highest index for a
-// frame>,
-// "frameIndex" : <index of the frame>,
-// "type" : <type of the scope:
-// 0: Global
-// 1: Local
-// 2: With
-// 3: Closure
-// 4: Catch >,
-// "object" : <the scope object defining the content of the scope.
-// For local and closure scopes this is transient objects,
-// which has a negative handle value>
-// }
-// "running" : <is the VM running after sending this response>
-// "success" : true
-// }
- QVariantMap bodyMap = bodyVal.toMap();
-
- //Check if the frameIndex is same as current Stack Index
- StackHandler *stackHandler = d->engine->stackHandler();
- if (bodyMap.value(_("frameIndex")).toInt() != stackHandler->currentIndex())
- return;
-
- QmlV8ObjectData objectData = extractData(bodyMap.value(_("object")), refsVal);
-
- QList<int> handlesToLookup;
- foreach (const QVariant &property, objectData.properties) {
- QmlV8ObjectData localData = extractData(property, refsVal);
- auto item = new WatchItem;
- item->exp = localData.name;
- //Check for v8 specific local data
- if (item->exp.startsWith('.') || item->exp.isEmpty())
- continue;
-
- item->name = QLatin1String(item->exp);
- item->iname = QByteArray("local.") + item->exp;
-
- int handle = localData.handle;
- if (localData.value.isValid()) {
- item->id = handle;
- item->type = localData.type;
- item->value = localData.value.toString();
- item->setHasChildren(localData.properties.count());
- d->engine->watchHandler()->insertItem(item);
- } else {
- handlesToLookup << handle;
- d->localsAndWatchers.insertMulti(handle, item->exp);
- }
- }
-
- if (!handlesToLookup.isEmpty())
- d->lookup(handlesToLookup);
- else
- d->engine->watchHandler()->notifyUpdateFinished();
-}
-
-QmlJS::ConsoleItem *constructLogItemTree(QmlJS::ConsoleItem *parent,
- const QmlV8ObjectData &objectData,
- const QVariant &refsVal)
-{
- using namespace QmlJS;
- bool sorted = boolSetting(SortStructMembers);
- if (!objectData.value.isValid())
- return 0;
-
- QString text;
- if (objectData.name.isEmpty())
- text = objectData.value.toString();
- else
- text = QString(_("%1: %2")).arg(QString::fromLatin1(objectData.name))
- .arg(objectData.value.toString());
-
- ConsoleItem *item = new ConsoleItem(parent, ConsoleItem::UndefinedType, text);
-
- QSet<QString> childrenFetched;
- foreach (const QVariant &property, objectData.properties) {
- const QmlV8ObjectData childObjectData = extractData(property, refsVal);
- if (childObjectData.handle == objectData.handle)
- continue;
- ConsoleItem *child = constructLogItemTree(item, childObjectData, refsVal);
- if (child) {
- const QString text = child->text();
- if (childrenFetched.contains(text))
- continue;
- childrenFetched.insert(text);
- item->insertChild(child, sorted);
- }
- }
-
- return item;
-}
-
-void QmlV8DebuggerClient::updateEvaluationResult(int sequence, bool success,
- const QVariant &bodyVal, const QVariant &refsVal)
-{
- // { "seq" : <number>,
- // "type" : "response",
- // "request_seq" : <number>,
- // "command" : "evaluate",
- // "body" : ...
- // "running" : <is the VM running after sending this response>
- // "success" : true
- // }
- WatchHandler *watchHandler = d->engine->watchHandler();
- if (d->updateLocalsAndWatchers.contains(sequence)) {
- d->updateLocalsAndWatchers.removeOne(sequence);
- //Update the locals
- foreach (int index, d->currentFrameScopes)
- d->scope(index);
- //Also update "this"
- QByteArray iname("local.this");
- const WatchItem *parent = watchHandler->findItem(iname);
- d->localsAndWatchers.insertMulti(parent->id, iname);
- d->lookup(QList<int>() << parent->id);
-
- } else if (d->debuggerCommands.contains(sequence)) {
- d->updateLocalsAndWatchers.removeOne(sequence);
- QmlV8ObjectData body = extractData(bodyVal, refsVal);
- using namespace QmlJS;
- ConsoleManagerInterface *consoleManager = ConsoleManagerInterface::instance();
- if (consoleManager) {
- ConsoleItem *item = constructLogItemTree(consoleManager->rootItem(), body, refsVal);
- if (item)
- consoleManager->printToConsolePane(item);
- }
- //Update the locals
- foreach (int index, d->currentFrameScopes)
- d->scope(index);
-
- } else {
- QmlV8ObjectData body = extractData(bodyVal, refsVal);
- if (d->evaluatingExpression.contains(sequence)) {
- QString exp = d->evaluatingExpression.take(sequence);
- //Do we have request to evaluate a local?
- if (exp.startsWith(QLatin1String("local."))) {
- const WatchItem *item = watchHandler->findItem(exp.toLatin1());
- createWatchDataList(item, body.properties, refsVal);
- } else {
- QByteArray iname = watchHandler->watcherName(exp.toLatin1());
- SDEBUG(QString(iname));
-
- auto item = new WatchItem(iname, exp);
- item->exp = exp.toLatin1();
- item->id = body.handle;
- if (success) {
- item->type = body.type;
- item->value = body.value.toString();
- item->wantsChildren = body.properties.count();
- } else {
- //Do not set type since it is unknown
- item->setError(body.value.toString());
- }
- watchHandler->insertItem(item);
- createWatchDataList(item, body.properties, refsVal);
- }
- //Insert the newly evaluated expression to the Watchers Window
- }
- }
-}
-
-void QmlV8DebuggerClient::expandLocalsAndWatchers(const QVariant &bodyVal, const QVariant &refsVal)
-{
- // { "seq" : <number>,
- // "type" : "response",
- // "request_seq" : <number>,
- // "command" : "lookup",
- // "body" : <array of serialized objects indexed using their handle>
- // "running" : <is the VM running after sending this response>
- // "success" : true
- // }
- const QVariantMap body = bodyVal.toMap();
-
- QStringList handlesList = body.keys();
- WatchHandler *watchHandler = d->engine->watchHandler();
- foreach (const QString &handle, handlesList) {
- QmlV8ObjectData bodyObjectData = extractData(
- body.value(handle), refsVal);
- QByteArray prepend = d->localsAndWatchers.take(handle.toInt());
-
- if (prepend.startsWith("local.") || prepend.startsWith("watch.")) {
- // Data for expanded local/watch.
- // Could be an object or function.
- const WatchItem *parent = watchHandler->findItem(prepend);
- createWatchDataList(parent, bodyObjectData.properties, refsVal);
- } else {
- //rest
- auto item = new WatchItem;
- item->exp = prepend;
- item->name = QLatin1String(item->exp);
- item->iname = QByteArray("local.") + item->exp;
- item->id = handle.toInt();
-
- item->type = bodyObjectData.type;
- item->value = bodyObjectData.value.toString();
-
- item->setHasChildren(bodyObjectData.properties.count());
-
- d->engine->watchHandler()->insertItem(item);
- }
- }
- d->engine->watchHandler()->notifyUpdateFinished();
-}
-
-void QmlV8DebuggerClient::createWatchDataList(const WatchItem *parent,
- const QVariantList &properties,
- const QVariant &refsVal)
-{
- if (properties.count()) {
- QTC_ASSERT(parent, return);
- foreach (const QVariant &property, properties) {
- QmlV8ObjectData propertyData = extractData(property, refsVal);
- auto item = new WatchItem;
- item->name = QString::fromUtf8(propertyData.name);
-
- //Check for v8 specific local data
- if (item->name.startsWith(QLatin1Char('.')) || item->name.isEmpty())
- continue;
- if (parent->type == "object") {
- if (parent->value == _("Array"))
- item->exp = parent->exp + '[' + item->name.toLatin1() + ']';
- else if (parent->value == _("Object"))
- item->exp = parent->exp + '.' + item->name.toLatin1();
- } else {
- item->exp = item->name.toLatin1();
- }
-
- item->iname = parent->iname + '.' + item->name.toLatin1();
- item->id = propertyData.handle;
- item->type = propertyData.type;
- item->value = propertyData.value.toString();
- item->setHasChildren(propertyData.properties.count());
- d->engine->watchHandler()->insertItem(item);
- }
- }
-}
-
-void QmlV8DebuggerClient::highlightExceptionCode(int lineNumber,
- const QString &filePath,
- const QString &errorMessage)
-{
- QList<IEditor *> editors = DocumentModel::editorsForFilePath(filePath);
-
- // set up the format for the errors
- QTextCharFormat errorFormat;
- errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
- errorFormat.setUnderlineColor(Qt::red);
-
- foreach (IEditor *editor, editors) {
- TextEditor::TextEditorWidget *ed = qobject_cast<TextEditor::TextEditorWidget *>(editor->widget());
- if (!ed)
- continue;
-
- QList<QTextEdit::ExtraSelection> selections;
- QTextEdit::ExtraSelection sel;
- sel.format = errorFormat;
- QTextCursor c(ed->document()->findBlockByNumber(lineNumber - 1));
- const QString text = c.block().text();
- for (int i = 0; i < text.size(); ++i) {
- if (! text.at(i).isSpace()) {
- c.setPosition(c.position() + i);
- break;
- }
- }
- c.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
- sel.cursor = c;
-
- sel.format.setToolTip(errorMessage);
-
- selections.append(sel);
- ed->setExtraSelections(TextEditor::TextEditorWidget::DebuggerExceptionSelection, selections);
-
- QString message = QString(_("%1: %2: %3")).arg(filePath).arg(lineNumber)
- .arg(errorMessage);
- d->engine->showMessage(message, ConsoleOutput);
- }
-}
-
-void QmlV8DebuggerClient::clearExceptionSelection()
-{
- QList<QTextEdit::ExtraSelection> selections;
-
- foreach (IEditor *editor, DocumentModel::editorsForOpenedDocuments()) {
- TextEditor::TextEditorWidget *ed = qobject_cast<TextEditor::TextEditorWidget *>(editor->widget());
- if (!ed)
- continue;
-
- ed->setExtraSelections(TextEditor::TextEditorWidget::DebuggerExceptionSelection, selections);
- }
-
-}
-
-void QmlV8DebuggerClient::stateChanged(State state)
-{
- emit newState(state);
-}
-
-void QmlV8DebuggerClient::sendMessage(const QByteArray &msg)
-{
- if (state() == Enabled)
- QmlDebugClient::sendMessage(msg);
- else
- d->sendBuffer.append(msg);
-}
-
-void QmlV8DebuggerClient::flushSendBuffer()
-{
- QTC_ASSERT(state() == Enabled, return);
- foreach (const QByteArray &msg, d->sendBuffer)
- QmlDebugClient::sendMessage(msg);
- d->sendBuffer.clear();
-}
-
-} // Internal
-} // Debugger
diff --git a/src/plugins/debugger/qml/qmlv8debuggerclient.h b/src/plugins/debugger/qml/qmlv8debuggerclient.h
deleted file mode 100644
index 5adb989efe..0000000000
--- a/src/plugins/debugger/qml/qmlv8debuggerclient.h
+++ /dev/null
@@ -1,143 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms and
-** conditions see http://www.qt.io/terms-conditions. For further information
-** use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-****************************************************************************/
-
-#ifndef QMLV8DEBUGGERCLIENT_H
-#define QMLV8DEBUGGERCLIENT_H
-
-#include <debugger/debuggerengine.h>
-#include <qmldebug/qmldebugclient.h>
-
-namespace Debugger {
-namespace Internal {
-
-class WatchData;
-class WatchItem;
-class QmlEngine;
-class QmlV8DebuggerClientPrivate;
-
-class QmlV8DebuggerClient : public QmlDebug::QmlDebugClient
-{
- Q_OBJECT
-
- enum Exceptions
- {
- NoExceptions,
- UncaughtExceptions,
- AllExceptions
- };
-
- enum StepAction
- {
- Continue,
- In,
- Out,
- Next
- };
-
-public:
- explicit QmlV8DebuggerClient(QmlDebug::QmlDebugConnection *client);
- ~QmlV8DebuggerClient();
-
- void startSession();
- void endSession();
- void resetSession();
-
- void executeStep();
- void executeStepOut();
- void executeNext();
- void executeStepI();
-
- void executeRunToLine(const ContextData &data);
-
- void continueInferior();
- void interruptInferior();
-
- void activateFrame(int index);
-
- bool acceptsBreakpoint(Breakpoint bp);
- void insertBreakpoint(Breakpoint bp, int adjustedLine,
- int adjustedColumn = -1);
- void removeBreakpoint(Breakpoint bp);
- void changeBreakpoint(Breakpoint bp);
- void synchronizeBreakpoints();
-
- void assignValueInDebugger(const WatchData *data,
- const QString &expression,
- const QVariant &valueV);
-
- void updateWatchData(const WatchData &);
- void executeDebuggerCommand(const QString &command);
-
- void synchronizeWatchers(const QStringList &watchers);
-
- void expandObject(const QByteArray &iname, quint64 objectId);
-
- void setEngine(QmlEngine *engine);
-
- void getSourceFiles();
-
- void flushSendBuffer();
-
- void stateChanged(State state);
- void sendMessage(const QByteArray &msg);
-
-signals:
- void newState(QmlDebug::QmlDebugClient::State state);
- void stackFrameCompleted();
-
-protected:
- void messageReceived(const QByteArray &data);
-
-private:
- void updateStack(const QVariant &bodyVal, const QVariant &refsVal);
- StackFrame extractStackFrame(const QVariant &bodyVal, const QVariant &refsVal);
- void setCurrentFrameDetails(const QVariant &bodyVal, const QVariant &refsVal);
- void updateScope(const QVariant &bodyVal, const QVariant &refsVal);
-
- void updateEvaluationResult(int sequence, bool success, const QVariant &bodyVal,
- const QVariant &refsVal);
- void expandLocalsAndWatchers(const QVariant &bodyVal, const QVariant &refsVal);
- void createWatchDataList(const WatchItem *parent,
- const QVariantList &properties,
- const QVariant &refsVal);
-
- void highlightExceptionCode(int lineNumber, const QString &filePath,
- const QString &errorMessage);
- void clearExceptionSelection();
-
-private:
- QmlV8DebuggerClientPrivate *d;
- friend class QmlV8DebuggerClientPrivate;
-};
-
-} // Internal
-} // Debugger
-
-#endif // QMLV8DEBUGGERCLIENT_H