1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qv8debugservice_p.h"
43 #include "qdeclarativedebugservice_p_p.h"
44 #include <private/qjsconverter_impl_p.h>
45 #include <private/qv8engine_p.h>
47 #include <QtCore/QHash>
48 #include <QtCore/QFileInfo>
49 #include <QtCore/QMutex>
51 const char *V8_DEBUGGER_KEY_CONNECT = "connect";
52 const char *V8_DEBUGGER_KEY_INTERRUPT = "interrupt";
53 const char *V8_DEBUGGER_KEY_DISCONNECT = "disconnect";
54 const char *V8_DEBUGGER_KEY_REQUEST = "v8request";
55 const char *V8_DEBUGGER_KEY_V8MESSAGE = "v8message";
56 const char *V8_DEBUGGER_KEY_BREAK_ON_SIGNAL = "breakonsignal";
57 const char *V8_DEBUGGER_KEY_BREAK_AFTER_COMPILE = "breakaftercompile";
61 struct SignalHandlerData
67 Q_GLOBAL_STATIC(QV8DebugService, v8ServiceInstance)
69 // DebugMessageHandler will call back already when the QV8DebugService constructor is
70 // running, we therefore need a plain pointer.
71 static QV8DebugService *v8ServiceInstancePtr = 0;
73 void DebugMessageDispatchHandler()
75 QMetaObject::invokeMethod(v8ServiceInstancePtr, "processDebugMessages", Qt::QueuedConnection);
78 void DebugMessageHandler(const v8::Debug::Message& message)
80 v8::DebugEvent event = message.GetEvent();
82 if (event != v8::Break && event != v8::Exception &&
83 event != v8::AfterCompile && event != v8::BeforeCompile)
85 v8ServiceInstancePtr->debugMessageHandler(QJSConverter::toString(message.GetJSON()), event);
88 class QV8DebugServicePrivate : public QDeclarativeDebugServicePrivate
91 QV8DebugServicePrivate()
92 : connectReceived(false)
93 , breakAfterCompile(false)
98 void initializeDebuggerThread();
100 static QByteArray packMessage(const QString &type, const QString &message = QString());
102 bool connectReceived;
103 bool breakAfterCompile;
104 QMutex initializeMutex;
105 QStringList breakOnSignals;
106 const QV8Engine *engine;
109 QV8DebugService::QV8DebugService(QObject *parent)
110 : QDeclarativeDebugService(*(new QV8DebugServicePrivate()),
111 QLatin1String("V8Debugger"), parent)
113 Q_D(QV8DebugService);
114 v8ServiceInstancePtr = this;
115 // wait for statusChanged() -> initialize()
116 d->initializeMutex.lock();
117 if (registerService() == Enabled) {
119 // ,block mode, client attached
120 while (!d->connectReceived) {
124 d->initializeMutex.unlock();
128 QV8DebugService::~QV8DebugService()
132 QV8DebugService *QV8DebugService::instance()
134 return v8ServiceInstance();
137 void QV8DebugService::initialize(const QV8Engine *engine)
139 // just make sure that the service is properly registered
140 v8ServiceInstance()->setEngine(engine);
143 void QV8DebugService::setEngine(const QV8Engine *engine)
145 Q_D(QV8DebugService);
150 //V8 DEBUG SERVICE PROTOCOL
151 // <HEADER><TYPE><DATA>
152 // <HEADER> : "V8DEBUG"
153 // <TYPE> : ("connect", "disconnect", "interrupt", "v8request", "v8message",
154 // "breakonsignal", "breakaftercompile")
155 // <DATA> : For _v8request_ and _v8message_ it is the JSON request string.
156 // For _breakonsignal_ it is <signalname_string><enabled_bool>
157 // For _breakaftercompile_ it is <enabled_bool>
158 // Empty string for the other types
159 void QV8DebugService::debugMessageHandler(const QString &message, const v8::DebugEvent &event)
161 Q_D(QV8DebugService);
162 sendMessage(QV8DebugServicePrivate::packMessage(QLatin1String(V8_DEBUGGER_KEY_V8MESSAGE), message));
163 if (event == v8::AfterCompile && d->breakAfterCompile)
164 scheduledDebugBreak(true);
168 void QV8DebugService::signalEmitted(const QString &signal)
170 //This function is only called by QDeclarativeBoundSignal
171 //only if there is a slot connected to the signal. Hence, there
172 //is no need for additional check.
173 Q_D(QV8DebugService);
175 //Parse just the name and remove the class info
176 //Normalize to Lower case.
177 QString signalName = signal.left(signal.indexOf(QLatin1String("("))).toLower();
179 foreach (const QString &signal, d->breakOnSignals) {
180 if (signal == signalName) {
181 scheduledDebugBreak(true);
187 // executed in the gui thread
188 void QV8DebugService::init()
190 Q_D(QV8DebugService);
191 v8::Debug::SetMessageHandler2(DebugMessageHandler);
192 v8::Debug::SetDebugMessageDispatchHandler(DebugMessageDispatchHandler);
193 d->initializeMutex.unlock();
196 // executed in the gui thread
197 void QV8DebugService::scheduledDebugBreak(bool schedule)
200 v8::Debug::DebugBreak();
202 v8::Debug::CancelDebugBreak();
205 // executed in the debugger thread
206 void QV8DebugService::statusChanged(QDeclarativeDebugService::Status newStatus)
208 Q_D(QV8DebugService);
209 if (newStatus == Enabled) {
210 // execute in GUI thread
211 d->initializeMutex.lock();
212 QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection);
217 //V8 DEBUG SERVICE PROTOCOL
218 // <HEADER><TYPE><DATA>
219 // <HEADER> : "V8DEBUG"
220 // <TYPE> : ("connect", "disconnect", "interrupt", "v8request", "v8message",
221 // "breakonsignal", "breakaftercompile")
222 // <DATA> : For _v8request_ and _v8message_ it is the JSON request string.
223 // For _breakonsignal_ it is <signalname_string><enabled_bool>
224 // For _breakaftercompile_ it is <enabled_bool>
225 // Empty string for the other types
226 // executed in the debugger thread
227 void QV8DebugService::messageReceived(const QByteArray &message)
229 Q_D(QV8DebugService);
231 QDataStream ds(message);
235 if (command == "V8DEBUG") {
240 if (type == V8_DEBUGGER_KEY_CONNECT) {
241 QMutexLocker locker(&d->initializeMutex);
242 d->connectReceived = true;
243 sendMessage(QV8DebugServicePrivate::packMessage(QLatin1String(V8_DEBUGGER_KEY_CONNECT)));
245 } else if (type == V8_DEBUGGER_KEY_INTERRUPT) {
246 // break has to be executed in gui thread
247 QMetaObject::invokeMethod(this, "scheduledDebugBreak", Qt::QueuedConnection, Q_ARG(bool, true));
248 sendMessage(QV8DebugServicePrivate::packMessage(QLatin1String(V8_DEBUGGER_KEY_INTERRUPT)));
250 } else if (type == V8_DEBUGGER_KEY_DISCONNECT) {
251 // cancel break has to be executed in gui thread
252 QMetaObject::invokeMethod(this, "scheduledDebugBreak", Qt::QueuedConnection, Q_ARG(bool, false));
253 sendDebugMessage(QString::fromUtf8(data));
255 } else if (type == V8_DEBUGGER_KEY_REQUEST) {
256 sendDebugMessage(QString::fromUtf8(data));
258 } else if (type == V8_DEBUGGER_KEY_BREAK_ON_SIGNAL) {
259 QDataStream rs(data);
262 rs >> signal >> enabled;
263 //Normalize to lower case.
264 QString signalName(QString::fromUtf8(signal).toLower());
266 d->breakOnSignals.append(signalName);
268 d->breakOnSignals.removeOne(signalName);
269 sendMessage(QV8DebugServicePrivate::packMessage(QLatin1String(V8_DEBUGGER_KEY_BREAK_ON_SIGNAL)));
271 } else if (type == V8_DEBUGGER_KEY_BREAK_AFTER_COMPILE) {
272 QDataStream rs(data);
273 rs >> d->breakAfterCompile;
274 sendMessage(QV8DebugServicePrivate::packMessage(QLatin1String(V8_DEBUGGER_KEY_BREAK_AFTER_COMPILE)));
279 void QV8DebugService::sendDebugMessage(const QString &message)
281 v8::Debug::SendCommand(message.utf16(), message.size());
284 void QV8DebugService::processDebugMessages()
286 Q_D(QV8DebugService);
287 v8::HandleScope handleScope;
288 v8::Context::Scope contextScope(d->engine->context());
289 v8::Debug::ProcessDebugMessages();
292 QByteArray QV8DebugServicePrivate::packMessage(const QString &type, const QString &message)
295 QDataStream rs(&reply, QIODevice::WriteOnly);
296 QByteArray cmd("V8DEBUG");
297 rs << cmd << type.toUtf8() << message.toUtf8();