OSDN Git Service

Refactoring v8debuggerclient and scriptdebuggerclient
[qt-creator-jp/qt-creator-jp.git] / src / plugins / debugger / qml / qscriptdebuggerclient.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at info@qt.nokia.com.
30 **
31 **************************************************************************/
32 #include "qscriptdebuggerclient.h"
33
34 #include "watchdata.h"
35 #include "watchhandler.h"
36 #include "breakpoint.h"
37 #include "breakhandler.h"
38 #include "debuggerconstants.h"
39 #include "qmlengine.h"
40 #include "stackhandler.h"
41 #include "debuggercore.h"
42
43 #include <QTextDocument>
44 #include <QFileInfo>
45 #include <QMessageBox>
46 #include <extensionsystem/pluginmanager.h>
47 #include <utils/qtcassert.h>
48
49 namespace Debugger {
50 namespace Internal {
51
52 struct JSAgentBreakpointData
53 {
54     QByteArray functionName;
55     QByteArray fileUrl;
56     qint32 lineNumber;
57 };
58
59 struct JSAgentStackData
60 {
61     QByteArray functionName;
62     QByteArray fileUrl;
63     qint32 lineNumber;
64 };
65
66 uint qHash(const JSAgentBreakpointData &b)
67 {
68     return b.lineNumber ^ qHash(b.fileUrl);
69 }
70
71 QDataStream &operator<<(QDataStream &s, const JSAgentBreakpointData &data)
72 {
73     return s << data.functionName << data.fileUrl << data.lineNumber;
74 }
75
76 QDataStream &operator<<(QDataStream &s, const JSAgentStackData &data)
77 {
78     return s << data.functionName << data.fileUrl << data.lineNumber;
79 }
80
81 QDataStream &operator>>(QDataStream &s, JSAgentBreakpointData &data)
82 {
83     return s >> data.functionName >> data.fileUrl >> data.lineNumber;
84 }
85
86 QDataStream &operator>>(QDataStream &s, JSAgentStackData &data)
87 {
88     return s >> data.functionName >> data.fileUrl >> data.lineNumber;
89 }
90
91 bool operator==(const JSAgentBreakpointData &b1, const JSAgentBreakpointData &b2)
92 {
93     return b1.lineNumber == b2.lineNumber && b1.fileUrl == b2.fileUrl;
94 }
95
96 typedef QSet<JSAgentBreakpointData> JSAgentBreakpoints;
97 typedef QList<JSAgentStackData> JSAgentStackFrames;
98
99
100 static QDataStream &operator>>(QDataStream &s, WatchData &data)
101 {
102     data = WatchData();
103     QByteArray name;
104     QByteArray value;
105     QByteArray type;
106     bool hasChildren = false;
107     s >> data.exp >> name >> value >> type >> hasChildren >> data.id;
108     data.name = QString::fromUtf8(name);
109     data.setType(type, false);
110     data.setValue(QString::fromUtf8(value));
111     data.setHasChildren(hasChildren);
112     data.setAllUnneeded();
113     return s;
114 }
115
116 class QScriptDebuggerClientPrivate
117 {
118 public:
119     explicit QScriptDebuggerClientPrivate(QScriptDebuggerClient *) :
120         ping(0), engine(0)
121     {
122
123     }
124
125     int ping;
126     QmlEngine *engine;
127     JSAgentBreakpoints breakpoints;
128 };
129
130 QScriptDebuggerClient::QScriptDebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection* client)
131     : QmlDebuggerClient(client, QLatin1String("JSDebugger")),
132       d(new QScriptDebuggerClientPrivate(this))
133 {
134 }
135
136 QScriptDebuggerClient::~QScriptDebuggerClient()
137 {
138     delete d;
139 }
140
141 void QScriptDebuggerClient::executeStep()
142 {
143     QByteArray reply;
144     QDataStream rs(&reply, QIODevice::WriteOnly);
145     QByteArray cmd = "STEPINTO";
146     rs << cmd;
147     sendMessage(reply);
148 }
149
150 void QScriptDebuggerClient::executeStepOut()
151 {
152     QByteArray reply;
153     QDataStream rs(&reply, QIODevice::WriteOnly);
154     QByteArray cmd = "STEPOUT";
155     rs << cmd;
156     sendMessage(reply);
157 }
158
159 void QScriptDebuggerClient::executeNext()
160 {
161     QByteArray reply;
162     QDataStream rs(&reply, QIODevice::WriteOnly);
163     QByteArray cmd = "STEPOVER";
164     rs << cmd;
165     sendMessage(reply);
166 }
167
168 void QScriptDebuggerClient::executeStepI()
169 {
170     QByteArray reply;
171     QDataStream rs(&reply, QIODevice::WriteOnly);
172     QByteArray cmd = "STEPINTO";
173     rs << cmd;
174     sendMessage(reply);
175 }
176
177 void QScriptDebuggerClient::continueInferior()
178 {
179     QByteArray reply;
180     QDataStream rs(&reply, QIODevice::WriteOnly);
181     QByteArray cmd = "CONTINUE";
182     rs << cmd;
183     sendMessage(reply);
184 }
185
186 void QScriptDebuggerClient::interruptInferior()
187 {
188     QByteArray reply;
189     QDataStream rs(&reply, QIODevice::WriteOnly);
190     QByteArray cmd = "INTERRUPT";
191     rs << cmd;
192     sendMessage(reply);
193 }
194
195 void QScriptDebuggerClient::connect()
196 {
197 }
198
199 void QScriptDebuggerClient::disconnect()
200 {
201 }
202
203 void QScriptDebuggerClient::activateFrame(int index)
204 {
205     QByteArray reply;
206     QDataStream rs(&reply, QIODevice::WriteOnly);
207     QByteArray cmd = "ACTIVATE_FRAME";
208     rs << cmd
209        << index;
210     sendMessage(reply);
211 }
212
213 void QScriptDebuggerClient::insertBreakpoint(BreakpointModelId id)
214 {
215     BreakHandler *handler = d->engine->breakHandler();
216     JSAgentBreakpointData bp;
217     bp.fileUrl = QUrl::fromLocalFile(handler->fileName(id)).toString().toUtf8();
218     bp.lineNumber = handler->lineNumber(id);
219     bp.functionName = handler->functionName(id).toUtf8();
220     d->breakpoints.insert(bp);
221 }
222
223 void QScriptDebuggerClient::removeBreakpoint(BreakpointModelId id)
224 {
225     BreakHandler *handler = d->engine->breakHandler();
226     JSAgentBreakpointData bp;
227     bp.fileUrl = QUrl::fromLocalFile(handler->fileName(id)).toString().toUtf8();
228     bp.lineNumber = handler->lineNumber(id);
229     bp.functionName = handler->functionName(id).toUtf8();
230     d->breakpoints.remove(bp);
231 }
232
233 void QScriptDebuggerClient::changeBreakpoint(BreakpointModelId /*id*/)
234 {
235 }
236
237 void QScriptDebuggerClient::updateBreakpoints()
238 {
239     QByteArray reply;
240     QDataStream rs(&reply, QIODevice::WriteOnly);
241     QByteArray cmd = "BREAKPOINTS";
242     rs << cmd
243        << d->breakpoints;
244     sendMessage(reply);
245 }
246
247 void QScriptDebuggerClient::assignValueInDebugger(const QByteArray expr, const quint64 &id,
248                                                   const QString &property, const QString value)
249 {
250     QByteArray reply;
251     QDataStream rs(&reply, QIODevice::WriteOnly);
252     QByteArray cmd = "SET_PROPERTY";
253     rs << cmd;
254     rs << expr << id << property << value;
255     sendMessage(reply);
256 }
257
258 void QScriptDebuggerClient::updateWatchData(const WatchData *data)
259 {
260     QByteArray reply;
261     QDataStream rs(&reply, QIODevice::WriteOnly);
262     QByteArray cmd = "EXEC";
263     rs << cmd;
264     rs << data->iname << data->name;
265     sendMessage(reply);
266 }
267
268 void QScriptDebuggerClient::executeDebuggerCommand(const QString &command)
269 {
270     QByteArray reply;
271     QDataStream rs(&reply, QIODevice::WriteOnly);
272     QByteArray cmd = "EXEC";
273     QByteArray console = "console";
274     rs << cmd << console << command;
275     sendMessage(reply);
276 }
277
278 void QScriptDebuggerClient::synchronizeWatchers(const QStringList &watchers)
279 {
280     // send watchers list
281     QByteArray reply;
282     QDataStream rs(&reply, QIODevice::WriteOnly);
283     QByteArray cmd = "WATCH_EXPRESSIONS";
284     rs << cmd;
285     rs << watchers;
286     sendMessage(reply);
287 }
288
289 void QScriptDebuggerClient::expandObject(const QByteArray &iname, quint64 objectId)
290 {
291     QByteArray reply;
292     QDataStream rs(&reply, QIODevice::WriteOnly);
293     QByteArray cmd = "EXPAND";
294     rs << cmd;
295     rs << iname << objectId;
296     sendMessage(reply);
297 }
298
299 void QScriptDebuggerClient::sendPing()
300 {
301     d->ping++;
302     QByteArray reply;
303     QDataStream rs(&reply, QIODevice::WriteOnly);
304     QByteArray cmd = "PING";
305     rs << cmd;
306     rs << d->ping;
307     sendMessage(reply);
308 }
309
310 void QScriptDebuggerClient::messageReceived(const QByteArray &data)
311 {
312     QByteArray rwData = data;
313     QDataStream stream(&rwData, QIODevice::ReadOnly);
314
315     QByteArray command;
316     stream >> command;
317
318     if (command == "STOPPED") {
319         d->engine->inferiorSpontaneousStop();
320
321         QString logString = QString::fromLatin1(command);
322
323         JSAgentStackFrames stackFrames;
324         QList<WatchData> watches;
325         QList<WatchData> locals;
326         stream >> stackFrames >> watches >> locals;
327
328         logString += QString::fromLatin1(" (%1 stack frames) (%2 watches)  (%3 locals)").
329                 arg(stackFrames.size()).arg(watches.size()).arg(locals.size());
330
331         StackFrames ideStackFrames;
332         for (int i = 0; i != stackFrames.size(); ++i) {
333             StackFrame frame;
334             frame.line = stackFrames.at(i).lineNumber;
335             frame.function = stackFrames.at(i).functionName;
336             frame.file = d->engine->toFileInProject(QUrl(stackFrames.at(i).fileUrl));
337             frame.usable = QFileInfo(frame.file).isReadable();
338             frame.level = i + 1;
339             ideStackFrames << frame;
340         }
341
342         if (ideStackFrames.size() && ideStackFrames.back().function == "<global>")
343             ideStackFrames.takeLast();
344         d->engine->stackHandler()->setFrames(ideStackFrames);
345
346         d->engine->watchHandler()->beginCycle();
347         bool needPing = false;
348
349         foreach (WatchData data, watches) {
350             data.iname = d->engine->watchHandler()->watcherName(data.exp);
351             d->engine->watchHandler()->insertData(data);
352
353             if (d->engine->watchHandler()->expandedINames().contains(data.iname)) {
354                 needPing = true;
355                 expandObject(data.iname,data.id);
356             }
357         }
358
359         foreach (WatchData data, locals) {
360             data.iname = "local." + data.exp;
361             d->engine->watchHandler()->insertData(data);
362
363             if (d->engine->watchHandler()->expandedINames().contains(data.iname)) {
364                 needPing = true;
365                 expandObject(data.iname,data.id);
366             }
367         }
368
369         if (needPing) {
370             sendPing();
371         } else {
372             d->engine->watchHandler()->endCycle();
373         }
374
375         bool becauseOfException;
376         stream >> becauseOfException;
377
378         logString += becauseOfException ? " exception" : " no_exception";
379
380         if (becauseOfException) {
381             QString error;
382             stream >> error;
383
384             logString += QLatin1Char(' ');
385             logString += error;
386             d->engine->logMessage(QmlEngine::LogReceive, logString);
387
388             QString msg = stackFrames.isEmpty()
389                     ? tr("<p>An uncaught exception occurred:</p><p>%1</p>")
390                       .arg(Qt::escape(error))
391                     : tr("<p>An uncaught exception occurred in <i>%1</i>:</p><p>%2</p>")
392                       .arg(stackFrames.value(0).fileUrl, Qt::escape(error));
393             showMessageBox(QMessageBox::Information, tr("Uncaught Exception"), msg);
394         } else {
395             //
396             // Make breakpoint non-pending
397             //
398             QString file;
399             QString function;
400             int line = -1;
401
402             if (!ideStackFrames.isEmpty()) {
403                 file = ideStackFrames.at(0).file;
404                 line = ideStackFrames.at(0).line;
405                 function = ideStackFrames.at(0).function;
406             }
407
408             BreakHandler *handler = d->engine->breakHandler();
409             foreach (BreakpointModelId id, handler->engineBreakpointIds(d->engine)) {
410                 QString processedFilename = handler->fileName(id);
411                 if (processedFilename == file && handler->lineNumber(id) == line) {
412                     QTC_ASSERT(handler->state(id) == BreakpointInserted,/**/);
413                     BreakpointResponse br = handler->response(id);
414                     br.fileName = file;
415                     br.lineNumber = line;
416                     br.functionName = function;
417                     handler->setResponse(id, br);
418                 }
419             }
420
421             d->engine->logMessage(QmlEngine::LogReceive, logString);
422         }
423
424         if (!ideStackFrames.isEmpty())
425             d->engine->gotoLocation(ideStackFrames.value(0));
426
427     } else if (command == "RESULT") {
428         WatchData data;
429         QByteArray iname;
430         stream >> iname >> data;
431
432         d->engine->logMessage(QmlEngine::LogReceive, QString("%1 %2 %3").arg(QString(command),
433                                                                              QString(iname), QString(data.value)));
434         data.iname = iname;
435         if (iname.startsWith("watch.")) {
436             d->engine->watchHandler()->insertData(data);
437         } else if (iname == "console") {
438             d->engine->showMessage(data.value, ScriptConsoleOutput);
439         } else {
440             qWarning() << "QmlEngine: Unexcpected result: " << iname << data.value;
441         }
442     } else if (command == "EXPANDED") {
443         QList<WatchData> result;
444         QByteArray iname;
445         stream >> iname >> result;
446
447         d->engine->logMessage(QmlEngine::LogReceive, QString("%1 %2 (%3 x watchdata)").arg(
448                                   QString(command), QString(iname), QString::number(result.size())));
449         bool needPing = false;
450         foreach (WatchData data, result) {
451             data.iname = iname + '.' + data.exp;
452             d->engine->watchHandler()->insertData(data);
453
454             if (d->engine->watchHandler()->expandedINames().contains(data.iname)) {
455                 needPing = true;
456                 expandObject(data.iname, data.id);
457             }
458         }
459         if (needPing)
460             sendPing();
461     } else if (command == "LOCALS") {
462         QList<WatchData> locals;
463         QList<WatchData> watches;
464         int frameId;
465         stream >> frameId >> locals;
466         if (!stream.atEnd()) { // compatibility with jsdebuggeragent from 2.1, 2.2
467             stream >> watches;
468         }
469
470         d->engine->logMessage(QmlEngine::LogReceive, QString("%1 %2 (%3 x locals) (%4 x watchdata)").arg(
471                                   QString(command), QString::number(frameId),
472                                   QString::number(locals.size()),
473                                   QString::number(watches.size())));
474         d->engine->watchHandler()->beginCycle();
475         bool needPing = false;
476         foreach (WatchData data, watches) {
477             data.iname = d->engine->watchHandler()->watcherName(data.exp);
478             d->engine->watchHandler()->insertData(data);
479
480             if (d->engine->watchHandler()->expandedINames().contains(data.iname)) {
481                 needPing = true;
482                 expandObject(data.iname, data.id);
483             }
484         }
485
486         foreach (WatchData data, locals) {
487             data.iname = "local." + data.exp;
488             d->engine->watchHandler()->insertData(data);
489             if (d->engine->watchHandler()->expandedINames().contains(data.iname)) {
490                 needPing = true;
491                 expandObject(data.iname, data.id);
492             }
493         }
494         if (needPing)
495             sendPing();
496         else
497             d->engine->watchHandler()->endCycle();
498
499     } else if (command == "PONG") {
500         int ping;
501         stream >> ping;
502
503         d->engine->logMessage(QmlEngine::LogReceive, QString("%1 %2").arg(QString(command), QString::number(ping)));
504
505         if (ping == d->ping)
506             d->engine->watchHandler()->endCycle();
507     } else {
508         qDebug() << Q_FUNC_INFO << "Unknown command: " << command;
509         d->engine->logMessage(QmlEngine::LogReceive, QString("%1 UNKNOWN COMMAND!!").arg(QString(command)));
510     }
511
512 }
513
514 void QScriptDebuggerClient::setEngine(QmlEngine *engine)
515 {
516     d->engine = engine;
517 }
518
519 } // Internal
520 } // Debugger