1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qv8debugservice_p.h"
43 #include "qqmldebugservice_p_p.h"
44 #include <private/qjsconverter_impl_p.h>
45 #include <private/qv4compiler_p.h>
46 #include <private/qv8engine_p.h>
48 #include <QtCore/QHash>
49 #include <QtCore/QFileInfo>
50 #include <QtCore/QMutex>
52 //V8 DEBUG SERVICE PROTOCOL
53 // <HEADER><COMMAND><DATA>
54 // <HEADER> : "V8DEBUG"
55 // <COMMAND> : ["connect", "disconnect", "interrupt",
56 // "v8request", "v8message", "breakonsignal",
57 // "breakaftercompile"]
58 // <DATA> : connect, disconnect, interrupt: empty
59 // v8request, v8message: <JSONrequest_string>
60 // breakonsignal: <signalname_string><enabled_bool>
61 // breakaftercompile: <enabled_bool>
63 const char *V8_DEBUGGER_KEY_VERSION = "version";
64 const char *V8_DEBUGGER_KEY_CONNECT = "connect";
65 const char *V8_DEBUGGER_KEY_INTERRUPT = "interrupt";
66 const char *V8_DEBUGGER_KEY_DISCONNECT = "disconnect";
67 const char *V8_DEBUGGER_KEY_REQUEST = "v8request";
68 const char *V8_DEBUGGER_KEY_V8MESSAGE = "v8message";
69 const char *V8_DEBUGGER_KEY_BREAK_ON_SIGNAL = "breakonsignal";
73 struct SignalHandlerData
79 Q_GLOBAL_STATIC(QV8DebugService, v8ServiceInstance)
81 // DebugMessageHandler will call back already when the QV8DebugService constructor is
82 // running, we therefore need a plain pointer.
83 static QV8DebugService *v8ServiceInstancePtr = 0;
85 void DebugMessageDispatchHandler()
87 QMetaObject::invokeMethod(v8ServiceInstancePtr, "processDebugMessages", Qt::QueuedConnection);
90 void DebugMessageHandler(const v8::Debug::Message& message)
92 v8::DebugEvent event = message.GetEvent();
94 if (message.IsEvent()) {
95 if (event == v8::AfterCompile || event == v8::BeforeCompile)
97 } else if (event != v8::Break && event != v8::Exception &&
98 event != v8::AfterCompile && event != v8::BeforeCompile) {
102 v8ServiceInstancePtr->debugMessageHandler(QJSConverter::toString(message.GetJSON()));
105 class QV8DebugServicePrivate : public QQmlDebugServicePrivate
108 QV8DebugServicePrivate()
109 : connectReceived(false)
114 void initializeDebuggerThread();
116 static QByteArray packMessage(const QString &type, const QString &message = QString());
118 bool connectReceived;
119 QMutex initializeMutex;
120 QStringList breakOnSignals;
121 const QV8Engine *engine;
124 QV8DebugService::QV8DebugService(QObject *parent)
125 : QQmlDebugService(*(new QV8DebugServicePrivate()),
126 QStringLiteral("V8Debugger"), 2, parent)
128 Q_D(QV8DebugService);
129 v8ServiceInstancePtr = this;
130 // wait for stateChanged() -> initialize()
131 d->initializeMutex.lock();
132 if (registerService() == Enabled) {
134 // ,block mode, client attached
135 while (!d->connectReceived) {
139 d->initializeMutex.unlock();
143 QV8DebugService::~QV8DebugService()
147 QV8DebugService *QV8DebugService::instance()
149 return v8ServiceInstance();
152 void QV8DebugService::initialize(const QV8Engine *engine)
154 // just make sure that the service is properly registered
155 v8ServiceInstance()->setEngine(engine);
158 void QV8DebugService::setEngine(const QV8Engine *engine)
160 Q_D(QV8DebugService);
165 void QV8DebugService::debugMessageHandler(const QString &message)
167 sendMessage(QV8DebugServicePrivate::packMessage(QLatin1String(V8_DEBUGGER_KEY_V8MESSAGE), message));
170 void QV8DebugService::signalEmitted(const QString &signal)
172 //This function is only called by QQmlBoundSignal
173 //only if there is a slot connected to the signal. Hence, there
174 //is no need for additional check.
175 Q_D(QV8DebugService);
177 //Parse just the name and remove the class info
178 //Normalize to Lower case.
179 QString signalName = signal.left(signal.indexOf(QLatin1String("("))).toLower();
181 foreach (const QString &signal, d->breakOnSignals) {
182 if (signal == signalName) {
183 scheduledDebugBreak(true);
189 // executed in the gui thread
190 void QV8DebugService::init()
192 Q_D(QV8DebugService);
193 v8::Debug::SetMessageHandler2(DebugMessageHandler);
194 v8::Debug::SetDebugMessageDispatchHandler(DebugMessageDispatchHandler);
195 QV4Compiler::enableV4(false);
196 d->initializeMutex.unlock();
199 // executed in the gui thread
200 void QV8DebugService::scheduledDebugBreak(bool schedule)
203 v8::Debug::DebugBreak();
205 v8::Debug::CancelDebugBreak();
208 // executed in the debugger thread
209 void QV8DebugService::stateChanged(QQmlDebugService::State newState)
211 Q_D(QV8DebugService);
212 if (newState == Enabled) {
213 // execute in GUI thread
214 d->initializeMutex.lock();
215 QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection);
219 // executed in the debugger thread
220 void QV8DebugService::messageReceived(const QByteArray &message)
222 Q_D(QV8DebugService);
224 QDataStream ds(message);
228 if (header == "V8DEBUG") {
231 ds >> command >> data;
233 if (command == V8_DEBUGGER_KEY_CONNECT) {
234 QMutexLocker locker(&d->initializeMutex);
235 d->connectReceived = true;
236 sendMessage(QV8DebugServicePrivate::packMessage(QLatin1String(V8_DEBUGGER_KEY_CONNECT)));
238 } else if (command == V8_DEBUGGER_KEY_INTERRUPT) {
239 // break has to be executed in gui thread
240 QMetaObject::invokeMethod(this, "scheduledDebugBreak", Qt::QueuedConnection, Q_ARG(bool, true));
241 sendMessage(QV8DebugServicePrivate::packMessage(QLatin1String(V8_DEBUGGER_KEY_INTERRUPT)));
243 } else if (command == V8_DEBUGGER_KEY_DISCONNECT) {
244 // cancel break has to be executed in gui thread
245 QMetaObject::invokeMethod(this, "scheduledDebugBreak", Qt::QueuedConnection, Q_ARG(bool, false));
246 sendDebugMessage(QString::fromUtf8(data));
248 } else if (command == V8_DEBUGGER_KEY_REQUEST) {
249 sendDebugMessage(QString::fromUtf8(data));
251 } else if (command == V8_DEBUGGER_KEY_BREAK_ON_SIGNAL) {
252 QDataStream rs(data);
255 rs >> signal >> enabled;
256 //Normalize to lower case.
257 QString signalName(QString::fromUtf8(signal).toLower());
259 d->breakOnSignals.append(signalName);
261 d->breakOnSignals.removeOne(signalName);
262 sendMessage(QV8DebugServicePrivate::packMessage(QLatin1String(V8_DEBUGGER_KEY_BREAK_ON_SIGNAL)));
268 void QV8DebugService::sendDebugMessage(const QString &message)
270 v8::Debug::SendCommand(message.utf16(), message.size());
273 void QV8DebugService::processDebugMessages()
275 Q_D(QV8DebugService);
276 v8::HandleScope handleScope;
277 v8::Context::Scope contextScope(d->engine->context());
278 v8::Debug::ProcessDebugMessages();
281 QByteArray QV8DebugServicePrivate::packMessage(const QString &type, const QString &message)
284 QDataStream rs(&reply, QIODevice::WriteOnly);
285 QByteArray cmd("V8DEBUG");
286 rs << cmd << type.toUtf8() << message.toUtf8();