diff options
Diffstat (limited to 'src/plugins/debugger/qml')
-rw-r--r-- | src/plugins/debugger/qml/baseqmldebuggerclient.h | 111 | ||||
-rw-r--r-- | src/plugins/debugger/qml/qml.pri | 10 | ||||
-rw-r--r-- | src/plugins/debugger/qml/qmladapter.cpp | 242 | ||||
-rw-r--r-- | src/plugins/debugger/qml/qmladapter.h | 107 | ||||
-rw-r--r-- | src/plugins/debugger/qml/qmlengine.cpp | 2372 | ||||
-rw-r--r-- | src/plugins/debugger/qml/qmlengine.h | 92 | ||||
-rw-r--r-- | src/plugins/debugger/qml/qmlengineutils.cpp | 311 | ||||
-rw-r--r-- | src/plugins/debugger/qml/qmlengineutils.h (renamed from src/plugins/debugger/qml/baseqmldebuggerclient.cpp) | 56 | ||||
-rw-r--r-- | src/plugins/debugger/qml/qmlinspectoradapter.cpp | 39 | ||||
-rw-r--r-- | src/plugins/debugger/qml/qmlinspectoradapter.h | 10 | ||||
-rw-r--r-- | src/plugins/debugger/qml/qmlinspectoragent.cpp | 1 | ||||
-rw-r--r-- | src/plugins/debugger/qml/qmlv8debuggerclient.cpp | 1799 | ||||
-rw-r--r-- | src/plugins/debugger/qml/qmlv8debuggerclient.h | 130 | ||||
-rw-r--r-- | src/plugins/debugger/qml/qscriptdebuggerclient.cpp | 606 | ||||
-rw-r--r-- | src/plugins/debugger/qml/qscriptdebuggerclient.h | 99 |
15 files changed, 2215 insertions, 3770 deletions
diff --git a/src/plugins/debugger/qml/baseqmldebuggerclient.h b/src/plugins/debugger/qml/baseqmldebuggerclient.h deleted file mode 100644 index 47435fbf99..0000000000 --- a/src/plugins/debugger/qml/baseqmldebuggerclient.h +++ /dev/null @@ -1,111 +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 BASEQMLDEBUGGERCLIENT_H -#define BASEQMLDEBUGGERCLIENT_H - -#include <debugger/debuggerengine.h> -#include <qmldebug/qmldebugclient.h> - -namespace Debugger { -namespace Internal { - -class WatchData; -class WatchItem; -class BreakHandler; -class BreakpointModelId; -class QmlEngine; -class BaseQmlDebuggerClientPrivate; - -class BaseQmlDebuggerClient : public QmlDebug::QmlDebugClient -{ - Q_OBJECT - -public: - BaseQmlDebuggerClient(QmlDebug::QmlDebugConnection* client, QLatin1String clientName); - virtual ~BaseQmlDebuggerClient(); - - virtual void startSession() = 0; - virtual void endSession() = 0; - virtual void resetSession() = 0; - - virtual void executeStep() = 0; - virtual void executeStepOut() = 0; - virtual void executeNext() = 0; - virtual void executeStepI() = 0; - - virtual void executeRunToLine(const ContextData &data) = 0; - - virtual void continueInferior() = 0; - virtual void interruptInferior() = 0; - - virtual void activateFrame(int index) = 0; - - virtual bool acceptsBreakpoint(Breakpoint bp); - virtual void insertBreakpoint(Breakpoint bp, int adjustedLine, - int adjustedColumn = -1) = 0; - virtual void removeBreakpoint(Breakpoint bp) = 0; - virtual void changeBreakpoint(Breakpoint bp) = 0; - virtual void synchronizeBreakpoints() = 0; - - virtual void assignValueInDebugger(const WatchData *data, - const QString &expression, - const QVariant &valueV) = 0; - - virtual void updateWatchData(const WatchData &data) = 0; - virtual void executeDebuggerCommand(const QString &command) = 0; - - virtual void synchronizeWatchers(const QStringList &watchers) = 0; - - virtual void expandObject(const QByteArray &iname, quint64 objectId) = 0; - - virtual void setEngine(QmlEngine *engine) = 0; - - virtual void getSourceFiles() {} - - void flushSendBuffer(); - -signals: - void newState(QmlDebug::QmlDebugClient::State state); - void stackFrameCompleted(); - -protected: - virtual void stateChanged(State state); - void sendMessage(const QByteArray &msg); - -private: - BaseQmlDebuggerClientPrivate *d; - friend class BaseQmlDebuggerClientPrivate; -}; - -} // Internal -} // Debugger - -#endif // BASEQMLDEBUGGERCLIENT_H diff --git a/src/plugins/debugger/qml/qml.pri b/src/plugins/debugger/qml/qml.pri index 3dd714070d..18ed7fe1f5 100644 --- a/src/plugins/debugger/qml/qml.pri +++ b/src/plugins/debugger/qml/qml.pri @@ -1,10 +1,7 @@ HEADERS += \ $$PWD/qmlengine.h \ - $$PWD/qmladapter.h \ - $$PWD/baseqmldebuggerclient.h \ + $$PWD/qmlengineutils.h \ $$PWD/qmlcppengine.h \ - $$PWD/qscriptdebuggerclient.h \ - $$PWD/qmlv8debuggerclient.h \ $$PWD/interactiveinterpreter.h \ $$PWD/qmlv8debuggerclientconstants.h \ $$PWD/qmlinspectoragent.h \ @@ -12,11 +9,8 @@ HEADERS += \ SOURCES += \ $$PWD/qmlengine.cpp \ - $$PWD/qmladapter.cpp \ - $$PWD/baseqmldebuggerclient.cpp \ + $$PWD/qmlengineutils.cpp \ $$PWD/qmlcppengine.cpp \ - $$PWD/qscriptdebuggerclient.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 73f29d0400..0000000000 --- a/src/plugins/debugger/qml/qmladapter.cpp +++ /dev/null @@ -1,242 +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 "qscriptdebuggerclient.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<BaseQmlDebuggerClient *>(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() -{ - QScriptDebuggerClient *debugClient1 = new QScriptDebuggerClient(m_conn); - connect(debugClient1, &QScriptDebuggerClient::newState, - this, &QmlAdapter::clientStateChanged); - connect(debugClient1, &QScriptDebuggerClient::newState, - this, &QmlAdapter::debugClientStateChanged); - - QmlV8DebuggerClient *debugClient2 = new QmlV8DebuggerClient(m_conn); - connect(debugClient2, &QmlV8DebuggerClient::newState, - this, &QmlAdapter::clientStateChanged); - connect(debugClient2, &QmlV8DebuggerClient::newState, - this, &QmlAdapter::debugClientStateChanged); - - m_debugClients.insert(debugClient1->name(),debugClient1); - m_debugClients.insert(debugClient2->name(),debugClient2); - - debugClient1->setEngine((QmlEngine*)(m_engine.data())); - 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); -} - -BaseQmlDebuggerClient *QmlAdapter::activeDebuggerClient() const -{ - return m_qmlClient; -} - -QHash<QString, BaseQmlDebuggerClient*> 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 e9f3936878..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 BaseQmlDebuggerClient; -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; - - BaseQmlDebuggerClient *activeDebuggerClient() const; - QHash<QString, BaseQmlDebuggerClient*> 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; - BaseQmlDebuggerClient *m_qmlClient; - QTimer m_connectionTimer; - QmlDebug::QmlDebugConnection *m_conn; - QHash<QString, BaseQmlDebuggerClient*> 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 c2b2ca3246..46e2796446 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -29,46 +29,53 @@ ****************************************************************************/ #include "qmlengine.h" -#include "baseqmldebuggerclient.h" + +#include "interactiveinterpreter.h" +#include "qmlinspectoradapter.h" #include "qmlinspectoragent.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 <QTimer> -#define DEBUG_QML 1 +#define DEBUG_QML 0 #if DEBUG_QML # define SDEBUG(s) qDebug() << s #else @@ -76,185 +83,139 @@ #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 handleBacktrace(const QVariant &bodyVal, const QVariant &refsVal); + void handleLookup(const QVariant &bodyVal, const QVariant &refsVal); + void handleEvaluate(int sequence, bool success, const QVariant &bodyVal, const QVariant &refsVal); + void handleFrame(const QVariant &bodyVal, const QVariant &refsVal); + void handleScope(const QVariant &bodyVal, const QVariant &refsVal); + 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 +223,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()), + connect(stackHandler(), &StackHandler::stackChanged, + this, &QmlEngine::updateCurrentContext); + connect(stackHandler(), &StackHandler::currentIndexChanged, + this, &QmlEngine::updateCurrentContext); + connect(inspectorView(), SIGNAL(currentIndexChanged(QModelIndex)), SLOT(updateCurrentContext())); - connect(inspectorTreeView(), 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 +322,7 @@ void QmlEngine::setupInferior() notifyInferiorSetupOk(); - if (m_automaticConnect) + if (d->automaticConnect) beginConnection(); } @@ -373,7 +337,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 +347,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 +361,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 +393,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 +420,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 +432,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 +456,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 +470,15 @@ void QmlEngine::errorMessageBoxFinished(int result) } } -void QmlEngine::connectionError(QDebugSupport::Error error) -{ - 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*/) +void QmlEngine::filterApplicationMessage(const QString &output, int /*channel*/) const { - 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 +487,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 +512,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 +529,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 +555,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 +575,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 +599,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 +619,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 +639,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 +654,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 +663,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 +716,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 +742,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 +767,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 (BaseQmlDebuggerClient *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 ¶ms = 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 +810,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 (BaseQmlDebuggerClient *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 +830,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 ¶ms = 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 (BaseQmlDebuggerClient *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 +896,6 @@ void QmlEngine::attemptBreakpointSynchronization() } DebuggerEngine::attemptBreakpointSynchronization(); - - if (m_adapter.activeDebuggerClient()) { - m_adapter.activeDebuggerClient()->synchronizeBreakpoints(); - } else { - foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) { - client->synchronizeBreakpoints(); - } - } } bool QmlEngine::acceptsBreakpoint(Breakpoint bp) const @@ -937,10 +907,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 +928,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 +936,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 +944,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 +974,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 +988,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 (BaseQmlDebuggerClient *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 +1053,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 +1080,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 +1095,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 +1109,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 +1125,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 +1151,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 +1170,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 +1215,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 +1223,1446 @@ void QmlEngine::updateScriptSource(const QString &fileName, int lineOffset, int } } -void QmlEngine::updateDocument(Core::IDocument *document, const QTextDocument *textDocument) +bool QmlEnginePrivate::canEvaluateScript(const QString &script) +{ + 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) { - TextEditor::TextDocument *baseTextDocument - = qobject_cast<TextEditor::TextDocument *>(document); - if (!baseTextDocument) + // { "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(¶ms, 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; +} + +QJsonObject QmlEnginePrivate::initObject() +{ + return QJsonObject { + {_(SEQ), ++sequence}, + {_(TYPE), _(REQUEST)} + }; +} + +void QmlEnginePrivate::sendAndLogV8Request(const QJsonObject &request) +{ + 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) + handleBacktrace(resp.value(_(BODY)), resp.value(_(REFS))); + + } else if (debugCommand == _(LOOKUP)) { + if (success) + handleLookup(resp.value(_(BODY)), resp.value(_(REFS))); + + } else if (debugCommand == _(EVALUATE)) { + int seq = resp.value(_("request_seq")).toInt(); + if (success) { + handleEvaluate(seq, success, resp.value(_(BODY)), resp.value(_(REFS))); + } else { + QVariantMap map; + map.insert(_(TYPE), QVariant(_("string"))); + map.insert(_(VALUE), resp.value(_("message"))); + handleEvaluate(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) + handleFrame(resp.value(_(BODY)), resp.value(_(REFS))); + + } else if (debugCommand == _(SCOPE)) { + if (success) + handleScope(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 ¶ms = 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(); + + QStringList messages = highlightExceptionCode(lineNumber, filePath, errorMessage); + foreach (const QString msg, messages) + engine->showMessage(msg, ConsoleOutput); + + 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 + handleEvaluate(sequence, success, QVariant(map), QVariant()); + } + + } //EVENT + } //V8MESSAGE + + } else { + //DO NOTHING + } +} + +void QmlEnginePrivate::handleBacktrace(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 = 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. + handleFrame(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::handleFrame(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::handleScope(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; - baseTextDocument->document()->setPlainText(textDocument->toPlainText()); + 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 { + handlesToLookup << handle; + localsAndWatchers.insertMulti(handle, item->exp); + } + } + + if (!handlesToLookup.isEmpty()) + lookup(handlesToLookup); + else + engine->watchHandler()->notifyUpdateFinished(); } -bool QmlEngine::canEvaluateScript(const QString &script) +ConsoleItem *constructLogItemTree(ConsoleItem *parent, + const QmlV8ObjectData &objectData, + const QVariant &refsVal) { - m_interpreter.clearText(); - m_interpreter.appendText(script); - return m_interpreter.canEvaluate(); + 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; } -bool QmlEngine::adjustBreakpointLineAndColumn( - const QString &filePath, quint32 *line, quint32 *column, bool *valid) +static void insertSubItems(WatchItem *parent, const QVariantList &properties, 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); + 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 { - ASTWalker walker; - walker(doc->ast(), line, column); - *valid = walker.done; - success = true; + 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()); + parent->appendChild(item); } - return success; +} + +void QmlEnginePrivate::handleEvaluate(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."))) { + WatchItem *item = watchHandler->findItem(exp.toLatin1()); + insertSubItems(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); + insertSubItems(item, body.properties, refsVal); + } + //Insert the newly evaluated expression to the Watchers Window + } + } +} + +void QmlEnginePrivate::handleLookup(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. + WatchItem *parent = watchHandler->findItem(prepend); + insertSubItems(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::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 +2670,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..08670f46e9 --- /dev/null +++ b/src/plugins/debugger/qml/qmlengineutils.cpp @@ -0,0 +1,311 @@ +/**************************************************************************** +** +** 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> + +#include <QTextBlock> + +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); + } +} + +QStringList highlightExceptionCode(int lineNumber, const QString &filePath, const QString &errorMessage) +{ + QStringList messages; + 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); + + messages.append(QString::fromLatin1("%1: %2: %3").arg(filePath).arg(lineNumber).arg(errorMessage)); + } + return messages; +} + +} // Internal +} // Debugger diff --git a/src/plugins/debugger/qml/baseqmldebuggerclient.cpp b/src/plugins/debugger/qml/qmlengineutils.h index 5a7f9287a1..c3f8c62111 100644 --- a/src/plugins/debugger/qml/baseqmldebuggerclient.cpp +++ b/src/plugins/debugger/qml/qmlengineutils.h @@ -28,56 +28,22 @@ ** ****************************************************************************/ -#include "baseqmldebuggerclient.h" -#include <debugger/breakhandler.h> +#ifndef QMLENGINEUTILS_H +#define QMLENGINEUTILS_H -#include <utils/qtcassert.h> +#include <qmldebug/qdebugmessageclient.h> +#include <qmldebug/qmloutputparser.h> namespace Debugger { namespace Internal { -class BaseQmlDebuggerClientPrivate -{ -public: - QList<QByteArray> sendBuffer; -}; +bool adjustBreakpointLineAndColumn(const QString &filePath, quint32 *line, quint32 *column, bool *valid); +void appendDebugOutput(QtMsgType type, const QString &message, const QmlDebug::QDebugContextInfo &info); -BaseQmlDebuggerClient::BaseQmlDebuggerClient(QmlDebug::QmlDebugConnection* client, QLatin1String clientName) - : QmlDebugClient(clientName, client), - d(new BaseQmlDebuggerClientPrivate()) -{ -} +void clearExceptionSelection(); +QStringList highlightExceptionCode(int lineNumber, const QString &filePath, const QString &errorMessage); -BaseQmlDebuggerClient::~BaseQmlDebuggerClient() -{ - delete d; -} +} // namespace Internal +} // namespace Debugger -bool BaseQmlDebuggerClient::acceptsBreakpoint(Breakpoint /*bp*/) -{ - return false; -} - -void BaseQmlDebuggerClient::stateChanged(State state) -{ - emit newState(state); -} - -void BaseQmlDebuggerClient::sendMessage(const QByteArray &msg) -{ - if (state() == Enabled) - QmlDebugClient::sendMessage(msg); - else - d->sendBuffer.append(msg); -} - -void BaseQmlDebuggerClient::flushSendBuffer() -{ - QTC_ASSERT(state() == Enabled, return); - foreach (const QByteArray &msg, d->sendBuffer) - QmlDebugClient::sendMessage(msg); - d->sendBuffer.clear(); -} - -} // Internal -} // 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/qmlinspectoragent.cpp b/src/plugins/debugger/qml/qmlinspectoragent.cpp index 2a4dc61478..64bd8cb089 100644 --- a/src/plugins/debugger/qml/qmlinspectoragent.cpp +++ b/src/plugins/debugger/qml/qmlinspectoragent.cpp @@ -686,6 +686,7 @@ void QmlInspectorAgent::insertObjectInTree(const ObjectReference &object) m_objectToSelect = -1; } m_debuggerEngine->watchHandler()->updateWatchersWindow(); + m_debuggerEngine->watchHandler()->reexpandItems(); } void QmlInspectorAgent::buildDebugIdHashRecursive(const ObjectReference &ref) diff --git a/src/plugins/debugger/qml/qmlv8debuggerclient.cpp b/src/plugins/debugger/qml/qmlv8debuggerclient.cpp deleted file mode 100644 index c821da13a0..0000000000 --- a/src/plugins/debugger/qml/qmlv8debuggerclient.cpp +++ /dev/null @@ -1,1799 +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; -}; - -/////////////////////////////////////////////////////////////////////// -// -// 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(¶ms, 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) - : BaseQmlDebuggerClient(client, QLatin1String("V8Debugger")), - 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 ¶ms = 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 ¶ms = 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 ¶ms = 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 ¶ms = 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); - } - -} - -} // Internal -} // Debugger diff --git a/src/plugins/debugger/qml/qmlv8debuggerclient.h b/src/plugins/debugger/qml/qmlv8debuggerclient.h deleted file mode 100644 index ae78443e77..0000000000 --- a/src/plugins/debugger/qml/qmlv8debuggerclient.h +++ /dev/null @@ -1,130 +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 "baseqmldebuggerclient.h" - -namespace Debugger { -namespace Internal { - -class QmlV8DebuggerClientPrivate; - -class QmlV8DebuggerClient : public BaseQmlDebuggerClient -{ - 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(); - -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 diff --git a/src/plugins/debugger/qml/qscriptdebuggerclient.cpp b/src/plugins/debugger/qml/qscriptdebuggerclient.cpp deleted file mode 100644 index fd9b246557..0000000000 --- a/src/plugins/debugger/qml/qscriptdebuggerclient.cpp +++ /dev/null @@ -1,606 +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 "qscriptdebuggerclient.h" - -#include "qmlengine.h" -#include <debugger/watchhandler.h> -#include <debugger/breakhandler.h> -#include <debugger/stackhandler.h> -#include <debugger/debuggercore.h> -#include <debugger/debuggerstringutils.h> -#include <qmldebug/qmldebugclient.h> - -#include <coreplugin/messagebox.h> - -#include <QFileInfo> -#include <utils/qtcassert.h> - -using QmlDebug::QmlDebugStream; - -namespace Debugger { -namespace Internal { - -struct JSAgentBreakpointData -{ - QByteArray functionName; - QByteArray fileUrl; - qint32 lineNumber; -}; - -struct JSAgentStackData -{ - QByteArray functionName; - QByteArray fileUrl; - qint32 lineNumber; -}; - -uint qHash(const JSAgentBreakpointData &b) -{ - return b.lineNumber ^ qHash(b.fileUrl); -} - -QDataStream &operator<<(QDataStream &s, const JSAgentBreakpointData &data) -{ - return s << data.functionName << data.fileUrl << data.lineNumber; -} - -QDataStream &operator<<(QDataStream &s, const JSAgentStackData &data) -{ - return s << data.functionName << data.fileUrl << data.lineNumber; -} - -QDataStream &operator>>(QDataStream &s, JSAgentBreakpointData &data) -{ - return s >> data.functionName >> data.fileUrl >> data.lineNumber; -} - -QDataStream &operator>>(QDataStream &s, JSAgentStackData &data) -{ - return s >> data.functionName >> data.fileUrl >> data.lineNumber; -} - -bool operator==(const JSAgentBreakpointData &b1, const JSAgentBreakpointData &b2) -{ - return b1.lineNumber == b2.lineNumber && b1.fileUrl == b2.fileUrl; -} - -typedef QSet<JSAgentBreakpointData> JSAgentBreakpoints; -typedef QList<JSAgentStackData> JSAgentStackFrames; - - -static QDataStream &operator>>(QDataStream &s, WatchData &data) -{ - data = WatchData(); - QByteArray name; - QByteArray value; - QByteArray type; - bool hasChildren = false; - s >> data.exp >> name >> value >> type >> hasChildren >> data.id; - data.name = QString::fromUtf8(name); - data.setType(type, false); - data.setValue(QString::fromUtf8(value)); - data.setHasChildren(hasChildren); - data.setAllUnneeded(); - return s; -} - -class QScriptDebuggerClientPrivate -{ -public: - explicit QScriptDebuggerClientPrivate(QScriptDebuggerClient *) : - ping(0), sessionStarted(false), engine(0) - { - - } - - int ping; - bool sessionStarted; - QmlEngine *engine; - JSAgentBreakpoints breakpoints; - - void logSendMessage(const QString &msg) const; - void logReceiveMessage(const QString &msg) const; -}; - -QScriptDebuggerClient::QScriptDebuggerClient(QmlDebug::QmlDebugConnection* client) - : BaseQmlDebuggerClient(client, QLatin1String("JSDebugger")), - d(new QScriptDebuggerClientPrivate(this)) -{ -} - -QScriptDebuggerClient::~QScriptDebuggerClient() -{ - delete d; -} - -void QScriptDebuggerClient::executeStep() -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "STEPINTO"; - rs << cmd; - d->logSendMessage(QLatin1String(cmd)); - sendMessage(reply); -} - -void QScriptDebuggerClient::executeStepOut() -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "STEPOUT"; - rs << cmd; - d->logSendMessage(QLatin1String(cmd)); - sendMessage(reply); -} - -void QScriptDebuggerClient::executeNext() -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "STEPOVER"; - rs << cmd; - d->logSendMessage(QLatin1String(cmd)); - sendMessage(reply); -} - -void QScriptDebuggerClient::executeStepI() -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "STEPINTO"; - rs << cmd; - d->logSendMessage(QLatin1String(cmd)); - sendMessage(reply); -} - -void QScriptDebuggerClient::executeRunToLine(const ContextData &data) -{ - JSAgentBreakpointData bp; - bp.fileUrl = QUrl::fromLocalFile(data.fileName).toString().toUtf8(); - bp.lineNumber = data.lineNumber; - bp.functionName = "TEMPORARY"; - d->breakpoints.insert(bp); - synchronizeBreakpoints(); - continueInferior(); -} - -void QScriptDebuggerClient::continueInferior() -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "CONTINUE"; - rs << cmd; - d->logSendMessage(QLatin1String(cmd)); - sendMessage(reply); -} - -void QScriptDebuggerClient::interruptInferior() -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "INTERRUPT"; - rs << cmd; - d->logSendMessage(QLatin1String(cmd)); - sendMessage(reply); -} - -void QScriptDebuggerClient::startSession() -{ - //Flush buffered data - flushSendBuffer(); - - //Set all breakpoints - BreakHandler *handler = d->engine->breakHandler(); - DebuggerEngine * engine = d->engine->isSlaveEngine() ? - d->engine->masterEngine() : d->engine; - foreach (Breakpoint bp, handler->engineBreakpoints(engine)) { - QTC_CHECK(bp.state() == BreakpointInsertProceeding); - bp.notifyBreakpointInsertOk(); - } - d->sessionStarted = true; -} - -void QScriptDebuggerClient::endSession() -{ -} - -void QScriptDebuggerClient::resetSession() -{ - d->sessionStarted = false; -} - -void QScriptDebuggerClient::activateFrame(int index) -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "ACTIVATE_FRAME"; - rs << cmd - << index; - d->logSendMessage(QLatin1String(cmd) + QLatin1Char(' ') + QString::number(index)); - sendMessage(reply); -} - -void QScriptDebuggerClient::insertBreakpoint(Breakpoint bp, - int adjustedLine, - int /*adjustedColumn*/) -{ - JSAgentBreakpointData jsbp; - jsbp.fileUrl = QUrl::fromLocalFile(bp.fileName()).toString().toUtf8(); - jsbp.lineNumber = adjustedLine; - jsbp.functionName = bp.functionName().toUtf8(); - d->breakpoints.insert(jsbp); - - BreakpointResponse br = bp.response(); - br.lineNumber = adjustedLine; - bp.setResponse(br); - if (d->sessionStarted && bp.state() == BreakpointInsertProceeding) - bp.notifyBreakpointInsertOk(); -} - -void QScriptDebuggerClient::removeBreakpoint(Breakpoint bp) -{ - JSAgentBreakpointData jsbp; - jsbp.fileUrl = QUrl::fromLocalFile(bp.fileName()).toString().toUtf8(); - jsbp.lineNumber = bp.lineNumber(); - jsbp.functionName = bp.functionName().toUtf8(); - d->breakpoints.remove(jsbp); -} - -void QScriptDebuggerClient::changeBreakpoint(Breakpoint bp) -{ - if (bp.isEnabled()) - insertBreakpoint(bp, bp.response().lineNumber); - else - removeBreakpoint(bp); - - BreakpointResponse br = bp.response(); - br.enabled = bp.isEnabled(); - bp.setResponse(br); -} - -void QScriptDebuggerClient::synchronizeBreakpoints() -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "BREAKPOINTS"; - rs << cmd - << d->breakpoints; - - QString logBreakpoints; - QTextStream str(&logBreakpoints); - str << cmd << " ("; - bool first = true; - foreach (const JSAgentBreakpointData &bp, d->breakpoints) { - if (first) - first = false; - else - str << ", "; - str << '[' << bp.functionName << ", " << bp.fileUrl << ", " << bp.lineNumber << ']'; - } - str << ')'; - d->logSendMessage(logBreakpoints); - - sendMessage(reply); -} - -void QScriptDebuggerClient::assignValueInDebugger(const WatchData *data, - const QString &expr, - const QVariant &valueV) -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "EXEC"; - rs << cmd; - QString expression = QString(_("%1 = %2;")).arg(expr).arg(valueV.toString()); - rs << data->iname << expression; - d->logSendMessage(QString::fromLatin1("%1 %2 %3 %4"). - arg(QLatin1String(cmd), QLatin1String(data->iname), expr, - valueV.toString())); - sendMessage(reply); -} - -void QScriptDebuggerClient::updateWatchData(const WatchData &data) -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "EXEC"; - rs << cmd; - rs << data.iname << data.name; - d->logSendMessage(QLatin1String(cmd) + QLatin1Char(' ') + QLatin1String(data.iname) - + QLatin1Char(' ') + data.name); - sendMessage(reply); -} - -void QScriptDebuggerClient::executeDebuggerCommand(const QString &command) -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "EXEC"; - QByteArray console = "console"; - rs << cmd << console << command; - d->logSendMessage(QLatin1String(cmd) + QLatin1Char(' ') + QLatin1String(console) - + QLatin1Char(' ') + command); - sendMessage(reply); -} - -void QScriptDebuggerClient::synchronizeWatchers(const QStringList &watchers) -{ - // send watchers list - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "WATCH_EXPRESSIONS"; - rs << cmd; - d->logSendMessage(QString::fromLatin1("%1 (%2)").arg(QLatin1String(cmd), - watchers.join(QLatin1String(", ")))); - sendMessage(reply); -} - -void QScriptDebuggerClient::expandObject(const QByteArray &iname, quint64 objectId) -{ - //Check if id is valid - if (qint64(objectId) == -1) - return; - - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "EXPAND"; - rs << cmd; - rs << iname << objectId; - d->logSendMessage(QLatin1String(cmd) + QLatin1Char(' ') + QLatin1String(iname) - + QString::number(objectId)); - sendMessage(reply); -} - -void QScriptDebuggerClient::sendPing() -{ - d->ping++; - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "PING"; - rs << cmd; - rs << d->ping; - d->logSendMessage(QLatin1String(cmd)); - sendMessage(reply); -} - -void QScriptDebuggerClient::messageReceived(const QByteArray &data) -{ - QByteArray rwData = data; - QmlDebugStream stream(&rwData, QIODevice::ReadOnly); - - QByteArray command; - stream >> command; - - WatchHandler *watchHandler = d->engine->watchHandler(); - StackHandler *stackHandler = d->engine->stackHandler(); - - if (command == "STOPPED") { - d->engine->inferiorSpontaneousStop(); - - QString logString = QString::fromLatin1(command); - - JSAgentStackFrames stackFrames; - QList<WatchData> watches; - QList<WatchData> locals; - stream >> stackFrames >> watches >> locals; - - logString += QString::fromLatin1(" (%1 stack frames) (%2 watches) (%3 locals)"). - arg(stackFrames.size()).arg(watches.size()).arg(locals.size()); - - StackFrames ideStackFrames; - for (int i = 0; i != stackFrames.size(); ++i) { - StackFrame frame; - frame.line = stackFrames.at(i).lineNumber; - frame.function = QLatin1String(stackFrames.at(i).functionName); - frame.file = d->engine->toFileInProject(QUrl(QLatin1String(stackFrames.at(i).fileUrl))); - frame.usable = QFileInfo(frame.file).isReadable(); - frame.level = i + 1; - ideStackFrames << frame; - } - - stackHandler->setFrames(ideStackFrames); - - bool becauseOfException; - stream >> becauseOfException; - - logString += becauseOfException ? QLatin1String(" exception") : QLatin1String(" no_exception"); - - if (becauseOfException) { - QString error; - stream >> error; - - logString += QLatin1Char(' '); - logString += error; - d->logReceiveMessage(logString); - - QString msg = stackFrames.isEmpty() - ? tr("<p>An uncaught exception occurred:</p><p>%1</p>") - .arg(error.toHtmlEscaped()) - : tr("<p>An uncaught exception occurred in \"%1\":</p><p>%2</p>") - .arg(QLatin1String(stackFrames.value(0).fileUrl), error.toHtmlEscaped()); - Core::AsynchronousMessageBox::information(tr("Uncaught Exception"), msg); - } else { - QString file; - int line = -1; - - if (!ideStackFrames.isEmpty()) { - file = ideStackFrames.at(0).file; - line = ideStackFrames.at(0).line; - } - - QList<JSAgentBreakpointData> breakpoints(d->breakpoints.toList()); - foreach (const JSAgentBreakpointData &data, breakpoints) { - if (data.fileUrl == QUrl::fromLocalFile(file).toString().toUtf8() && - data.lineNumber == line && - data.functionName == "TEMPORARY") { - breakpoints.removeOne(data); - d->breakpoints = JSAgentBreakpoints::fromList(breakpoints); - synchronizeBreakpoints(); - break; - } - } - - d->logReceiveMessage(logString); - } - - if (!ideStackFrames.isEmpty()) - d->engine->gotoLocation(ideStackFrames.value(0)); - - insertLocalsAndWatches(locals, watches, stackHandler->currentIndex()); - - } else if (command == "RESULT") { - WatchData data; - QByteArray iname; - stream >> iname >> data; - - d->logReceiveMessage(QLatin1String(command) + QLatin1Char(' ') - + QLatin1String(iname) + QLatin1Char(' ') + data.value); - - auto item = new WatchItem(data); - item->iname = iname; - if (iname.startsWith("watch.")) { - watchHandler->insertItem(item); - } else if (iname == "console") { - d->engine->showMessage(item->value, ConsoleOutput); - } else if (iname.startsWith("local.")) { - item->name = item->name.left(item->name.indexOf(QLatin1Char(' '))); - watchHandler->insertItem(item); - } else { - qWarning() << "QmlEngine: Unexcpected result: " << iname << item->value; - } - } else if (command == "EXPANDED") { - QList<WatchData> result; - QByteArray iname; - stream >> iname >> result; - d->logReceiveMessage(QString::fromLatin1("%1 %2 (%3 x watchdata)"). - arg(QLatin1String(command), QLatin1String(iname), - QString::number(result.size()))); - bool needPing = false; - - foreach (WatchData data, result) { - data.iname = iname + '.' + data.exp; - watchHandler->insertItem(new WatchItem(data)); - - if (watchHandler->isExpandedIName(data.iname) && qint64(data.id) != -1) { - needPing = true; - expandObject(data.iname, data.id); - } - } - if (needPing) - sendPing(); - } else if (command == "LOCALS") { - QList<WatchData> locals; - QList<WatchData> watches; - int frameId; - stream >> frameId >> locals; - if (!stream.atEnd()) { // compatibility with jsdebuggeragent from 2.1, 2.2 - stream >> watches; - } - - d->logReceiveMessage(QString::fromLatin1("%1 %2 (%3 x locals) (%4 x watchdata)").arg( - QLatin1String(command), QString::number(frameId), - QString::number(locals.size()), QString::number(watches.size()))); - - insertLocalsAndWatches(locals, watches, frameId); - - } else if (command == "PONG") { - int ping; - stream >> ping; - d->logReceiveMessage(QLatin1String(command) + QLatin1Char(' ') + QString::number(ping)); - } else { - qDebug() << Q_FUNC_INFO << "Unknown command: " << command; - d->logReceiveMessage(QLatin1String(command) + QLatin1String(" UNKNOWN COMMAND!!")); - } - -} - -void QScriptDebuggerClient::insertLocalsAndWatches(QList<WatchData> &locals, - QList<WatchData> &watches, - int stackFrameIndex) -{ - WatchHandler *watchHandler = d->engine->watchHandler(); - watchHandler->removeAllData(); - if (stackFrameIndex < 0) - return; - const StackFrame frame = d->engine->stackHandler()->frameAt(stackFrameIndex); - if (!frame.isUsable()) - return; - - bool needPing = false; - foreach (const WatchData &data, watches) { - auto item = new WatchItem(data); - item->iname = watchHandler->watcherName(data.exp); - watchHandler->insertItem(item); - - if (watchHandler->isExpandedIName(data.iname) && qint64(data.id) != -1) { - needPing = true; - expandObject(data.iname, data.id); - } - } - - foreach (const WatchData &data, locals) { - auto item = new WatchItem(data); - if (item->name == QLatin1String("<no initialized data>")) - item->name = tr("No Local Variables"); - item->iname = "local." + item->exp; - watchHandler->insertItem(item); - - if (watchHandler->isExpandedIName(data.iname) && qint64(data.id) != -1) { - needPing = true; - expandObject(data.iname, data.id); - } - } - - if (needPing) - sendPing(); - emit stackFrameCompleted(); -} - -void QScriptDebuggerClient::setEngine(QmlEngine *engine) -{ - d->engine = engine; - connect(this, &QScriptDebuggerClient::stackFrameCompleted, - engine, &DebuggerEngine::stackFrameCompleted); -} - -void QScriptDebuggerClientPrivate::logSendMessage(const QString &msg) const -{ - if (engine) - engine->logMessage(QLatin1String("QScriptDebuggerClient"), QmlEngine::LogSend, msg); -} - -void QScriptDebuggerClientPrivate::logReceiveMessage(const QString &msg) const -{ - if (engine) - engine->logMessage(QLatin1String("QScriptDebuggerClient"), QmlEngine::LogReceive, msg); -} - -} // Internal -} // Debugger diff --git a/src/plugins/debugger/qml/qscriptdebuggerclient.h b/src/plugins/debugger/qml/qscriptdebuggerclient.h deleted file mode 100644 index a09a1839c2..0000000000 --- a/src/plugins/debugger/qml/qscriptdebuggerclient.h +++ /dev/null @@ -1,99 +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 QSCRIPTDEBUGGERCLIENT_H -#define QSCRIPTDEBUGGERCLIENT_H - -#include "baseqmldebuggerclient.h" - -namespace Debugger { -namespace Internal { - -class QScriptDebuggerClientPrivate; - -class QScriptDebuggerClient : public BaseQmlDebuggerClient -{ - Q_OBJECT - -public: - QScriptDebuggerClient(QmlDebug::QmlDebugConnection *client); - ~QScriptDebuggerClient(); - - 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); - - 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 &data); - void executeDebuggerCommand(const QString &command); - - void synchronizeWatchers(const QStringList &watchers); - - void expandObject(const QByteArray &iname, quint64 objectId); - - void setEngine(QmlEngine *engine); - -protected: - void messageReceived(const QByteArray &data); - -private: - void sendPing(); - void insertLocalsAndWatches(QList<WatchData> &locals, QList<WatchData> &watches, - int stackFrameIndex); - -private: - QScriptDebuggerClientPrivate *d; - friend class QScriptDebuggerClientPrivate; -}; - -} // Internal -} // Debugger - -#endif // QSCRIPTDEBUGGERCLIENT_H |