39c991ff77bdd73b2d5c5b05dd9e2040d0b6ae92
[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/qv8debug_p.h>
45 #include <private/qjsconverter_impl_p.h>
46 #include <private/qv8engine_p.h>
47
48 #include <QtCore/QHash>
49 #include <QtCore/QFileInfo>
50 #include <QtCore/QMutex>
51
52 const char *V8_DEBUGGER_KEY_CONNECT = "connect";
53 const char *V8_DEBUGGER_KEY_INTERRUPT = "interrupt";
54 const char *V8_DEBUGGER_KEY_DISCONNECT = "disconnect";
55 const char *V8_DEBUGGER_KEY_REQUEST = "v8request";
56 const char *V8_DEBUGGER_KEY_V8MESSAGE = "v8message";
57 const char *V8_DEBUGGER_KEY_BREAK_ON_SIGNAL = "breakonsignal";
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()));
86 }
87
88 class QV8DebugServicePrivate : public QDeclarativeDebugServicePrivate
89 {
90 public:
91     QV8DebugServicePrivate()
92         : connectReceived(false)
93         , engine(0)
94     {
95     }
96
97     void initializeDebuggerThread();
98
99     static QByteArray packMessage(const QString &type, const QString &message = QString());
100
101     bool connectReceived;
102
103     QMutex initializeMutex;
104     QStringList breakOnSignals;
105     const QV8Engine *engine;
106 };
107
108 QV8DebugService::QV8DebugService(QObject *parent)
109     : QDeclarativeDebugService(*(new QV8DebugServicePrivate()),
110                                QLatin1String("V8Debugger"), parent)
111 {
112     Q_D(QV8DebugService);
113     v8ServiceInstancePtr = this;
114     // wait for statusChanged() -> initialize()
115     d->initializeMutex.lock();
116     if (registerService() == Enabled) {
117         init();
118         // ,block mode, client attached
119         while (!d->connectReceived) {
120             waitForMessage();
121         }
122     } else {
123         d->initializeMutex.unlock();
124     }
125 }
126
127 QV8DebugService::~QV8DebugService()
128 {
129 }
130
131 QV8DebugService *QV8DebugService::instance()
132 {
133     return v8ServiceInstance();
134 }
135
136 void QV8DebugService::initialize(const QV8Engine *engine)
137 {
138     // just make sure that the service is properly registered
139     v8ServiceInstance()->setEngine(engine);
140 }
141
142 void QV8DebugService::setEngine(const QV8Engine *engine)
143 {
144     Q_D(QV8DebugService);
145
146     d->engine = engine;
147 }
148
149 //V8 DEBUG SERVICE PROTOCOL
150 // <HEADER><TYPE><DATA>
151 // <HEADER> : "V8DEBUG"
152 // <TYPE> : ("connect", "disconnect", "interrupt", "v8request", "v8message", "breakonsignal")
153 // <DATA> : For _v8request_ and _v8message_ it is the JSON request string.
154 //          For _breakonsignal_ it is <signalname_string><enabled_bool>
155 //          Empty string for the other types
156 void QV8DebugService::debugMessageHandler(const QString &message)
157 {
158     sendMessage(QV8DebugServicePrivate::packMessage(QLatin1String(V8_DEBUGGER_KEY_V8MESSAGE), message));
159 }
160
161
162 void QV8DebugService::signalEmitted(const QString &signal)
163 {
164     //This function is only called by QDeclarativeBoundSignal
165     //only if there is a slot connected to the signal. Hence, there
166     //is no need for additional check.
167     Q_D(QV8DebugService);
168
169     //Parse just the name and remove the class info
170     //Normalize to Lower case.
171     QString signalName = signal.left(signal.indexOf(QLatin1String("("))).toLower();
172
173     foreach (const QString &signal, d->breakOnSignals) {
174         if (signal == signalName) {
175             scheduledDebugBreak(true);
176             break;
177         }
178     }
179 }
180
181 // executed in the gui thread
182 void QV8DebugService::init()
183 {
184     Q_D(QV8DebugService);
185     v8::Debug::SetMessageHandler2(DebugMessageHandler);
186     v8::Debug::SetDebugMessageDispatchHandler(DebugMessageDispatchHandler);
187     d->initializeMutex.unlock();
188 }
189
190 // executed in the gui thread
191 void QV8DebugService::scheduledDebugBreak(bool schedule)
192 {
193     if (schedule)
194         v8::Debug::DebugBreak();
195     else
196         v8::Debug::CancelDebugBreak();
197 }
198
199 // executed in the debugger thread
200 void QV8DebugService::statusChanged(QDeclarativeDebugService::Status newStatus)
201 {
202     Q_D(QV8DebugService);
203     if (newStatus == Enabled) {
204         // execute in GUI thread
205         d->initializeMutex.lock();
206         QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection);
207     }
208 }
209
210
211 //V8 DEBUG SERVICE PROTOCOL
212 // <HEADER><TYPE><DATA>
213 // <HEADER> : "V8DEBUG"
214 // <TYPE> : ("connect", "disconnect", "interrupt", "v8request", "v8message", "breakonsignal")
215 // <DATA> : For _v8request_ and _v8message_ it is the JSON request string.
216 //          For _breakonsignal_ it is <signalname_string><enabled_bool>
217 //          Empty string for the other types
218 // executed in the debugger thread
219 void QV8DebugService::messageReceived(const QByteArray &message)
220 {
221     Q_D(QV8DebugService);
222
223     QDataStream ds(message);
224     QByteArray command;
225     ds >> command;
226
227     if (command == "V8DEBUG") {
228         QByteArray type;
229         QByteArray data;
230         ds >> type >> data;
231
232         if (type == V8_DEBUGGER_KEY_CONNECT) {
233             QMutexLocker locker(&d->initializeMutex);
234             d->connectReceived = true;
235             sendMessage(QV8DebugServicePrivate::packMessage(QLatin1String(V8_DEBUGGER_KEY_CONNECT)));
236
237         } else if (type == V8_DEBUGGER_KEY_INTERRUPT) {
238             // break has to be executed in gui thread
239             QMetaObject::invokeMethod(this, "scheduledDebugBreak", Qt::QueuedConnection, Q_ARG(bool, true));
240             sendMessage(QV8DebugServicePrivate::packMessage(QLatin1String(V8_DEBUGGER_KEY_INTERRUPT)));
241
242         } else if (type == V8_DEBUGGER_KEY_DISCONNECT) {
243             // cancel break has to be executed in gui thread
244             QMetaObject::invokeMethod(this, "scheduledDebugBreak", Qt::QueuedConnection, Q_ARG(bool, false));
245             sendDebugMessage(QString::fromUtf8(data));
246
247         } else if (type == V8_DEBUGGER_KEY_REQUEST) {
248             sendDebugMessage(QString::fromUtf8(data));
249
250         } else if (type == V8_DEBUGGER_KEY_BREAK_ON_SIGNAL) {
251             QDataStream rs(data);
252             QByteArray signal;
253             bool enabled;
254             rs >> signal >> enabled;
255              //Normalize to lower case.
256             QString signalName(QString::fromUtf8(signal).toLower());
257             if (enabled)
258                 d->breakOnSignals.append(signalName);
259             else
260                 d->breakOnSignals.removeOne(signalName);
261             sendMessage(QV8DebugServicePrivate::packMessage(QLatin1String(V8_DEBUGGER_KEY_BREAK_ON_SIGNAL)));
262         }
263     }
264 }
265
266 void QV8DebugService::sendDebugMessage(const QString &message)
267 {
268     v8::Debug::SendCommand(message.utf16(), message.size());
269 }
270
271 void QV8DebugService::processDebugMessages()
272 {
273     Q_D(QV8DebugService);
274     v8::HandleScope handleScope;
275     v8::Context::Scope contextScope(d->engine->context());
276     v8::Debug::ProcessDebugMessages();
277 }
278
279 QByteArray QV8DebugServicePrivate::packMessage(const QString &type, const QString &message)
280 {
281     QByteArray reply;
282     QDataStream rs(&reply, QIODevice::WriteOnly);
283     QByteArray cmd("V8DEBUG");
284     rs << cmd << type.toUtf8() << message.toUtf8();
285     return reply;
286 }
287
288 QT_END_NAMESPACE