From 06723f8df97cd975509ad5c0f768513b7f24b56c Mon Sep 17 00:00:00 2001 From: Aurindam Jana Date: Tue, 6 Sep 2011 17:03:16 +0200 Subject: [PATCH] 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: Qt Sanity Bot Reviewed-by: Leandro T. C. Melo Reviewed-by: Kai Koehne --- src/plugins/debugger/qml/qmlv8debuggerclient.cpp | 185 +++++++++++++++++------ src/plugins/debugger/qml/qmlv8debuggerclient.h | 34 ++++- src/plugins/texteditor/basetexteditor.h | 1 + 3 files changed, 171 insertions(+), 49 deletions(-) diff --git a/src/plugins/debugger/qml/qmlv8debuggerclient.cpp b/src/plugins/debugger/qml/qmlv8debuggerclient.cpp index 692c080d04..5f7b11f45e 100644 --- a/src/plugins/debugger/qml/qmlv8debuggerclient.cpp +++ b/src/plugins/debugger/qml/qmlv8debuggerclient.cpp @@ -44,6 +44,10 @@ #include #include +#include +#include + +#include #include #include #include @@ -51,20 +55,31 @@ #define INITIALPARAMS "seq" << ':' << ++d->sequence << ',' << "type" << ':' << "request" +using namespace Core; using namespace Json; namespace Debugger { namespace Internal { +struct ExceptionInfo +{ + int sourceLine; + QString filePath; + QString errorMessage; +}; + class QmlV8DebuggerClientPrivate { public: explicit QmlV8DebuggerClientPrivate(QmlV8DebuggerClient *) : - sequence(0), ping(0), engine(0) + handleException(false), + sequence(0), + ping(0), + engine(0) { - } + bool handleException; int sequence; int ping; QmlEngine *engine; @@ -73,6 +88,7 @@ public: QHash locals; QHash watches; QByteArray frames; + QScopedPointer exceptionInfo; }; QmlV8DebuggerClient::QmlV8DebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection* client) @@ -86,7 +102,7 @@ QmlV8DebuggerClient::~QmlV8DebuggerClient() delete d; } -QByteArray QmlV8DebuggerClient::packMessage(QByteArray& message) +QByteArray QmlV8DebuggerClient::packMessage(const QByteArray &message) { QByteArray reply; QDataStream rs(&reply, QIODevice::WriteOnly); @@ -95,15 +111,21 @@ QByteArray QmlV8DebuggerClient::packMessage(QByteArray& message) return reply; } -void QmlV8DebuggerClient::executeStep() +void QmlV8DebuggerClient::breakOnException(Exceptions exceptionsType, bool enabled) { + //TODO: Have to deal with NoExceptions QByteArray request; JsonInputStream(request) << '{' << INITIALPARAMS ; - JsonInputStream(request) << ',' << "command" << ':' << "continue"; + JsonInputStream(request) << ',' << "command" << ':' << "setexceptionbreak"; 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) << '}'; @@ -112,71 +134,134 @@ void QmlV8DebuggerClient::executeStep() sendMessage(packMessage(request)); } -void QmlV8DebuggerClient::executeStepOut() +void QmlV8DebuggerClient::storeExceptionInformation(const QByteArray &message) { - QByteArray request; + JsonValue response(message); - JsonInputStream(request) << '{' << INITIALPARAMS ; - JsonInputStream(request) << ',' << "command" << ':' << "continue"; + JsonValue body = response.findChild("body"); - JsonInputStream(request) << ',' << "arguments" << ':'; - JsonInputStream(request) << '{' << "stepaction" << ':' << "out"; - JsonInputStream(request) << '}'; + d->exceptionInfo.reset(new ExceptionInfo); + d->exceptionInfo->sourceLine = body.findChild("sourceLine").toVariant().toInt(); + 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 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(editor->widget()); + if (!ed) + continue; -} + QList 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() -{ - QByteArray request; + sel.format.setToolTip(d->exceptionInfo->errorMessage); - JsonInputStream(request) << '{' << INITIALPARAMS ; - JsonInputStream(request) << ',' << "command" << ':' << "continue"; + selections.append(sel); + ed->setExtraSelections(TextEditor::BaseTextEditorWidget::DebuggerExceptionSelection, selections); - JsonInputStream(request) << ',' << "arguments" << ':'; - JsonInputStream(request) << '{' << "stepaction" << ':' << "next"; - JsonInputStream(request) << '}'; + d->engine->showMessage(d->exceptionInfo->errorMessage, ScriptConsoleOutput); + } + } - 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 openedEditors = editorManager->openedEditors(); + QList selections; - sendMessage(packMessage(request)); + foreach (IEditor *editor, openedEditors) { + TextEditor::BaseTextEditorWidget *ed = qobject_cast(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; JsonInputStream(request) << '{' << INITIALPARAMS ; JsonInputStream(request) << ',' << "command" << ':' << "continue"; - JsonInputStream(request) << ',' << "arguments" << ':'; - JsonInputStream(request) << '{' << "stepaction" << ':' << "in"; - JsonInputStream(request) << '}'; + if (type != Continue) { + JsonInputStream(request) << ',' << "arguments" << ':'; + + 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)); - } -void QmlV8DebuggerClient::continueInferior() +void QmlV8DebuggerClient::executeStep() { - QByteArray request; + continueDebugging(In); +} - JsonInputStream(request) << '{' << INITIALPARAMS ; - JsonInputStream(request) << ',' << "command" << ':' << "continue"; - JsonInputStream(request) << '}'; +void QmlV8DebuggerClient::executeStepOut() +{ + continueDebugging(Out); +} +void QmlV8DebuggerClient::executeNext() +{ + continueDebugging(Next); +} - sendMessage(packMessage(request)); +void QmlV8DebuggerClient::executeStepI() +{ + continueDebugging(In); +} +void QmlV8DebuggerClient::continueInferior() +{ + continueDebugging(Continue); } void QmlV8DebuggerClient::interruptInferior() @@ -194,6 +279,10 @@ void QmlV8DebuggerClient::interruptInferior() void QmlV8DebuggerClient::startSession() { + //Set up Exception Handling first + //TODO: For now we enable breaks for all exceptions + breakOnException(AllExceptions, true); + QByteArray request; JsonInputStream(request) << '{' << INITIALPARAMS ; @@ -206,6 +295,8 @@ void QmlV8DebuggerClient::startSession() void QmlV8DebuggerClient::endSession() { + clearExceptionSelection(); + QByteArray request; JsonInputStream(request) << '{' << INITIALPARAMS ; @@ -418,12 +509,17 @@ void QmlV8DebuggerClient::messageReceived(const QByteArray &data) if (event == "break") { d->engine->inferiorSpontaneousStop(); 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; JsonValue response(message); @@ -468,6 +564,9 @@ void QmlV8DebuggerClient::setStackFrames(QByteArray &message) d->engine->gotoLocation(ideStackFrames.value(0)); } + if (d->handleException) { + handleException(); + } } 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); @@ -553,7 +652,7 @@ void QmlV8DebuggerClient::expandLocal(QByteArray &message) } } -void QmlV8DebuggerClient::setExpression(QByteArray &message) +void QmlV8DebuggerClient::setExpression(const QByteArray &message) { JsonValue response(message); JsonValue body = response.findChild("body"); @@ -569,7 +668,7 @@ void QmlV8DebuggerClient::setExpression(QByteArray &message) //TODO: For watch point } -void QmlV8DebuggerClient::updateBreakpoints(QByteArray &message) +void QmlV8DebuggerClient::updateBreakpoints(const QByteArray &message) { JsonValue response(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; data.exp = property.findChild("name").toVariant().toByteArray(); diff --git a/src/plugins/debugger/qml/qmlv8debuggerclient.h b/src/plugins/debugger/qml/qmlv8debuggerclient.h index 04aeec1762..17165d879a 100644 --- a/src/plugins/debugger/qml/qmlv8debuggerclient.h +++ b/src/plugins/debugger/qml/qmlv8debuggerclient.h @@ -48,6 +48,21 @@ class QmlV8DebuggerClient : public QmlDebuggerClient { Q_OBJECT + enum Exceptions + { + NoExceptions, + UncaughtExceptions, + AllExceptions + }; + + enum StepAction + { + Continue, + In, + Out, + Next + }; + public: explicit QmlV8DebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection *client); ~QmlV8DebuggerClient(); @@ -91,14 +106,21 @@ protected: private: void listBreakpoints(); void backtrace(); - void setStackFrames(QByteArray &); + void setStackFrames(const QByteArray &message); void setLocals(int frameIndex); - void setExpression(QByteArray &message); - void updateBreakpoints(QByteArray &message); - void expandLocal(QByteArray &message); - void setPropertyValue(Json::JsonValue &refs, Json::JsonValue &property, QByteArray &prepend); + void setExpression(const QByteArray &message); + void updateBreakpoints(const QByteArray &message); + void expandLocal(const QByteArray &message); + void setPropertyValue(const Json::JsonValue &refs, const Json::JsonValue &property, const QByteArray &prepend); 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: QmlV8DebuggerClientPrivate *d; diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h index e93f99cc84..4820729abc 100644 --- a/src/plugins/texteditor/basetexteditor.h +++ b/src/plugins/texteditor/basetexteditor.h @@ -398,6 +398,7 @@ public: OtherSelection, SnippetPlaceholderSelection, ObjCSelection, + DebuggerExceptionSelection, NExtraSelectionKinds }; void setExtraSelections(ExtraSelectionKind kind, const QList &selections); -- 2.11.0