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 "qv8debug_p.h"
45 #include "qv8engine_p.h"
46 #include "qdeclarativeengine_p.h"
48 #include <QtCore/QEventLoop>
49 #include <QtCore/QHash>
50 #include <QtCore/QFileInfo>
54 Q_GLOBAL_STATIC(QV8DebugService, v8ServiceInstance)
56 void DebugMessageHandler(const v8::Debug::Message& message)
58 v8::DebugEvent event = message.GetEvent();
60 if (event != v8::Break && event != v8::Exception &&
61 event != v8::AfterCompile && event != v8::BeforeCompile) {
65 const QString response(QV8Engine::toStringStatic(
68 QV8DebugService *service = QV8DebugService::instance();
69 service->debugMessageHandler(response);
71 if ((event == v8::Break || event == v8::Exception) &&
72 !message.WillStartRunning()) {
73 service->executionStopped();
74 } else if (event == v8::AfterCompile) {
75 service->appendSourcePath(response);
76 } //TODO::v8::Exception
79 class QV8DebugServicePrivate : public QDeclarativeDebugServicePrivate
82 QV8DebugServicePrivate()
85 //Create a new isolate
86 isolate = v8::Isolate::New();
88 //Enter the isolate and initialize
89 v8::Isolate::Scope i_scope(isolate);
92 //Create an instance in the new isolate
93 engine = new QJSEngine();
96 ~QV8DebugServicePrivate()
102 void sendDebugMessage(const QString &message);
103 static QByteArray packMessage(const QString &message);
107 v8::Isolate *isolate;
108 QList<QDeclarativeEngine *> engines;
110 QHash<QString,QString> sourcePath;
111 QHash<QString,QString> requestCache;
112 QHash<int,QString> eventList;
115 QV8DebugService::QV8DebugService(QObject *parent)
116 : QDeclarativeDebugService(*(new QV8DebugServicePrivate()),
117 QLatin1String("V8Debugger"), parent)
119 Q_D(QV8DebugService);
120 v8::Debug::SetMessageHandler2(DebugMessageHandler);
121 if (status() == Enabled) {
122 // ,block mode, client attached
123 while (!d->initialized) {
129 QV8DebugService::~QV8DebugService()
133 QV8DebugService *QV8DebugService::instance()
135 return v8ServiceInstance();
138 void QV8DebugService::addEngine(QDeclarativeEngine *engine)
140 Q_D(QV8DebugService);
142 Q_ASSERT(!d->engines.contains(engine));
144 d->engines.append(engine);
147 void QV8DebugService::removeEngine(QDeclarativeEngine *engine)
149 Q_D(QV8DebugService);
151 Q_ASSERT(d->engines.contains(engine));
153 d->engines.removeAll(engine);
156 void QV8DebugService::debugMessageHandler(const QString &message)
158 sendMessage(QV8DebugServicePrivate::packMessage(message));
161 void QV8DebugService::executionStopped()
163 Q_D(QV8DebugService);
165 if (!d->loop.isRunning()) {
166 d->loop.exec(QEventLoop::ExcludeUserInputEvents);
170 void QV8DebugService::appendSourcePath(const QString &message)
172 Q_D(QV8DebugService);
175 /* Parse the byte string in a separate isolate
176 This will ensure that the debug message handler does not
177 receive any messages related to this operation */
179 v8::Isolate::Scope i_scope(d->isolate);
180 QJSValue parser = d->engine->evaluate(QLatin1String("JSON.parse"));
181 QJSValue out = parser.call(QJSValue(), QJSValueList() << QJSValue(message));
182 msgMap = out.toVariant().toMap();
185 const QString sourcePath(msgMap.value(QLatin1String("body")).toMap().value(
186 QLatin1String("script")).toMap().value(
187 QLatin1String("name")).toString());
188 const QString fileName(QFileInfo(sourcePath).fileName());
190 d->sourcePath.insert(fileName, sourcePath);
192 //Check if there are any pending breakpoint requests for this file
193 if (d->requestCache.contains(fileName)) {
194 QList<QString> list = d->requestCache.values(fileName);
195 d->requestCache.remove(fileName);
196 foreach (QString request, list) {
197 request.replace(fileName, sourcePath);
198 d->sendDebugMessage(request);
203 void QV8DebugService::signalEmitted(const QString &signal)
205 //This function is only called by QDeclarativeBoundSignal
206 //only if there is a slot connected to the signal. Hence, there
207 //is no need for additional check.
208 Q_D(QV8DebugService);
210 //Parse just the name and remove the class info
211 if (d->eventList.key(signal.left(signal.indexOf(QLatin1String("("))))) {
212 v8::Debug::DebugBreak();
216 void QV8DebugService::messageReceived(const QByteArray &message)
218 Q_D(QV8DebugService);
220 QDataStream ds(message);
224 if (command == "V8DEBUG") {
227 QByteArray requestArray;
229 request = QString::fromUtf8(requestArray);
233 /* Parse the byte string in a separate isolate
234 This will ensure that the debug message handler does not
235 receive any messages related to this operation */
237 v8::Isolate::Scope i_scope(d->isolate);
238 QJSValue parser = d->engine->evaluate(QLatin1String("JSON.parse"));
239 QJSValue out = parser.call(QJSValue(), QJSValueList() << QJSValue(request));
240 reqMap = out.toVariant().toMap();
243 const QString debugCommand(reqMap.value(QLatin1String("command")).toString());
245 if (debugCommand == QLatin1String("connect")) {
246 d->initialized = true;
248 } else if (debugCommand == QLatin1String("interrupt")) {
249 v8::Debug::DebugBreak();
252 bool forwardRequestToV8 = true;
254 if (debugCommand == QLatin1String("setbreakpoint")) {
255 const QVariantMap arguments = reqMap.value(QLatin1String("arguments")).toMap();
256 const QString type(arguments.value(QLatin1String("type")).toString());
258 if (type == QLatin1String("script")) {
259 QString fileName(arguments.value(QLatin1String("target")).toString());
261 //Check if the filepath has been cached
262 if (d->sourcePath.contains(fileName)) {
263 QString filePath = d->sourcePath.value(fileName);
264 request.replace(fileName, filePath);
266 //Store the setbreakpoint message till filepath is resolved
267 d->requestCache.insertMulti(fileName, request);
268 forwardRequestToV8 = false;
270 } else if (type == QLatin1String("event")) {
271 //Do not send this request to v8
272 forwardRequestToV8 = false;
274 //Prepare the response string
275 //Create a json message using v8 debugging protocol
276 //and send it to client
278 // { "seq" : <number>,
279 // "type" : "response",
280 // "request_seq" : <number>,
281 // "command" : "setbreakpoint",
282 // "body" : { "type" : <"function" or "script">
283 // "breakpoint" : <break point number of the new break point>
285 // "running" : <is the VM running after sending this response>
289 v8::Isolate::Scope(d->isolate);
290 const QString obj(QLatin1String("{}"));
291 QJSValue parser = d->engine->evaluate(QLatin1String("JSON.parse"));
292 QJSValue jsonVal = parser.call(QJSValue(), QJSValueList() << obj);
293 jsonVal.setProperty(QLatin1String("type"), QJSValue(QLatin1String("response")));
295 const int sequence = reqMap.value(QLatin1String("seq")).toInt();
296 jsonVal.setProperty(QLatin1String("request_seq"), QJSValue(sequence));
297 jsonVal.setProperty(QLatin1String("command"), QJSValue(debugCommand));
299 //Check that the function starts with 'on'
300 QString eventName(arguments.value(QLatin1String("target")).toString());
303 if (eventName.startsWith(QLatin1String("on"))) {
304 d->eventList.insert(-sequence, eventName.remove(0,2).toLower());
306 QJSValue args = parser.call(QJSValue(), QJSValueList() << obj);
308 args.setProperty(QLatin1String("type"), QJSValue(QLatin1String("event")));
309 args.setProperty(QLatin1String("breakpoint"), QJSValue(-sequence));
311 jsonVal.setProperty(QLatin1String("body"), args);
312 jsonVal.setProperty(QLatin1String("success"), QJSValue(true));
315 jsonVal.setProperty(QLatin1String("success"), QJSValue(false));
319 jsonVal.setProperty(QLatin1String("running"), QJSValue(!d->loop.isRunning()));
321 QJSValue stringify = d->engine->evaluate(QLatin1String("JSON.stringify"));
322 QJSValue json = stringify.call(QJSValue(), QJSValueList() << jsonVal);
323 debugMessageHandler(json.toString());
327 } else if (debugCommand == QLatin1String("clearbreakpoint")) {
328 //check if the breakpoint is a negative integer (event breakpoint)
329 const QVariantMap arguments = reqMap.value(QLatin1String("arguments")).toMap();
330 const int bp = arguments.value(QLatin1String("breakpoint")).toInt();
333 d->eventList.remove(bp);
334 forwardRequestToV8 = false;
337 if (forwardRequestToV8)
338 d->sendDebugMessage(request);
342 QDeclarativeDebugService::messageReceived(message);
345 void QV8DebugServicePrivate::sendDebugMessage(const QString &message)
347 if (loop.isRunning())
350 v8::Debug::SendCommand(message.utf16(), message.size());
353 QByteArray QV8DebugServicePrivate::packMessage(const QString &message)
356 QDataStream rs(&reply, QIODevice::WriteOnly);
357 QByteArray cmd("V8DEBUG");
358 rs << cmd << message.toUtf8();