Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / declarative / debugger / qv8debugservice.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
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 //V8 DEBUG SERVICE PROTOCOL
52 // <HEADER><COMMAND><DATA>
53 // <HEADER> : "V8DEBUG"
54 // <COMMAND> : ["connect", "disconnect", "interrupt",
55 //              "v8request", "v8message", "breakonsignal",
56 //              "breakaftercompile"]
57 // <DATA> : connect, disconnect, interrupt: empty
58 //          v8request, v8message: <JSONrequest_string>
59 //          breakonsignal: <signalname_string><enabled_bool>
60 //          breakaftercompile: <enabled_bool>
61
62 const char *V8_DEBUGGER_KEY_VERSION = "version";
63 const char *V8_DEBUGGER_KEY_CONNECT = "connect";
64 const char *V8_DEBUGGER_KEY_INTERRUPT = "interrupt";
65 const char *V8_DEBUGGER_KEY_DISCONNECT = "disconnect";
66 const char *V8_DEBUGGER_KEY_REQUEST = "v8request";
67 const char *V8_DEBUGGER_KEY_V8MESSAGE = "v8message";
68 const char *V8_DEBUGGER_KEY_BREAK_ON_SIGNAL = "breakonsignal";
69 const char *V8_DEBUGGER_KEY_BREAK_AFTER_COMPILE = "breakaftercompile";
70
71 QT_BEGIN_NAMESPACE
72
73 struct SignalHandlerData
74 {
75     QString functionName;
76     bool enabled;
77 };
78
79 Q_GLOBAL_STATIC(QV8DebugService, v8ServiceInstance)
80
81 // DebugMessageHandler will call back already when the QV8DebugService constructor is
82 // running, we therefore need a plain pointer.
83 static QV8DebugService *v8ServiceInstancePtr = 0;
84
85 void DebugMessageDispatchHandler()
86 {
87     QMetaObject::invokeMethod(v8ServiceInstancePtr, "processDebugMessages", Qt::QueuedConnection);
88 }
89
90 void DebugMessageHandler(const v8::Debug::Message& message)
91 {
92     v8::DebugEvent event = message.GetEvent();
93
94     if (event != v8::Break && event != v8::Exception &&
95             event != v8::AfterCompile && event != v8::BeforeCompile)
96             return;
97     v8ServiceInstancePtr->debugMessageHandler(QJSConverter::toString(message.GetJSON()), event);
98 }
99
100 class QV8DebugServicePrivate : public QDeclarativeDebugServicePrivate
101 {
102 public:
103     QV8DebugServicePrivate()
104         : connectReceived(false)
105         , breakAfterCompile(false)
106         , engine(0)
107     {
108     }
109
110     void initializeDebuggerThread();
111
112     static QByteArray packMessage(const QString &type, const QString &message = QString());
113
114     bool connectReceived;
115     bool breakAfterCompile;
116     QMutex initializeMutex;
117     QStringList breakOnSignals;
118     const QV8Engine *engine;
119 };
120
121 QV8DebugService::QV8DebugService(QObject *parent)
122     : QDeclarativeDebugService(*(new QV8DebugServicePrivate()),
123                                QLatin1String("V8Debugger"), 2, parent)
124 {
125     Q_D(QV8DebugService);
126     v8ServiceInstancePtr = this;
127     // wait for statusChanged() -> initialize()
128     d->initializeMutex.lock();
129     if (registerService() == Enabled) {
130         init();
131         // ,block mode, client attached
132         while (!d->connectReceived) {
133             waitForMessage();
134         }
135     } else {
136         d->initializeMutex.unlock();
137     }
138 }
139
140 QV8DebugService::~QV8DebugService()
141 {
142 }
143
144 QV8DebugService *QV8DebugService::instance()
145 {
146     return v8ServiceInstance();
147 }
148
149 void QV8DebugService::initialize(const QV8Engine *engine)
150 {
151     // just make sure that the service is properly registered
152     v8ServiceInstance()->setEngine(engine);
153 }
154
155 void QV8DebugService::setEngine(const QV8Engine *engine)
156 {
157     Q_D(QV8DebugService);
158
159     d->engine = engine;
160 }
161
162 void QV8DebugService::debugMessageHandler(const QString &message, const v8::DebugEvent &event)
163 {
164     Q_D(QV8DebugService);
165     sendMessage(QV8DebugServicePrivate::packMessage(QLatin1String(V8_DEBUGGER_KEY_V8MESSAGE), message));
166     if (event == v8::AfterCompile && d->breakAfterCompile)
167         scheduledDebugBreak(true);
168 }
169
170 void QV8DebugService::signalEmitted(const QString &signal)
171 {
172     //This function is only called by QDeclarativeBoundSignal
173     //only if there is a slot connected to the signal. Hence, there
174     //is no need for additional check.
175     Q_D(QV8DebugService);
176
177     //Parse just the name and remove the class info
178     //Normalize to Lower case.
179     QString signalName = signal.left(signal.indexOf(QLatin1String("("))).toLower();
180
181     foreach (const QString &signal, d->breakOnSignals) {
182         if (signal == signalName) {
183             scheduledDebugBreak(true);
184             break;
185         }
186     }
187 }
188
189 // executed in the gui thread
190 void QV8DebugService::init()
191 {
192     Q_D(QV8DebugService);
193     v8::Debug::SetMessageHandler2(DebugMessageHandler);
194     v8::Debug::SetDebugMessageDispatchHandler(DebugMessageDispatchHandler);
195     d->initializeMutex.unlock();
196 }
197
198 // executed in the gui thread
199 void QV8DebugService::scheduledDebugBreak(bool schedule)
200 {
201     if (schedule)
202         v8::Debug::DebugBreak();
203     else
204         v8::Debug::CancelDebugBreak();
205 }
206
207 // executed in the debugger thread
208 void QV8DebugService::statusChanged(QDeclarativeDebugService::Status newStatus)
209 {
210     Q_D(QV8DebugService);
211     if (newStatus == Enabled) {
212         // execute in GUI thread
213         d->initializeMutex.lock();
214         QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection);
215     }
216 }
217
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 header;
225     ds >> header;
226
227     if (header == "V8DEBUG") {
228         QByteArray command;
229         QByteArray data;
230         ds >> command >> data;
231
232         if (command == 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 (command == 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 (command == 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 (command == V8_DEBUGGER_KEY_REQUEST) {
248             sendDebugMessage(QString::fromUtf8(data));
249
250         } else if (command == 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         } else if (command == V8_DEBUGGER_KEY_BREAK_AFTER_COMPILE) {
264             QDataStream rs(data);
265             rs >> d->breakAfterCompile;
266             sendMessage(QV8DebugServicePrivate::packMessage(QLatin1String(V8_DEBUGGER_KEY_BREAK_AFTER_COMPILE)));
267
268         }
269     }
270 }
271
272 void QV8DebugService::sendDebugMessage(const QString &message)
273 {
274     v8::Debug::SendCommand(message.utf16(), message.size());
275 }
276
277 void QV8DebugService::processDebugMessages()
278 {
279     Q_D(QV8DebugService);
280     v8::HandleScope handleScope;
281     v8::Context::Scope contextScope(d->engine->context());
282     v8::Debug::ProcessDebugMessages();
283 }
284
285 QByteArray QV8DebugServicePrivate::packMessage(const QString &type, const QString &message)
286 {
287     QByteArray reply;
288     QDataStream rs(&reply, QIODevice::WriteOnly);
289     QByteArray cmd("V8DEBUG");
290     rs << cmd << type.toUtf8() << message.toUtf8();
291     return reply;
292 }
293
294 QT_END_NAMESPACE