diff options
| author | Aurindam Jana <aurindam.jana@nokia.com> | 2011-07-26 16:22:49 +0200 |
|---|---|---|
| committer | Aurindam Jana <aurindam.jana@nokia.com> | 2011-08-03 11:27:38 +0200 |
| commit | ab574ba88dcbc15538e58a6d46185a9b032fe7f9 (patch) | |
| tree | 8e09146112f00d4491096bdfec9bb10966a8efdf /src/plugins/debugger/qml/qscriptdebuggerclient.cpp | |
| parent | 999265105bd30b7b30e6688fec35b1448eb276be (diff) | |
| download | qt-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.cpp | 511 |
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 |
