/************************************************************************** ** ** 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 "qmlv8debuggerclient.h" #include "qmlv8debuggerclientconstants.h" #include "debuggerstringutils.h" #include "watchhandler.h" #include "breakpoint.h" #include "breakhandler.h" #include "qmlengine.h" #include "stackhandler.h" #include #include #include #include #include #include #include #include #include #define DEBUG_QML 0 #if DEBUG_QML # define SDEBUG(s) qDebug() << s #else # define SDEBUG(s) #endif using namespace Core; namespace Debugger { namespace Internal { typedef QPair WatchDataPair; struct QmlV8ObjectData { QByteArray type; QVariant value; QVariant properties; }; class QmlV8DebuggerClientPrivate { public: explicit QmlV8DebuggerClientPrivate(QmlV8DebuggerClient *q) : q(q), sequence(-1), engine(0) { q->resetState(); parser = m_scriptEngine.evaluate(_("JSON.parse")); stringifier = m_scriptEngine.evaluate(_("JSON.stringify")); } void connect(); void disconnect(); void interrupt(); void continueDebugging(QmlV8DebuggerClient::StepAction stepAction, int stepCount = 1); void evaluate(const QString expr, bool global = false, bool disableBreak = false, int frame = -1, bool addContext = false); void lookup(const QList 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 scopes(int frameNumber = -1); void scripts(int types = 4, const QList ids = QList(), bool includeSource = false, const QVariant filter = QVariant()); void source(int frame = -1, int fromLine = -1, int toLine = -1); void setBreakpoint(const QString type, const QString target, int line = -1, int column = -1, bool enabled = true, const QString condition = QString(), int ignoreCount = -1); void changeBreakpoint(int breakpoint, bool enabled = true, const QString condition = QString(), int ignoreCount = -1); void clearBreakpoint(int breakpoint); void setExceptionBreak(QmlV8DebuggerClient::Exceptions type, bool enabled = false); void listBreakpoints(); void v8flags(const QString flags); void version(); //void profile(ProfileCommand command); //NOT SUPPORTED void gc(); QmlV8ObjectData extractData(const QVariant &data); private: QByteArray packMessage(const QByteArray &message); QScriptValue initObject(); public: QmlV8DebuggerClient *q; int sequence; QmlEngine *engine; QHash breakpoints; QHash breakpointsSync; QHash locals; QHash watches; QScriptValue parser; QScriptValue stringifier; QmlV8DebuggerClient::V8DebuggerStates state; int currentFrameIndex; bool updateCurrentStackFrameIndex; private: QScriptEngine m_scriptEngine; }; /////////////////////////////////////////////////////////////////////// // // QmlV8DebuggerClientPrivate // /////////////////////////////////////////////////////////////////////// void QmlV8DebuggerClientPrivate::connect() { // { "seq" : , // "type" : "request", // "command" : "connect", // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(CONNECT))); const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } void QmlV8DebuggerClientPrivate::disconnect() { // { "seq" : , // "type" : "request", // "command" : "disconnect", // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(DISCONNECT))); const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } void QmlV8DebuggerClientPrivate::interrupt() { // { "seq" : , // "type" : "request", // "command" : "interrupt", // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(INTERRUPT))); const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } void QmlV8DebuggerClientPrivate::continueDebugging(QmlV8DebuggerClient::StepAction action, int count) { //First reset q->resetState(); // { "seq" : , // "type" : "request", // "command" : "continue", // "arguments" : { "stepaction" : <"in", "next" or "out">, // "stepcount" : // } // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(CONTINEDEBUGGING))); if (action != QmlV8DebuggerClient::Continue) { QScriptValue args = parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT))); switch (action) { case QmlV8DebuggerClient::In: args.setProperty(_(STEPACTION), QScriptValue(_(IN))); break; case QmlV8DebuggerClient::Out: args.setProperty(_(STEPACTION), QScriptValue(_(OUT))); break; case QmlV8DebuggerClient::Next: args.setProperty(_(STEPACTION), QScriptValue(_(NEXT))); break; default:break; } if (count != 1) args.setProperty(_(STEPCOUNT), QScriptValue(count)); jsonVal.setProperty(_(ARGUMENTS), args); } const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } void QmlV8DebuggerClientPrivate::evaluate(const QString expr, bool global, bool disableBreak, int frame, bool addContext) { updateCurrentStackFrameIndex = false; // { "seq" : , // "type" : "request", // "command" : "evaluate", // "arguments" : { "expression" : , // "frame" : , // "global" : , // "disable_break" : , // "additional_context" : [ // { "name" : , "handle" : }, // { "name" : , "handle" : }, // ... // ] // } // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(EVALUATE))); QScriptValue args = parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT))); args.setProperty(_(EXPRESSION), QScriptValue(expr)); if (frame != -1) args.setProperty(_(FRAME), QScriptValue(frame)); if (global) args.setProperty(_(GLOBAL), QScriptValue(global)); if (disableBreak) args.setProperty(_(DISABLE_BREAK), QScriptValue(disableBreak)); if (addContext) { QAbstractItemModel *localsModel = engine->localsModel(); int rowCount = localsModel->rowCount(); QScriptValue ctxtList = parser.call(QScriptValue(), QScriptValueList() << _(ARRAY )); while (rowCount) { QModelIndex index = localsModel->index(--rowCount, 0); const WatchData *data = engine->watchHandler()->watchData(LocalsWatch, index); QScriptValue ctxt = parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT))); ctxt.setProperty(_(NAME), QScriptValue(data->name)); ctxt.setProperty(_(HANDLE), QScriptValue(int(data->id))); ctxtList.setProperty(rowCount, ctxt); } args.setProperty(_(ADDITIONAL_CONTEXT), QScriptValue(ctxtList)); } jsonVal.setProperty(_(ARGUMENTS), args); const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } void QmlV8DebuggerClientPrivate::lookup(QList handles, bool includeSource) { // { "seq" : , // "type" : "request", // "command" : "lookup", // "arguments" : { "handles" : , // "includeSource" : , // } // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(LOOKUP))); QScriptValue args = parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT))); QScriptValue array = parser.call(QScriptValue(), QScriptValueList() << _(ARRAY)); int index = 0; foreach (int handle, handles) { array.setProperty(index++, QScriptValue(handle)); } args.setProperty(_(HANDLES), array); if (includeSource) args.setProperty(_(INCLUDESOURCE), QScriptValue(includeSource)); jsonVal.setProperty(_(ARGUMENTS), args); const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } void QmlV8DebuggerClientPrivate::backtrace(int fromFrame, int toFrame, bool bottom) { // { "seq" : , // "type" : "request", // "command" : "backtrace", // "arguments" : { "fromFrame" : // "toFrame" : // "bottom" : // } // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(BACKTRACE))); QScriptValue args = parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT))); if (fromFrame != -1) args.setProperty(_(FROMFRAME), QScriptValue(fromFrame)); if (toFrame != -1) args.setProperty(_(TOFRAME), QScriptValue(toFrame)); if (bottom) args.setProperty(_(BOTTOM), QScriptValue(bottom)); jsonVal.setProperty(_(ARGUMENTS), args); const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } void QmlV8DebuggerClientPrivate::frame(int number) { // { "seq" : , // "type" : "request", // "command" : "frame", // "arguments" : { "number" : // } // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(FRAME))); if (number != -1) { QScriptValue args = parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT))); args.setProperty(_(NUMBER), QScriptValue(number)); jsonVal.setProperty(_(ARGUMENTS), args); } const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } void QmlV8DebuggerClientPrivate::scope(int number, int frameNumber) { // { "seq" : , // "type" : "request", // "command" : "scope", // "arguments" : { "number" : // "frameNumber" : // } // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(SCOPE))); if (number != -1) { QScriptValue args = parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT))); args.setProperty(_(NUMBER), QScriptValue(number)); if (frameNumber != -1) args.setProperty(_(FRAMENUMBER), QScriptValue(frameNumber)); jsonVal.setProperty(_(ARGUMENTS), args); } const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } void QmlV8DebuggerClientPrivate::scopes(int frameNumber) { // { "seq" : , // "type" : "request", // "command" : "scopes", // "arguments" : { "frameNumber" : // } // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(SCOPES))); if (frameNumber != -1) { QScriptValue args = parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT))); args.setProperty(_(FRAMENUMBER), QScriptValue(frameNumber)); jsonVal.setProperty(_(ARGUMENTS), args); } const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } void QmlV8DebuggerClientPrivate::scripts(int types, const QList ids, bool includeSource, const QVariant /*filter*/) { // { "seq" : , // "type" : "request", // "command" : "scripts", // "arguments" : { "types" : // "ids" : // "includeSource" : // "filter" : // } // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(SCRIPTS))); QScriptValue args = parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT))); args.setProperty(_(TYPES), QScriptValue(types)); if (ids.count()) { QScriptValue array = parser.call(QScriptValue(), QScriptValueList() << _(ARRAY)); int index = 0; foreach (int id, ids) { array.setProperty(index++, QScriptValue(id)); } args.setProperty(_(IDS), array); } if (includeSource) args.setProperty(_(INCLUDESOURCE), QScriptValue(includeSource)); jsonVal.setProperty(_(ARGUMENTS), args); const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } void QmlV8DebuggerClientPrivate::source(int frame, int fromLine, int toLine) { // { "seq" : , // "type" : "request", // "command" : "source", // "arguments" : { "frame" : // "fromLine" : // "toLine" : // } // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(SOURCE))); QScriptValue args = parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT))); if (frame != -1) args.setProperty(_(FRAME), QScriptValue(frame)); if (fromLine != -1) args.setProperty(_(FROMLINE), QScriptValue(fromLine)); if (toLine != -1) args.setProperty(_(TOLINE), QScriptValue(toLine)); jsonVal.setProperty(_(ARGUMENTS), args); const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } void QmlV8DebuggerClientPrivate::setBreakpoint(const QString type, const QString target, int line, int column, bool enabled, const QString condition, int ignoreCount) { // { "seq" : , // "type" : "request", // "command" : "setbreakpoint", // "arguments" : { "type" : <"function" or "script" or "scriptId" or "scriptRegExp"> // "target" : // "line" : // "column" : // "enabled" : // "condition" : // "ignoreCount" : // } // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(SETBREAKPOINT))); QScriptValue args = parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT))); args.setProperty(_(TYPE), QScriptValue(type)); args.setProperty(_(TARGET), QScriptValue(target)); if (line != -1) args.setProperty(_(LINE), QScriptValue(line)); if (column != -1) args.setProperty(_(COLUMN), QScriptValue(column)); args.setProperty(_(ENABLED), QScriptValue(enabled)); if (!condition.isEmpty()) args.setProperty(_(CONDITION), QScriptValue(condition)); if (ignoreCount != -1) args.setProperty(_(IGNORECOUNT), QScriptValue(ignoreCount)); jsonVal.setProperty(_(ARGUMENTS), args); const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } void QmlV8DebuggerClientPrivate::changeBreakpoint(int breakpoint, bool enabled, const QString condition, int ignoreCount) { // { "seq" : , // "type" : "request", // "command" : "changebreakpoint", // "arguments" : { "breakpoint" : // "enabled" : // "condition" : // "ignoreCount" : sendMessage(packMessage(jsonMessage.toString().toUtf8())); } void QmlV8DebuggerClientPrivate::clearBreakpoint(int breakpoint) { // { "seq" : , // "type" : "request", // "command" : "clearbreakpoint", // "arguments" : { "breakpoint" : // } // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(CLEARBREAKPOINT))); QScriptValue args = parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT))); args.setProperty(_(BREAKPOINT), QScriptValue(breakpoint)); jsonVal.setProperty(_(ARGUMENTS), args); const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } void QmlV8DebuggerClientPrivate::setExceptionBreak(QmlV8DebuggerClient::Exceptions type, bool enabled) { // { "seq" : , // "type" : "request", // "command" : "setexceptionbreak", // "arguments" : { "type" : , // "enabled" : // } // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(SETEXCEPTIONBREAK))); QScriptValue args = parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT))); if (type == QmlV8DebuggerClient::AllExceptions) args.setProperty(_(TYPE), QScriptValue(_(ALL))); //Not Supported // else if (type == QmlV8DebuggerClient::UncaughtExceptions) // args.setProperty(_(TYPE),QScriptValue(_(UNCAUGHT))); if (enabled) args.setProperty(_(ENABLED), QScriptValue(enabled)); jsonVal.setProperty(_(ARGUMENTS), args); const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } void QmlV8DebuggerClientPrivate::listBreakpoints() { // { "seq" : , // "type" : "request", // "command" : "listbreakpoints", // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(LISTBREAKPOINTS))); const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } void QmlV8DebuggerClientPrivate::v8flags(const QString flags) { // { "seq" : , // "type" : "request", // "command" : "v8flags", // "arguments" : { "flags" : // } // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(V8FLAGS))); QScriptValue args = parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT))); args.setProperty(_(FLAGS), QScriptValue(flags)); jsonVal.setProperty(_(ARGUMENTS), args); const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } void QmlV8DebuggerClientPrivate::version() { // { "seq" : , // "type" : "request", // "command" : "version", // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(VERSION))); const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } //void QmlV8DebuggerClientPrivate::profile(ProfileCommand command) //{ //// { "seq" : , //// "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); // q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); //} void QmlV8DebuggerClientPrivate::gc() { // { "seq" : , // "type" : "request", // "command" : "gc", // "arguments" : { "type" : , // } // } QScriptValue jsonVal = initObject(); jsonVal.setProperty(_(COMMAND), QScriptValue(_(GARBAGECOLLECTOR))); QScriptValue args = parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT))); args.setProperty(_(TYPE), QScriptValue(_(ALL))); jsonVal.setProperty(_(ARGUMENTS), args); const QScriptValue jsonMessage = stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); q->sendMessage(packMessage(jsonMessage.toString().toUtf8())); } QmlV8ObjectData QmlV8DebuggerClientPrivate::extractData(const QVariant &data) { // { "handle" : , // "type" : <"undefined", "null", "boolean", "number", "string", "object", "function" or "frame"> // } // {"handle":,"type":"undefined"} // {"handle":,"type":"null"} // { "handle":, // "type" : <"boolean", "number" or "string"> // "value" : // } // {"handle":7,"type":"boolean","value":true} // {"handle":8,"type":"number","value":42} // { "handle" : , // "type" : "object", // "className" : , // "constructorFunction" : {"ref":}, // "protoObject" : {"ref":}, // "prototypeObject" : {"ref":}, // "properties" : [ {"name" : , // "ref" : // }, // ... // ] // } // { "handle" : , // "type" : "function", // "className" : "Function", // "constructorFunction" : {"ref":}, // "protoObject" : {"ref":}, // "prototypeObject" : {"ref":}, // "name" : , // "inferredName" : // "source" : , // "script" : , // "scriptId" : , // "position" : , // "line" : , // "column" : , // "properties" : [ {"name" : , // "ref" : // }, // ... // ] // } QmlV8ObjectData objectData; const QVariantMap dataMap = data.toMap(); 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")); } else if (type == _("function")) { objectData.type = QByteArray("function"); objectData.value = dataMap.value(_(NAME)); objectData.properties = dataMap.value(_("properties")); } else if (type == _("script")) { objectData.type = QByteArray("script"); objectData.value = dataMap.value(_(NAME)); } return objectData; } QByteArray QmlV8DebuggerClientPrivate::packMessage(const QByteArray &message) { QByteArray reply; QDataStream rs(&reply, QIODevice::WriteOnly); QByteArray cmd = V8DEBUG; rs << cmd << message; SDEBUG(QString(message)); return reply; } QScriptValue QmlV8DebuggerClientPrivate::initObject() { QScriptValue jsonVal = parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT))); jsonVal.setProperty(_(SEQ), QScriptValue(++sequence)); jsonVal.setProperty(_(TYPE), _(REQUEST)); return jsonVal; } /////////////////////////////////////////////////////////////////////// // // QmlV8DebuggerClient // /////////////////////////////////////////////////////////////////////// QmlV8DebuggerClient::QmlV8DebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection *client) : QmlDebuggerClient(client, QLatin1String("V8Debugger")), d(new QmlV8DebuggerClientPrivate(this)) { } QmlV8DebuggerClient::~QmlV8DebuggerClient() { delete d; } void QmlV8DebuggerClient::startSession() { d->connect(); } void QmlV8DebuggerClient::endSession() { resetState(); d->disconnect(); } void QmlV8DebuggerClient::executeStep() { QTC_CHECK(d->state == QmlV8DebuggerClient::WaitingForRequestState); d->continueDebugging(In); } void QmlV8DebuggerClient::executeStepOut() { QTC_CHECK(d->state == QmlV8DebuggerClient::WaitingForRequestState); d->continueDebugging(Out); } void QmlV8DebuggerClient::executeNext() { QTC_CHECK(d->state == QmlV8DebuggerClient::WaitingForRequestState); d->continueDebugging(Next); } void QmlV8DebuggerClient::executeStepI() { QTC_CHECK(d->state == QmlV8DebuggerClient::WaitingForRequestState); d->continueDebugging(In); } void QmlV8DebuggerClient::continueInferior() { QTC_CHECK(d->state == QmlV8DebuggerClient::WaitingForRequestState); d->continueDebugging(Continue); } void QmlV8DebuggerClient::interruptInferior() { QTC_CHECK(d->state == QmlV8DebuggerClient::WaitingForRequestState || d->state == QmlV8DebuggerClient::RunningState); d->interrupt(); } void QmlV8DebuggerClient::activateFrame(int index) { QTC_CHECK(d->state == QmlV8DebuggerClient::WaitingForRequestState); d->backtrace(index); } bool QmlV8DebuggerClient::acceptsBreakpoint(const BreakpointModelId &id) { BreakpointType type = d->engine->breakHandler()->breakpointData(id).type; return (type == BreakpointOnQmlSignalHandler || type == BreakpointByFunction || type == BreakpointAtJavaScriptThrow); } void QmlV8DebuggerClient::insertBreakpoint(const BreakpointModelId &id) { QTC_CHECK(d->state == QmlV8DebuggerClient::WaitingForRequestState || d->state == QmlV8DebuggerClient::RunningState); BreakHandler *handler = d->engine->breakHandler(); const BreakpointParameters ¶ms = handler->breakpointData(id); if (params.type == BreakpointAtJavaScriptThrow) { handler->notifyBreakpointInsertOk(id); d->setExceptionBreak(AllExceptions, params.enabled); } else if (params.type == BreakpointByFileAndLine) { d->setBreakpoint(QString(_(SCRIPT)), QFileInfo(params.fileName).fileName(), params.lineNumber - 1, -1, params.enabled, QString(params.condition), params.ignoreCount); } else if (params.type == BreakpointByFunction) { d->setBreakpoint(QString(_(FUNCTION)), params.functionName, -1, -1, params.enabled, QString(params.condition), params.ignoreCount); } else if (params.type == BreakpointOnQmlSignalHandler) { d->setBreakpoint(QString(_(EVENT)), params.functionName, -1, -1, params.enabled); } d->breakpointsSync.insert(d->sequence, id); } void QmlV8DebuggerClient::removeBreakpoint(const BreakpointModelId &id) { QTC_CHECK(d->state == QmlV8DebuggerClient::WaitingForRequestState || d->state == QmlV8DebuggerClient::RunningState); BreakHandler *handler = d->engine->breakHandler(); int breakpoint = d->breakpoints.value(id); d->breakpoints.remove(id); if (handler->breakpointData(id).type == BreakpointAtJavaScriptThrow) { d->setExceptionBreak(AllExceptions); } else { d->clearBreakpoint(breakpoint); } } void QmlV8DebuggerClient::changeBreakpoint(const BreakpointModelId &id) { QTC_CHECK(d->state == QmlV8DebuggerClient::WaitingForRequestState || d->state == QmlV8DebuggerClient::RunningState); BreakHandler *handler = d->engine->breakHandler(); const BreakpointParameters ¶ms = handler->breakpointData(id); if (params.type == BreakpointAtJavaScriptThrow) { d->setExceptionBreak(AllExceptions, params.enabled); } int breakpoint = d->breakpoints.value(id); d->changeBreakpoint(breakpoint, params.enabled, QString(params.condition), params.ignoreCount); BreakpointResponse br = handler->response(id); br.enabled = params.enabled; br.condition = params.condition; br.ignoreCount = params.ignoreCount; handler->setResponse(id, br); } void QmlV8DebuggerClient::synchronizeBreakpoints() { //NOT USED } void QmlV8DebuggerClient::assignValueInDebugger(const QByteArray /*expr*/, const quint64 &/*id*/, const QString &property, const QString &value) { QTC_CHECK(d->state == QmlV8DebuggerClient::WaitingForRequestState); StackHandler *stackHandler = d->engine->stackHandler(); QString expression = QString(_("%1 = %2;")).arg(property).arg(value); if (stackHandler->isContentsValid()) { d->state = QmlV8DebuggerClient::BacktraceRequestedState; d->evaluate(expression, false, false, stackHandler->currentIndex()); } } void QmlV8DebuggerClient::updateWatchData(const WatchData &data) { if (data.isWatcher()) { WatchDataPair pair(data.iname, data.exp); if (d->watches.key(pair)) { WatchData data1 = data; data1.setAllUnneeded(); // data1.setValue(_("")); // data1.setHasChildren(false); d->engine->watchHandler()->insertData(data1); } else { StackHandler *stackHandler = d->engine->stackHandler(); if (stackHandler->isContentsValid()) d->evaluate(data.exp, false, false, stackHandler->currentIndex()); else d->evaluate(data.exp); d->watches.insert(d->sequence, pair); } } } void QmlV8DebuggerClient::executeDebuggerCommand(const QString &command) { QTC_CHECK(d->state == QmlV8DebuggerClient::WaitingForRequestState || d->state == QmlV8DebuggerClient::RunningState); StackHandler *stackHandler = d->engine->stackHandler(); if (stackHandler->isContentsValid()) { //Set the state d->state = QmlV8DebuggerClient::BacktraceRequestedState; d->evaluate(command, false, false, stackHandler->currentIndex()); } else { //Currently cannot evaluate if not in a javascript break d->engine->showMessage(_("Request Was Unsuccessful"), ScriptConsoleOutput); // d->evaluate(command); } } void QmlV8DebuggerClient::synchronizeWatchers(const QStringList &/*watchers*/) { //TODO:: } void QmlV8DebuggerClient::expandObject(const QByteArray &iname, quint64 objectId) { QTC_CHECK(d->state == QmlV8DebuggerClient::WaitingForRequestState); d->locals.insertMulti(objectId, iname); d->lookup(QList() << objectId); } void QmlV8DebuggerClient::setEngine(QmlEngine *engine) { d->engine = engine; } void QmlV8DebuggerClient::messageReceived(const QByteArray &data) { QDataStream ds(data); QByteArray command; ds >> command; if (command == V8DEBUG) { QByteArray response; ds >> response; QString responseString(response); SDEBUG(responseString); const QVariantMap resp = d->parser.call(QScriptValue(), QScriptValueList() << QScriptValue(responseString)).toVariant().toMap(); const QString type(resp.value(_(TYPE)).toString()); if (type == _("response")) { bool success = resp.value(_("success")).toBool(); if (!success) { SDEBUG("Request was unsuccessful"); d->engine->logMessage(QmlEngine::LogReceive, QString(_("V8 Response Error: %1")).arg( resp.value(_("message")).toString())); } const QString debugCommand(resp.value(_(COMMAND)).toString()); if (debugCommand == _(CONNECT)) { //debugging session started } else if (debugCommand == _(DISCONNECT)) { //debugging session ended } else if (debugCommand == _(BACKTRACE)) { if (success) { updateStack(resp.value(_(BODY)), resp.value(_(REFS))); } } else if (debugCommand == _(LOOKUP)) { expandLocal(resp.value(_(BODY)), resp.value(_(REFS))); } else if (debugCommand == _(EVALUATE)) { if (success) { int seq = resp.value(_("request_seq")).toInt(); updateEvaluationResult(seq, resp.value(_(BODY)), resp.value(_(REFS))); } else { d->engine->showMessage(resp.value(_("message")).toString(), ScriptConsoleOutput); } } else if (debugCommand == _(LISTBREAKPOINTS)) { updateBreakpoints(resp.value(_(BODY))); } else if (debugCommand == _(SETBREAKPOINT)) { // { "seq" : , // "type" : "response", // "request_seq" : , // "command" : "setbreakpoint", // "body" : { "type" : <"function" or "script"> // "breakpoint" : // } // "running" : // "success" : true // } int seq = resp.value(_("request_seq")).toInt(); const QVariantMap breakpointData = resp.value(_(BODY)).toMap(); int index = breakpointData.value(_("breakpoint")).toInt(); BreakpointModelId id = d->breakpointsSync.take(seq); d->breakpoints.insert(id, index); d->engine->breakHandler()->notifyBreakpointInsertOk(id); } else if (debugCommand == _(CHANGEBREAKPOINT)) { // DO NOTHING } else if (debugCommand == _(CLEARBREAKPOINT)) { // DO NOTHING } else if (debugCommand == _(SETEXCEPTIONBREAK)) { // { "seq" : , // "type" : "response", // "request_seq" : , // "command" : "setexceptionbreak", // “body” : { "type" : , // "enabled" : // } // "running" : true // "success" : true // } //TODO:: } else if (debugCommand == _(FRAME)) { if (success) { const QVariant body = resp.value(_(BODY)); const QVariant refs = resp.value(_(REFS)); const QVariant locals = body.toMap().value(_("locals")); StackFrame frame = createStackFrame(body, refs); updateLocals(locals, refs); d->engine->stackHandler()->setCurrentIndex(frame.level); } } else if (debugCommand == _(SCOPE)) { } else if (debugCommand == _(SCOPES)) { } else if (debugCommand == _(SOURCE)) { } else if (debugCommand == _(SCRIPTS)) { } else if (debugCommand == _(VERSION)) { } else if (debugCommand == _(V8FLAGS)) { } else if (debugCommand == _(GARBAGECOLLECTOR)) { } else { // DO NOTHING } } else if (type == _(EVENT)) { const QString eventType(resp.value(_(EVENT)).toString()); if (eventType == _("break")) { //DO NOTHING } 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 (resp.value(_("running")).toBool()) { d->state = QmlV8DebuggerClient::RunningState; SDEBUG(QString(_("State: %1")).arg(d->state)); } else { if (d->state == QmlV8DebuggerClient::RunningState) { d->state = QmlV8DebuggerClient::BreakpointsRequestedState; SDEBUG(QString(_("State: %1")).arg(d->state)); } d->engine->inferiorSpontaneousStop(); } if (d->state == QmlV8DebuggerClient::BreakpointsRequestedState) { d->state = QmlV8DebuggerClient::BacktraceRequestedState; SDEBUG(QString(_("State: %1")).arg(d->state)); d->listBreakpoints(); } else if (d->state == QmlV8DebuggerClient::BacktraceRequestedState) { d->state = QmlV8DebuggerClient::WaitingForRequestState; SDEBUG(QString(_("State: %1")).arg(d->state)); d->backtrace(d->currentFrameIndex); } } else { //DO NOTHING } } void QmlV8DebuggerClient::updateStack(const QVariant &bodyVal, const QVariant &refsVal) { // { "seq" : , // "type" : "response", // "request_seq" : , // "command" : "backtrace", // "body" : { "fromFrame" : // "toFrame" : // "totalFrames" : // "frames" : // } // "running" : // "success" : true // } StackFrames stackFrames; const QVariantMap body = bodyVal.toMap(); const QVariantList frames = body.value(_("frames")).toList(); d->engine->watchHandler()->beginCycle(); foreach (const QVariant &frame, frames) { stackFrames << createStackFrame(frame, refsVal); } d->engine->watchHandler()->endCycle(); d->currentFrameIndex = body.value(_("fromFrame")).toInt(); if (!d->currentFrameIndex ) { d->engine->stackHandler()->setFrames(stackFrames); } if (d->updateCurrentStackFrameIndex) { d->engine->stackHandler()->setCurrentIndex(d->currentFrameIndex); d->engine->gotoLocation(stackFrames.value(d->currentFrameIndex)); } d->updateCurrentStackFrameIndex = true; } StackFrame QmlV8DebuggerClient::createStackFrame(const QVariant &bodyVal, const QVariant &refsVal) { // { "seq" : , // "type" : "response", // "request_seq" : , // "command" : "frame", // "body" : { "index" : , // "receiver" : , // "func" : , // "script" :