summaryrefslogtreecommitdiff
path: root/src/plugins/debugger/qml/qscriptdebuggerclient.cpp
diff options
context:
space:
mode:
authorAurindam Jana <aurindam.jana@nokia.com>2011-07-26 16:22:49 +0200
committerAurindam Jana <aurindam.jana@nokia.com>2011-08-03 11:27:38 +0200
commitab574ba88dcbc15538e58a6d46185a9b032fe7f9 (patch)
tree8e09146112f00d4491096bdfec9bb10966a8efdf /src/plugins/debugger/qml/qscriptdebuggerclient.cpp
parent999265105bd30b7b30e6688fec35b1448eb276be (diff)
downloadqt-creator-ab574ba88dcbc15538e58a6d46185a9b032fe7f9.tar.gz
Delegate javascript debugging to Script and V8 debugger clients.
The appropriate client handles the debugging based on the service available at the server side. Change-Id: I46b66036f700fc7e45e8b38cef7f1ce1445b1122 Reviewed-on: http://codereview.qt.nokia.com/2497 Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com> Reviewed-by: Kai Koehne <kai.koehne@nokia.com>
Diffstat (limited to 'src/plugins/debugger/qml/qscriptdebuggerclient.cpp')
-rw-r--r--src/plugins/debugger/qml/qscriptdebuggerclient.cpp511
1 files changed, 511 insertions, 0 deletions
diff --git a/src/plugins/debugger/qml/qscriptdebuggerclient.cpp b/src/plugins/debugger/qml/qscriptdebuggerclient.cpp
new file mode 100644
index 0000000000..51cd2cf46f
--- /dev/null
+++ b/src/plugins/debugger/qml/qscriptdebuggerclient.cpp
@@ -0,0 +1,511 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
+**
+**************************************************************************/
+#include "qscriptdebuggerclient.h"
+
+#include "watchdata.h"
+#include "watchhandler.h"
+#include "breakpoint.h"
+#include "breakhandler.h"
+#include "debuggerconstants.h"
+#include "qmlengine.h"
+#include "stackhandler.h"
+#include "debuggercore.h"
+
+#include <QTextDocument>
+#include <QFileInfo>
+#include <QMessageBox>
+#include <extensionsystem/pluginmanager.h>
+#include <utils/qtcassert.h>
+
+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), engine(0)
+ {
+
+ }
+
+ int ping;
+ QmlEngine *engine;
+ JSAgentBreakpoints breakpoints;
+};
+
+QScriptDebuggerClient::QScriptDebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection* client)
+ : QmlDebuggerClient(client, QLatin1String("JSDebugger")),
+ d(new QScriptDebuggerClientPrivate(this))
+{
+}
+
+QScriptDebuggerClient::~QScriptDebuggerClient()
+{
+ delete d;
+}
+
+void QScriptDebuggerClient::executeStep()
+{
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ QByteArray cmd = "STEPINTO";
+ rs << cmd;
+ sendMessage(reply);
+}
+
+void QScriptDebuggerClient::executeStepOut()
+{
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ QByteArray cmd = "STEPOUT";
+ rs << cmd;
+ sendMessage(reply);
+}
+
+void QScriptDebuggerClient::executeNext()
+{
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ QByteArray cmd = "STEPOVER";
+ rs << cmd;
+ sendMessage(reply);
+}
+
+void QScriptDebuggerClient::executeStepI()
+{
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ QByteArray cmd = "STEPINTO";
+ rs << cmd;
+ sendMessage(reply);
+}
+
+void QScriptDebuggerClient::continueInferior()
+{
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ QByteArray cmd = "CONTINUE";
+ rs << cmd;
+ sendMessage(reply);
+}
+
+void QScriptDebuggerClient::interruptInferior()
+{
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ QByteArray cmd = "INTERRUPT";
+ rs << cmd;
+ sendMessage(reply);
+}
+
+void QScriptDebuggerClient::activateFrame(int index)
+{
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ QByteArray cmd = "ACTIVATE_FRAME";
+ rs << cmd
+ << index;
+ sendMessage(reply);
+}
+
+void QScriptDebuggerClient::insertBreakpoints(BreakHandler *handler, BreakpointModelId *id)
+{
+ JSAgentBreakpointData bp;
+ bp.fileUrl = QUrl::fromLocalFile(handler->fileName(*id)).toString().toUtf8();
+ bp.lineNumber = handler->lineNumber(*id);
+ bp.functionName = handler->functionName(*id).toUtf8();
+ d->breakpoints.insert(bp);
+}
+
+void QScriptDebuggerClient::removeBreakpoints(BreakpointModelId */*id*/)
+{
+
+}
+
+void QScriptDebuggerClient::setBreakpoints()
+{
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ QByteArray cmd = "BREAKPOINTS";
+ rs << cmd
+ << d->breakpoints;
+
+ QStringList breakPointsStr;
+ foreach (const JSAgentBreakpointData &bp, d->breakpoints) {
+ breakPointsStr << QString("('%1' '%2' %3)").arg(QString(bp.functionName),
+ QString(bp.fileUrl), QString::number(bp.lineNumber));
+ }
+
+ sendMessage(reply);
+
+ d->breakpoints.clear();
+}
+
+void QScriptDebuggerClient::assignValueInDebugger(const QByteArray expr, const quint64 &id,
+ const QString &property, const QString value)
+{
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ QByteArray cmd = "SET_PROPERTY";
+ rs << cmd;
+ rs << expr << id << property << value;
+ sendMessage(reply);
+}
+
+void QScriptDebuggerClient::updateWatchData(const WatchData *data)
+{
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ QByteArray cmd = "EXEC";
+ rs << cmd;
+ rs << data->iname << data->name;
+ sendMessage(reply);
+}
+
+void QScriptDebuggerClient::executeDebuggerCommand(const QString &command)
+{
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ QByteArray cmd = "EXEC";
+ QByteArray console = "console";
+ rs << cmd << console << command;
+ sendMessage(reply);
+}
+
+void QScriptDebuggerClient::synchronizeWatchers(const QStringList &watchers)
+{
+ // send watchers list
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ QByteArray cmd = "WATCH_EXPRESSIONS";
+ rs << cmd;
+ rs << watchers;
+ sendMessage(reply);
+}
+
+void QScriptDebuggerClient::expandObject(const QByteArray &iname, quint64 objectId)
+{
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ QByteArray cmd = "EXPAND";
+ rs << cmd;
+ rs << iname << objectId;
+ sendMessage(reply);
+}
+
+void QScriptDebuggerClient::sendPing()
+{
+ d->ping++;
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ QByteArray cmd = "PING";
+ rs << cmd;
+ rs << d->ping;
+ sendMessage(reply);
+}
+
+void QScriptDebuggerClient::messageReceived(const QByteArray &data)
+{
+ QByteArray rwData = data;
+ QDataStream stream(&rwData, QIODevice::ReadOnly);
+
+ QByteArray command;
+ stream >> command;
+
+ 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 = stackFrames.at(i).functionName;
+ frame.file = d->engine->toFileInProject(QUrl(stackFrames.at(i).fileUrl));
+ frame.usable = QFileInfo(frame.file).isReadable();
+ frame.level = i + 1;
+ ideStackFrames << frame;
+ }
+
+ if (ideStackFrames.size() && ideStackFrames.back().function == "<global>")
+ ideStackFrames.takeLast();
+ d->engine->stackHandler()->setFrames(ideStackFrames);
+
+ d->engine->watchHandler()->beginCycle();
+ bool needPing = false;
+
+ foreach (WatchData data, watches) {
+ data.iname = d->engine->watchHandler()->watcherName(data.exp);
+ d->engine->watchHandler()->insertData(data);
+
+ if (d->engine->watchHandler()->expandedINames().contains(data.iname)) {
+ needPing = true;
+ expandObject(data.iname,data.id);
+ }
+ }
+
+ foreach (WatchData data, locals) {
+ data.iname = "local." + data.exp;
+ d->engine->watchHandler()->insertData(data);
+
+ if (d->engine->watchHandler()->expandedINames().contains(data.iname)) {
+ needPing = true;
+ expandObject(data.iname,data.id);
+ }
+ }
+
+ if (needPing) {
+ sendPing();
+ } else {
+ d->engine->watchHandler()->endCycle();
+ }
+
+ bool becauseOfException;
+ stream >> becauseOfException;
+
+ logString += becauseOfException ? " exception" : " no_exception";
+
+ if (becauseOfException) {
+ QString error;
+ stream >> error;
+
+ logString += QLatin1Char(' ');
+ logString += error;
+ d->engine->logMessage(QmlEngine::LogReceive, logString);
+
+ QString msg = stackFrames.isEmpty()
+ ? tr("<p>An uncaught exception occurred:</p><p>%1</p>")
+ .arg(Qt::escape(error))
+ : tr("<p>An uncaught exception occurred in <i>%1</i>:</p><p>%2</p>")
+ .arg(stackFrames.value(0).fileUrl, Qt::escape(error));
+ showMessageBox(QMessageBox::Information, tr("Uncaught Exception"), msg);
+ } else {
+ //
+ // Make breakpoint non-pending
+ //
+ QString file;
+ QString function;
+ int line = -1;
+
+ if (!ideStackFrames.isEmpty()) {
+ file = ideStackFrames.at(0).file;
+ line = ideStackFrames.at(0).line;
+ function = ideStackFrames.at(0).function;
+ }
+
+ BreakHandler *handler = d->engine->breakHandler();
+ foreach (BreakpointModelId id, handler->engineBreakpointIds(d->engine)) {
+ QString processedFilename = handler->fileName(id);
+ if (processedFilename == file && handler->lineNumber(id) == line) {
+ QTC_ASSERT(handler->state(id) == BreakpointInserted,/**/);
+ BreakpointResponse br = handler->response(id);
+ br.fileName = file;
+ br.lineNumber = line;
+ br.functionName = function;
+ handler->setResponse(id, br);
+ }
+ }
+
+ d->engine->logMessage(QmlEngine::LogReceive, logString);
+ }
+
+ if (!ideStackFrames.isEmpty())
+ d->engine->gotoLocation(ideStackFrames.value(0));
+
+ } else if (command == "RESULT") {
+ WatchData data;
+ QByteArray iname;
+ stream >> iname >> data;
+
+ d->engine->logMessage(QmlEngine::LogReceive, QString("%1 %2 %3").arg(QString(command),
+ QString(iname), QString(data.value)));
+ data.iname = iname;
+ if (iname.startsWith("watch.")) {
+ d->engine->watchHandler()->insertData(data);
+ } else if (iname == "console") {
+ d->engine->showMessage(data.value, ScriptConsoleOutput);
+ } else {
+ qWarning() << "QmlEngine: Unexcpected result: " << iname << data.value;
+ }
+ } else if (command == "EXPANDED") {
+ QList<WatchData> result;
+ QByteArray iname;
+ stream >> iname >> result;
+
+ d->engine->logMessage(QmlEngine::LogReceive, QString("%1 %2 (%3 x watchdata)").arg(
+ QString(command), QString(iname), QString::number(result.size())));
+ bool needPing = false;
+ foreach (WatchData data, result) {
+ data.iname = iname + '.' + data.exp;
+ d->engine->watchHandler()->insertData(data);
+
+ if (d->engine->watchHandler()->expandedINames().contains(data.iname)) {
+ 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->engine->logMessage(QmlEngine::LogReceive, QString("%1 %2 (%3 x locals) (%4 x watchdata)").arg(
+ QString(command), QString::number(frameId),
+ QString::number(locals.size()),
+ QString::number(watches.size())));
+ d->engine->watchHandler()->beginCycle();
+ bool needPing = false;
+ foreach (WatchData data, watches) {
+ data.iname = d->engine->watchHandler()->watcherName(data.exp);
+ d->engine->watchHandler()->insertData(data);
+
+ if (d->engine->watchHandler()->expandedINames().contains(data.iname)) {
+ needPing = true;
+ expandObject(data.iname, data.id);
+ }
+ }
+
+ foreach (WatchData data, locals) {
+ data.iname = "local." + data.exp;
+ d->engine->watchHandler()->insertData(data);
+ if (d->engine->watchHandler()->expandedINames().contains(data.iname)) {
+ needPing = true;
+ expandObject(data.iname, data.id);
+ }
+ }
+ if (needPing)
+ sendPing();
+ else
+ d->engine->watchHandler()->endCycle();
+
+ } else if (command == "PONG") {
+ int ping;
+ stream >> ping;
+
+ d->engine->logMessage(QmlEngine::LogReceive, QString("%1 %2").arg(QString(command), QString::number(ping)));
+
+ if (ping == d->ping)
+ d->engine->watchHandler()->endCycle();
+ } else {
+ qDebug() << Q_FUNC_INFO << "Unknown command: " << command;
+ d->engine->logMessage(QmlEngine::LogReceive, QString("%1 UNKNOWN COMMAND!!").arg(QString(command)));
+ }
+
+}
+
+void QScriptDebuggerClient::setEngine(QmlEngine *engine)
+{
+ d->engine = engine;
+}
+
+} // Internal
+} // Debugger