Skip to content
Snippets Groups Projects
Commit 06723f8d authored by Aurindam Jana's avatar Aurindam Jana
Browse files

JSDebugger: Break on Exception

The debugger breaks on Javascript exception. The error message is
printed on the ScriptConsole and the relevant code is marked with
a wavy underline.

Change-Id: I5e6f603430c3b8a0db450d1e8c821714ec0140ab
Reviewed-on: http://codereview.qt-project.org/4276


Reviewed-by: default avatarQt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: default avatarLeandro T. C. Melo <leandro.melo@nokia.com>
Reviewed-by: default avatarKai Koehne <kai.koehne@nokia.com>
parent 57ab1cb2
No related branches found
No related tags found
No related merge requests found
...@@ -44,6 +44,10 @@ ...@@ -44,6 +44,10 @@
#include <extensionsystem/pluginmanager.h> #include <extensionsystem/pluginmanager.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <coreplugin/editormanager/editormanager.h>
#include <texteditor/basetexteditor.h>
#include <QtGui/QTextBlock>
#include <QtCore/QVariant> #include <QtCore/QVariant>
#include <QtCore/QFileInfo> #include <QtCore/QFileInfo>
#include <QtGui/QTextDocument> #include <QtGui/QTextDocument>
...@@ -51,20 +55,31 @@ ...@@ -51,20 +55,31 @@
#define INITIALPARAMS "seq" << ':' << ++d->sequence << ',' << "type" << ':' << "request" #define INITIALPARAMS "seq" << ':' << ++d->sequence << ',' << "type" << ':' << "request"
using namespace Core;
using namespace Json; using namespace Json;
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
struct ExceptionInfo
{
int sourceLine;
QString filePath;
QString errorMessage;
};
class QmlV8DebuggerClientPrivate class QmlV8DebuggerClientPrivate
{ {
public: public:
explicit QmlV8DebuggerClientPrivate(QmlV8DebuggerClient *) : explicit QmlV8DebuggerClientPrivate(QmlV8DebuggerClient *) :
sequence(0), ping(0), engine(0) handleException(false),
sequence(0),
ping(0),
engine(0)
{ {
} }
bool handleException;
int sequence; int sequence;
int ping; int ping;
QmlEngine *engine; QmlEngine *engine;
...@@ -73,6 +88,7 @@ public: ...@@ -73,6 +88,7 @@ public:
QHash<int,QByteArray> locals; QHash<int,QByteArray> locals;
QHash<int,QByteArray> watches; QHash<int,QByteArray> watches;
QByteArray frames; QByteArray frames;
QScopedPointer<ExceptionInfo> exceptionInfo;
}; };
QmlV8DebuggerClient::QmlV8DebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection* client) QmlV8DebuggerClient::QmlV8DebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection* client)
...@@ -86,7 +102,7 @@ QmlV8DebuggerClient::~QmlV8DebuggerClient() ...@@ -86,7 +102,7 @@ QmlV8DebuggerClient::~QmlV8DebuggerClient()
delete d; delete d;
} }
QByteArray QmlV8DebuggerClient::packMessage(QByteArray& message) QByteArray QmlV8DebuggerClient::packMessage(const QByteArray &message)
{ {
QByteArray reply; QByteArray reply;
QDataStream rs(&reply, QIODevice::WriteOnly); QDataStream rs(&reply, QIODevice::WriteOnly);
...@@ -95,15 +111,21 @@ QByteArray QmlV8DebuggerClient::packMessage(QByteArray& message) ...@@ -95,15 +111,21 @@ QByteArray QmlV8DebuggerClient::packMessage(QByteArray& message)
return reply; return reply;
} }
void QmlV8DebuggerClient::executeStep() void QmlV8DebuggerClient::breakOnException(Exceptions exceptionsType, bool enabled)
{ {
//TODO: Have to deal with NoExceptions
QByteArray request; QByteArray request;
JsonInputStream(request) << '{' << INITIALPARAMS ; JsonInputStream(request) << '{' << INITIALPARAMS ;
JsonInputStream(request) << ',' << "command" << ':' << "continue"; JsonInputStream(request) << ',' << "command" << ':' << "setexceptionbreak";
JsonInputStream(request) << ',' << "arguments" << ':'; JsonInputStream(request) << ',' << "arguments" << ':';
JsonInputStream(request) << '{' << "stepaction" << ':' << "in"; if (exceptionsType == AllExceptions)
JsonInputStream(request) << '{' << "type" << ':' << "all";
else if (exceptionsType == UncaughtExceptions)
JsonInputStream(request) << '{' << "type" << ':' << "uncaught";
JsonInputStream(request) << ',' << "enabled" << ':' << enabled;
JsonInputStream(request) << '}'; JsonInputStream(request) << '}';
JsonInputStream(request) << '}'; JsonInputStream(request) << '}';
...@@ -112,71 +134,134 @@ void QmlV8DebuggerClient::executeStep() ...@@ -112,71 +134,134 @@ void QmlV8DebuggerClient::executeStep()
sendMessage(packMessage(request)); sendMessage(packMessage(request));
} }
void QmlV8DebuggerClient::executeStepOut() void QmlV8DebuggerClient::storeExceptionInformation(const QByteArray &message)
{ {
QByteArray request; JsonValue response(message);
JsonInputStream(request) << '{' << INITIALPARAMS ; JsonValue body = response.findChild("body");
JsonInputStream(request) << ',' << "command" << ':' << "continue";
JsonInputStream(request) << ',' << "arguments" << ':'; d->exceptionInfo.reset(new ExceptionInfo);
JsonInputStream(request) << '{' << "stepaction" << ':' << "out"; d->exceptionInfo->sourceLine = body.findChild("sourceLine").toVariant().toInt();
JsonInputStream(request) << '}'; QUrl fileUrl(body.findChild("script").findChild("name").toVariant().toString());
d->exceptionInfo->filePath = d->engine->toFileInProject(fileUrl);
d->exceptionInfo->errorMessage = body.findChild("exception").findChild("text").toVariant().toString();
}
JsonInputStream(request) << '}'; void QmlV8DebuggerClient::handleException()
{
EditorManager *editorManager = EditorManager::instance();
QList<IEditor *> openedEditors = editorManager->openedEditors();
// set up the format for the errors
QTextCharFormat errorFormat;
errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
errorFormat.setUnderlineColor(Qt::red);
sendMessage(packMessage(request)); foreach (IEditor *editor, openedEditors) {
if (editor->file()->fileName() == d->exceptionInfo->filePath) {
TextEditor::BaseTextEditorWidget *ed = qobject_cast<TextEditor::BaseTextEditorWidget *>(editor->widget());
if (!ed)
continue;
} QList<QTextEdit::ExtraSelection> selections;
QTextEdit::ExtraSelection sel;
sel.format = errorFormat;
QTextCursor c(ed->document()->findBlockByNumber(d->exceptionInfo->sourceLine));
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;
void QmlV8DebuggerClient::executeNext() sel.format.setToolTip(d->exceptionInfo->errorMessage);
{
QByteArray request;
JsonInputStream(request) << '{' << INITIALPARAMS ; selections.append(sel);
JsonInputStream(request) << ',' << "command" << ':' << "continue"; ed->setExtraSelections(TextEditor::BaseTextEditorWidget::DebuggerExceptionSelection, selections);
JsonInputStream(request) << ',' << "arguments" << ':'; d->engine->showMessage(d->exceptionInfo->errorMessage, ScriptConsoleOutput);
JsonInputStream(request) << '{' << "stepaction" << ':' << "next"; }
JsonInputStream(request) << '}'; }
JsonInputStream(request) << '}'; //Delete the info even if the code hasnt been highlighted
d->exceptionInfo.reset();
}
void QmlV8DebuggerClient::clearExceptionSelection()
{
//Check if break was due to exception
if (d->handleException) {
EditorManager *editorManager = EditorManager::instance();
QList<IEditor *> openedEditors = editorManager->openedEditors();
QList<QTextEdit::ExtraSelection> selections;
sendMessage(packMessage(request)); foreach (IEditor *editor, openedEditors) {
TextEditor::BaseTextEditorWidget *ed = qobject_cast<TextEditor::BaseTextEditorWidget *>(editor->widget());
if (!ed)
continue;
ed->setExtraSelections(TextEditor::BaseTextEditorWidget::DebuggerExceptionSelection, selections);
}
d->handleException = false;
}
} }
void QmlV8DebuggerClient::executeStepI() void QmlV8DebuggerClient::continueDebugging(StepAction type)
{ {
clearExceptionSelection();
QByteArray request; QByteArray request;
JsonInputStream(request) << '{' << INITIALPARAMS ; JsonInputStream(request) << '{' << INITIALPARAMS ;
JsonInputStream(request) << ',' << "command" << ':' << "continue"; JsonInputStream(request) << ',' << "command" << ':' << "continue";
JsonInputStream(request) << ',' << "arguments" << ':'; if (type != Continue) {
JsonInputStream(request) << '{' << "stepaction" << ':' << "in"; JsonInputStream(request) << ',' << "arguments" << ':';
JsonInputStream(request) << '}';
switch (type) {
case In: JsonInputStream(request) << '{' << "stepaction" << ':' << "in";
break;
case Out: JsonInputStream(request) << '{' << "stepaction" << ':' << "out";
break;
case Next: JsonInputStream(request) << '{' << "stepaction" << ':' << "next";
break;
default:break;
}
JsonInputStream(request) << '}'; JsonInputStream(request) << '}';
}
JsonInputStream(request) << '}';
sendMessage(packMessage(request)); sendMessage(packMessage(request));
} }
void QmlV8DebuggerClient::continueInferior() void QmlV8DebuggerClient::executeStep()
{ {
QByteArray request; continueDebugging(In);
}
JsonInputStream(request) << '{' << INITIALPARAMS ; void QmlV8DebuggerClient::executeStepOut()
JsonInputStream(request) << ',' << "command" << ':' << "continue"; {
JsonInputStream(request) << '}'; continueDebugging(Out);
}
void QmlV8DebuggerClient::executeNext()
{
continueDebugging(Next);
}
sendMessage(packMessage(request)); void QmlV8DebuggerClient::executeStepI()
{
continueDebugging(In);
}
void QmlV8DebuggerClient::continueInferior()
{
continueDebugging(Continue);
} }
void QmlV8DebuggerClient::interruptInferior() void QmlV8DebuggerClient::interruptInferior()
...@@ -194,6 +279,10 @@ void QmlV8DebuggerClient::interruptInferior() ...@@ -194,6 +279,10 @@ void QmlV8DebuggerClient::interruptInferior()
void QmlV8DebuggerClient::startSession() void QmlV8DebuggerClient::startSession()
{ {
//Set up Exception Handling first
//TODO: For now we enable breaks for all exceptions
breakOnException(AllExceptions, true);
QByteArray request; QByteArray request;
JsonInputStream(request) << '{' << INITIALPARAMS ; JsonInputStream(request) << '{' << INITIALPARAMS ;
...@@ -206,6 +295,8 @@ void QmlV8DebuggerClient::startSession() ...@@ -206,6 +295,8 @@ void QmlV8DebuggerClient::startSession()
void QmlV8DebuggerClient::endSession() void QmlV8DebuggerClient::endSession()
{ {
clearExceptionSelection();
QByteArray request; QByteArray request;
JsonInputStream(request) << '{' << INITIALPARAMS ; JsonInputStream(request) << '{' << INITIALPARAMS ;
...@@ -418,12 +509,17 @@ void QmlV8DebuggerClient::messageReceived(const QByteArray &data) ...@@ -418,12 +509,17 @@ void QmlV8DebuggerClient::messageReceived(const QByteArray &data)
if (event == "break") { if (event == "break") {
d->engine->inferiorSpontaneousStop(); d->engine->inferiorSpontaneousStop();
listBreakpoints(); listBreakpoints();
} else if (event == "exception") {
d->handleException = true;
d->engine->inferiorSpontaneousStop();
storeExceptionInformation(response);
backtrace();
} }
} }
} }
} }
void QmlV8DebuggerClient::setStackFrames(QByteArray &message) void QmlV8DebuggerClient::setStackFrames(const QByteArray &message)
{ {
d->frames = message; d->frames = message;
JsonValue response(message); JsonValue response(message);
...@@ -468,6 +564,9 @@ void QmlV8DebuggerClient::setStackFrames(QByteArray &message) ...@@ -468,6 +564,9 @@ void QmlV8DebuggerClient::setStackFrames(QByteArray &message)
d->engine->gotoLocation(ideStackFrames.value(0)); d->engine->gotoLocation(ideStackFrames.value(0));
} }
if (d->handleException) {
handleException();
}
} }
void QmlV8DebuggerClient::setLocals(int frameIndex) void QmlV8DebuggerClient::setLocals(int frameIndex)
...@@ -533,7 +632,7 @@ void QmlV8DebuggerClient::setLocals(int frameIndex) ...@@ -533,7 +632,7 @@ void QmlV8DebuggerClient::setLocals(int frameIndex)
} }
} }
void QmlV8DebuggerClient::expandLocal(QByteArray &message) void QmlV8DebuggerClient::expandLocal(const QByteArray &message)
{ {
JsonValue response(message); JsonValue response(message);
...@@ -553,7 +652,7 @@ void QmlV8DebuggerClient::expandLocal(QByteArray &message) ...@@ -553,7 +652,7 @@ void QmlV8DebuggerClient::expandLocal(QByteArray &message)
} }
} }
void QmlV8DebuggerClient::setExpression(QByteArray &message) void QmlV8DebuggerClient::setExpression(const QByteArray &message)
{ {
JsonValue response(message); JsonValue response(message);
JsonValue body = response.findChild("body"); JsonValue body = response.findChild("body");
...@@ -569,7 +668,7 @@ void QmlV8DebuggerClient::setExpression(QByteArray &message) ...@@ -569,7 +668,7 @@ void QmlV8DebuggerClient::setExpression(QByteArray &message)
//TODO: For watch point //TODO: For watch point
} }
void QmlV8DebuggerClient::updateBreakpoints(QByteArray &message) void QmlV8DebuggerClient::updateBreakpoints(const QByteArray &message)
{ {
JsonValue response(message); JsonValue response(message);
...@@ -600,7 +699,7 @@ void QmlV8DebuggerClient::updateBreakpoints(QByteArray &message) ...@@ -600,7 +699,7 @@ void QmlV8DebuggerClient::updateBreakpoints(QByteArray &message)
} }
} }
void QmlV8DebuggerClient::setPropertyValue(JsonValue &refs, JsonValue &property, QByteArray &prepend) void QmlV8DebuggerClient::setPropertyValue(const JsonValue &refs, const JsonValue &property, const QByteArray &prepend)
{ {
WatchData data; WatchData data;
data.exp = property.findChild("name").toVariant().toByteArray(); data.exp = property.findChild("name").toVariant().toByteArray();
......
...@@ -48,6 +48,21 @@ class QmlV8DebuggerClient : public QmlDebuggerClient ...@@ -48,6 +48,21 @@ class QmlV8DebuggerClient : public QmlDebuggerClient
{ {
Q_OBJECT Q_OBJECT
enum Exceptions
{
NoExceptions,
UncaughtExceptions,
AllExceptions
};
enum StepAction
{
Continue,
In,
Out,
Next
};
public: public:
explicit QmlV8DebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection *client); explicit QmlV8DebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection *client);
~QmlV8DebuggerClient(); ~QmlV8DebuggerClient();
...@@ -91,14 +106,21 @@ protected: ...@@ -91,14 +106,21 @@ protected:
private: private:
void listBreakpoints(); void listBreakpoints();
void backtrace(); void backtrace();
void setStackFrames(QByteArray &); void setStackFrames(const QByteArray &message);
void setLocals(int frameIndex); void setLocals(int frameIndex);
void setExpression(QByteArray &message); void setExpression(const QByteArray &message);
void updateBreakpoints(QByteArray &message); void updateBreakpoints(const QByteArray &message);
void expandLocal(QByteArray &message); void expandLocal(const QByteArray &message);
void setPropertyValue(Json::JsonValue &refs, Json::JsonValue &property, QByteArray &prepend); void setPropertyValue(const Json::JsonValue &refs, const Json::JsonValue &property, const QByteArray &prepend);
int indexInRef(const Json::JsonValue &refs, int refIndex); int indexInRef(const Json::JsonValue &refs, int refIndex);
QByteArray packMessage(QByteArray& message); QByteArray packMessage(const QByteArray &message);
void breakOnException(Exceptions exceptionsType, bool enabled);
void storeExceptionInformation(const QByteArray &message);
void handleException();
void clearExceptionSelection();
void continueDebugging(StepAction type);
private: private:
QmlV8DebuggerClientPrivate *d; QmlV8DebuggerClientPrivate *d;
......
...@@ -398,6 +398,7 @@ public: ...@@ -398,6 +398,7 @@ public:
OtherSelection, OtherSelection,
SnippetPlaceholderSelection, SnippetPlaceholderSelection,
ObjCSelection, ObjCSelection,
DebuggerExceptionSelection,
NExtraSelectionKinds NExtraSelectionKinds
}; };
void setExtraSelections(ExtraSelectionKind kind, const QList<QTextEdit::ExtraSelection> &selections); void setExtraSelections(ExtraSelectionKind kind, const QList<QTextEdit::ExtraSelection> &selections);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment