QV8DebugService: Add breakaftercompile
[profile/ivi/qtdeclarative.git] / src / declarative / debugger / qv8debugservice.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
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.
17 **
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.
21 **
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.
29 **
30 ** Other Usage
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.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qv8debugservice_p.h"
43 #include "qdeclarativedebugservice_p_p.h"
44 #include <private/qjsconverter_impl_p.h>
45 #include <private/qv8engine_p.h>
46
47 #include <QtCore/QHash>
48 #include <QtCore/QFileInfo>
49 #include <QtCore/QMutex>
50
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";
58
59 QT_BEGIN_NAMESPACE
60
61 struct SignalHandlerData
62 {
63     QString functionName;
64     bool enabled;
65 };
66
67 Q_GLOBAL_STATIC(QV8DebugService, v8ServiceInstance)
68
69 // DebugMessageHandler will call back already when the QV8DebugService constructor is
70 // running, we therefore need a plain pointer.
71 static QV8DebugService *v8ServiceInstancePtr = 0;
72
73 void DebugMessageDispatchHandler()
74 {
75     QMetaObject::invokeMethod(v8ServiceInstancePtr, "processDebugMessages", Qt::QueuedConnection);
76 }
77
78 void DebugMessageHandler(const v8::Debug::Message& message)
79 {
80     v8::DebugEvent event = message.GetEvent();
81
82     if (event != v8::Break && event != v8::Exception &&
83             event != v8::AfterCompile && event != v8::BeforeCompile)
84             return;
85     v8ServiceInstancePtr->debugMessageHandler(QJSConverter::toString(message.GetJSON()), event);
86 }
87
88 class QV8DebugServicePrivate : public QDeclarativeDebugServicePrivate
89 {
90 public:
91     QV8DebugServicePrivate()
92         : connectReceived(false)
93         , breakAfterCompile(false)
94         , engine(0)
95     {
96     }
97
98     void initializeDebuggerThread();
99
100     static QByteArray packMessage(const QString &type, const QString &message = QString());
101
102     bool connectReceived;
103     bool breakAfterCompile;
104     QMutex initializeMutex;
105     QStringList breakOnSignals;
106     const QV8Engine *engine;
107 };
108
109 QV8DebugService::QV8DebugService(QObject *parent)
110     : QDeclarativeDebugService(*(new QV8DebugServicePrivate()),
111                                QLatin1String("V8Debugger"), parent)
112 {
113     Q_D(QV8DebugService);
114     v8ServiceInstancePtr = this;
115     // wait for statusChanged() -> initialize()
116     d->initializeMutex.lock();
117     if (registerService() == Enabled) {
118         init();
119         // ,block mode, client attached
120         while (!d->connectReceived) {
121             waitForMessage();
122         }
123     } else {
124         d->initializeMutex.unlock();
125     }
126 }
127
128 QV8DebugService::~QV8DebugService()
129 {
130 }
131
132 QV8DebugService *QV8DebugService::instance()
133 {
134     return v8ServiceInstance();
135 }
136
137 void QV8DebugService::initialize(const QV8Engine *engine)
138 {
139     // just make sure that the service is properly registered
140     v8ServiceInstance()->setEngine(engine);
141 }
142
143 void QV8DebugService::setEngine(const QV8Engine *engine)
144 {
145     Q_D(QV8DebugService);
146
147     d->engine = engine;
148 }
149
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)
160 {
161     Q_D(QV8DebugService);
162     sendMessage(QV8DebugServicePrivate::packMessage(QLatin1String(V8_DEBUGGER_KEY_V8MESSAGE), message));
163     if (event == v8::AfterCompile && d->breakAfterCompile)
164         scheduledDebugBreak(true);
165 }
166
167
168 void QV8DebugService::signalEmitted(const QString &signal)
169 {
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);
174
175     //Parse just the name and remove the class info
176     //Normalize to Lower case.
177     QString signalName = signal.left(signal.indexOf(QLatin1String("("))).toLower();
178
179     foreach (const QString &signal, d->breakOnSignals) {
180         if (signal == signalName) {
181             scheduledDebugBreak(true);
182             break;
183         }
184     }
185 }
186
187 // executed in the gui thread
188 void QV8DebugService::init()
189 {
190     Q_D(QV8DebugService);
191     v8::Debug::SetMessageHandler2(DebugMessageHandler);
192     v8::Debug::SetDebugMessageDispatchHandler(DebugMessageDispatchHandler);
193     d->initializeMutex.unlock();
194 }
195
196 // executed in the gui thread
197 void QV8DebugService::scheduledDebugBreak(bool schedule)
198 {
199     if (schedule)
200         v8::Debug::DebugBreak();
201     else
202         v8::Debug::CancelDebugBreak();
203 }
204
205 // executed in the debugger thread
206 void QV8DebugService::statusChanged(QDeclarativeDebugService::Status newStatus)
207 {
208     Q_D(QV8DebugService);
209     if (newStatus == Enabled) {
210         // execute in GUI thread
211         d->initializeMutex.lock();
212         QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection);
213     }
214 }
215
216
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)
228 {
229     Q_D(QV8DebugService);
230
231     QDataStream ds(message);
232     QByteArray command;
233     ds >> command;
234
235     if (command == "V8DEBUG") {
236         QByteArray type;
237         QByteArray data;
238         ds >> type >> data;
239
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)));
244
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)));
249
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));
254
255         } else if (type == V8_DEBUGGER_KEY_REQUEST) {
256             sendDebugMessage(QString::fromUtf8(data));
257
258         } else if (type == V8_DEBUGGER_KEY_BREAK_ON_SIGNAL) {
259             QDataStream rs(data);
260             QByteArray signal;
261             bool enabled;
262             rs >> signal >> enabled;
263              //Normalize to lower case.
264             QString signalName(QString::fromUtf8(signal).toLower());
265             if (enabled)
266                 d->breakOnSignals.append(signalName);
267             else
268                 d->breakOnSignals.removeOne(signalName);
269             sendMessage(QV8DebugServicePrivate::packMessage(QLatin1String(V8_DEBUGGER_KEY_BREAK_ON_SIGNAL)));
270
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)));
275         }
276     }
277 }
278
279 void QV8DebugService::sendDebugMessage(const QString &message)
280 {
281     v8::Debug::SendCommand(message.utf16(), message.size());
282 }
283
284 void QV8DebugService::processDebugMessages()
285 {
286     Q_D(QV8DebugService);
287     v8::HandleScope handleScope;
288     v8::Context::Scope contextScope(d->engine->context());
289     v8::Debug::ProcessDebugMessages();
290 }
291
292 QByteArray QV8DebugServicePrivate::packMessage(const QString &type, const QString &message)
293 {
294     QByteArray reply;
295     QDataStream rs(&reply, QIODevice::WriteOnly);
296     QByteArray cmd("V8DEBUG");
297     rs << cmd << type.toUtf8() << message.toUtf8();
298     return reply;
299 }
300
301 QT_END_NAMESPACE