source: trunk/src/scripttools/debugging/qscriptdebugger.cpp@ 5

Last change on this file since 5 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 61.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtSCriptTools module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qscriptdebugger_p.h"
43#include "qscriptdebuggerconsole_p.h"
44#include "qscriptdebuggerconsolecommandmanager_p.h"
45#include "qscriptdebuggerconsolecommandjob_p.h"
46#include "qscriptstdmessagehandler_p.h"
47#include "qscriptdebuggerfrontend_p.h"
48#include "qscriptdebuggereventhandlerinterface_p.h"
49#include "qscriptdebuggerresponsehandlerinterface_p.h"
50#include "qscriptdebuggerjobschedulerinterface_p.h"
51#include "qscriptdebuggerconsolewidgetinterface_p.h"
52#include "qscriptcompletionproviderinterface_p.h"
53#include "qscriptcompletiontask_p.h"
54#include "qscripttooltipproviderinterface_p.h"
55#include "qscriptdebuggerstackwidgetinterface_p.h"
56#include "qscriptdebuggerstackmodel_p.h"
57#include "qscriptdebuggerscriptswidgetinterface_p.h"
58#include "qscriptdebuggerscriptsmodel_p.h"
59#include "qscriptdebuggerlocalswidgetinterface_p.h"
60#include "qscriptdebuggerlocalsmodel_p.h"
61#include "qscriptdebuggercodewidgetinterface_p.h"
62#include "qscriptdebuggercodeviewinterface_p.h"
63#include "qscriptdebuggercodefinderwidgetinterface_p.h"
64#include "qscriptbreakpointswidgetinterface_p.h"
65#include "qscriptbreakpointsmodel_p.h"
66#include "qscriptdebugoutputwidgetinterface_p.h"
67#include "qscripterrorlogwidgetinterface_p.h"
68#include "qscriptdebuggerwidgetfactoryinterface_p.h"
69#include "qscriptdebuggerevent_p.h"
70#include "qscriptdebuggervalue_p.h"
71#include "qscriptdebuggerresponse_p.h"
72#include "qscriptdebuggercommand_p.h"
73#include "qscriptdebuggercommandschedulerfrontend_p.h"
74#include "qscriptdebuggercommandschedulerjob_p.h"
75#include "qscriptdebuggerjob_p_p.h"
76#include "qscriptxmlparser_p.h"
77
78#include "qscriptenginedebuggerfrontend_p.h"
79#include "qscriptdebuggerbackend_p.h"
80#include <QtScript/qscriptengine.h>
81
82#include "private/qobject_p.h"
83
84#include <QtScript/qscriptcontext.h>
85#include <QtScript/qscriptcontextinfo.h>
86
87#include <QtCore/qcoreapplication.h>
88#include <QtCore/qdir.h>
89#include <QtCore/qfileinfo.h>
90#include <QtCore/qstringlist.h>
91#include <QtCore/qdebug.h>
92
93#include <QtGui/qaction.h>
94#include <QtGui/qevent.h>
95#include <QtGui/qicon.h>
96#include <QtGui/qinputdialog.h>
97
98QT_BEGIN_NAMESPACE
99typedef QPair<QList<qint64>, QList<qint64> > QScriptScriptsDelta;
100typedef QPair<QList<qint64>, QList<qint64> > QScriptContextsDelta;
101QT_END_NAMESPACE
102
103Q_DECLARE_METATYPE(QScriptScriptsDelta)
104
105QT_BEGIN_NAMESPACE
106
107Q_SCRIPT_EXPORT QString qt_scriptToXml(const QString &program, int lineNumber = 1);
108
109namespace {
110
111static int scriptDebuggerCount = 0;
112static bool eventCallbackRegistered = false;
113static bool widgetInPaintEvent = false;
114
115static bool scriptDebuggerEventCallback(void **data)
116{
117 QEvent *event = reinterpret_cast<QEvent*>(data[1]);
118 if (event->type() == QEvent::Paint) {
119 QObject *receiver = reinterpret_cast<QObject*>(data[0]);
120 bool was = widgetInPaintEvent;
121 widgetInPaintEvent = true;
122 QCoreApplication::instance()->notify(receiver, event);
123 widgetInPaintEvent = was;
124 bool *result = reinterpret_cast<bool*>(data[2]);
125 *result = true;
126 return true;
127 }
128 return false;
129}
130
131}
132
133/*!
134 \since 4.5
135 \class QScriptDebugger
136 \internal
137
138 \brief The QScriptDebugger class provides a Qt Script debugger.
139
140 \ingroup scripttools
141 \mainclass
142*/
143
144class QScriptDebuggerPrivate
145 : public QObjectPrivate,
146 public QScriptDebuggerCommandSchedulerInterface,
147 public QScriptDebuggerJobSchedulerInterface,
148 public QScriptDebuggerEventHandlerInterface,
149 public QScriptDebuggerResponseHandlerInterface,
150 public QScriptCompletionProviderInterface,
151 public QScriptToolTipProviderInterface
152{
153 Q_DECLARE_PUBLIC(QScriptDebugger)
154public:
155 QScriptDebuggerPrivate();
156 ~QScriptDebuggerPrivate();
157
158 int scheduleJob(QScriptDebuggerJob *job);
159 void finishJob(QScriptDebuggerJob *job);
160 void hibernateUntilEvaluateFinished(QScriptDebuggerJob *job);
161
162 void maybeStartNewJob();
163
164 int scheduleCommand(const QScriptDebuggerCommand &command,
165 QScriptDebuggerResponseHandlerInterface *responseHandler);
166
167 void handleResponse(
168 const QScriptDebuggerResponse &response, int commandId);
169 bool debuggerEvent(const QScriptDebuggerEvent &event);
170
171 QScriptCompletionTaskInterface *createCompletionTask(
172 const QString &contents, int cursorPosition, int frameIndex, int options);
173
174 QString toolTip(int frameIndex, int lineNumber,
175 const QStringList &path);
176
177 static QPixmap pixmap(const QString &path);
178
179 void startInteraction(QScriptDebuggerEvent::Type type,
180 qint64 scriptId, int lineNumber);
181 void sync();
182 void loadLocals(int frameIndex);
183 QScriptDebuggerLocalsModel *createLocalsModel();
184 void selectScriptForFrame(int frameIndex);
185 void emitStoppedSignal();
186
187 void maybeDelete(QWidget *widget);
188
189 // private slots
190 void _q_onLineEntered(const QString &contents);
191 void _q_onCurrentFrameChanged(int frameIndex);
192 void _q_onCurrentScriptChanged(qint64 scriptId);
193 void _q_onScriptLocationSelected(int lineNumber);
194 void _q_interrupt();
195 void _q_continue();
196 void _q_stepInto();
197 void _q_stepOver();
198 void _q_stepOut();
199 void _q_runToCursor();
200 void _q_runToNewScript();
201 void _q_toggleBreakpoint();
202 void _q_clearDebugOutput();
203 void _q_clearErrorLog();
204 void _q_clearConsole();
205 void _q_findInScript();
206 void _q_findNextInScript();
207 void _q_findPreviousInScript();
208 void _q_onFindCodeRequest(const QString &, int);
209 void _q_goToLine();
210
211 void executeConsoleCommand(const QString &command);
212 void findCode(const QString &exp, int options);
213
214 QScriptDebuggerFrontend *frontend;
215
216 bool interactive;
217 QScriptDebuggerConsole *console;
218
219 int nextJobId;
220 QList<QScriptDebuggerJob*> pendingJobs;
221 QList<int> pendingJobIds;
222 QScriptDebuggerJob *activeJob;
223 bool activeJobHibernating;
224 QHash<int, QScriptDebuggerCommand> watchedCommands;
225 QHash<int, QScriptDebuggerResponseHandlerInterface*> responseHandlers;
226
227 QScriptDebuggerConsoleWidgetInterface *consoleWidget;
228 QScriptDebuggerStackWidgetInterface *stackWidget;
229 QScriptDebuggerStackModel *stackModel;
230 QScriptDebuggerScriptsWidgetInterface *scriptsWidget;
231 QScriptDebuggerScriptsModel *scriptsModel;
232 QScriptDebuggerLocalsWidgetInterface *localsWidget;
233 QHash<int, QScriptDebuggerLocalsModel*> localsModels;
234 QScriptDebuggerCodeWidgetInterface *codeWidget;
235 QScriptDebuggerCodeFinderWidgetInterface *codeFinderWidget;
236 QScriptBreakpointsWidgetInterface *breakpointsWidget;
237 QScriptBreakpointsModel *breakpointsModel;
238 QScriptDebugOutputWidgetInterface *debugOutputWidget;
239 QScriptErrorLogWidgetInterface *errorLogWidget;
240 QScriptDebuggerWidgetFactoryInterface *widgetFactory;
241
242 QAction *interruptAction;
243 QAction *continueAction;
244 QAction *stepIntoAction;
245 QAction *stepOverAction;
246 QAction *stepOutAction;
247 QAction *runToCursorAction;
248 QAction *runToNewScriptAction;
249
250 QAction *toggleBreakpointAction;
251
252 QAction *clearDebugOutputAction;
253 QAction *clearErrorLogAction;
254 QAction *clearConsoleAction;
255
256 QAction *findInScriptAction;
257 QAction *findNextInScriptAction;
258 QAction *findPreviousInScriptAction;
259 QAction *goToLineAction;
260
261 int updatesEnabledTimerId;
262};
263
264QScriptDebuggerPrivate::QScriptDebuggerPrivate()
265{
266 frontend = 0;
267 activeJob = 0;
268 activeJobHibernating = false;
269 nextJobId = 0;
270 interactive = false;
271
272 console = new QScriptDebuggerConsole();
273 QString scriptsPath = QLatin1String(":/qt/scripttools/debugging/scripts/commands");
274 QScriptStdMessageHandler tmp;
275 console->loadScriptedCommands(scriptsPath, &tmp);
276
277 consoleWidget = 0;
278 stackWidget = 0;
279 stackModel = 0;
280 scriptsWidget = 0;
281 scriptsModel = 0;
282 localsWidget = 0;
283 codeWidget = 0;
284 codeFinderWidget = 0;
285 breakpointsWidget = 0;
286 breakpointsModel = 0;
287 debugOutputWidget = 0;
288 errorLogWidget = 0;
289 widgetFactory = 0;
290
291 interruptAction = 0;
292 continueAction = 0;
293 stepIntoAction = 0;
294 stepOverAction = 0;
295 stepOutAction = 0;
296 runToCursorAction = 0;
297 runToNewScriptAction = 0;
298
299 toggleBreakpointAction = 0;
300
301 clearErrorLogAction = 0;
302 clearDebugOutputAction = 0;
303 clearConsoleAction = 0;
304
305 findInScriptAction = 0;
306 findNextInScriptAction = 0;
307 findPreviousInScriptAction = 0;
308 goToLineAction = 0;
309
310 updatesEnabledTimerId = -1;
311}
312
313QScriptDebuggerPrivate::~QScriptDebuggerPrivate()
314{
315 delete console;
316 qDeleteAll(pendingJobs);
317 delete activeJob;
318 maybeDelete(consoleWidget);
319 maybeDelete(stackWidget);
320 maybeDelete(scriptsWidget);
321 maybeDelete(localsWidget);
322 maybeDelete(codeWidget);
323 maybeDelete(codeFinderWidget);
324 maybeDelete(breakpointsWidget);
325 maybeDelete(debugOutputWidget);
326 maybeDelete(errorLogWidget);
327}
328
329void QScriptDebuggerPrivate::maybeDelete(QWidget *widget)
330{
331 if (widget && !widget->parent())
332 delete widget;
333}
334
335QPixmap QScriptDebuggerPrivate::pixmap(const QString &path)
336{
337 static QString prefix = QString::fromLatin1(":/qt/scripttools/debugging/images/");
338 return QPixmap(prefix + path);
339}
340
341/*!
342 \reimp
343*/
344int QScriptDebuggerPrivate::scheduleJob(QScriptDebuggerJob *job)
345{
346 QScriptDebuggerJobPrivate *priv = QScriptDebuggerJobPrivate::get(job);
347 Q_ASSERT(priv->jobScheduler == 0);
348 priv->jobScheduler = this;
349 int id = nextJobId;
350 pendingJobs.append(job);
351 pendingJobIds.append(id);
352 maybeStartNewJob();
353 return id;
354}
355
356/*!
357 \reimp
358*/
359void QScriptDebuggerPrivate::finishJob(QScriptDebuggerJob *job)
360{
361 Q_UNUSED(job);
362 Q_ASSERT(activeJob == job);
363 delete activeJob;
364 activeJob = 0;
365 activeJobHibernating = false;
366 maybeStartNewJob();
367}
368
369/*!
370 \reimp
371*/
372void QScriptDebuggerPrivate::hibernateUntilEvaluateFinished(QScriptDebuggerJob *job)
373{
374 Q_UNUSED(job);
375 Q_ASSERT(activeJob == job);
376 activeJobHibernating = true;
377}
378
379/*!
380 Starts a new job if appropriate.
381*/
382void QScriptDebuggerPrivate::maybeStartNewJob()
383{
384 if (activeJob || pendingJobs.isEmpty())
385 return;
386 activeJob = pendingJobs.takeFirst();
387 activeJob->start();
388}
389
390/*!
391 \reimp
392*/
393int QScriptDebuggerPrivate::scheduleCommand(
394 const QScriptDebuggerCommand &command,
395 QScriptDebuggerResponseHandlerInterface *responseHandler)
396{
397 if (!frontend)
398 return -1;
399 int id = frontend->scheduleCommand(command, this);
400 if (responseHandler && (responseHandler != this))
401 responseHandlers.insert(id, responseHandler);
402 if ((command.type() == QScriptDebuggerCommand::SetBreakpoint)
403 || (command.type() == QScriptDebuggerCommand::SetBreakpointData)
404 || (command.type() == QScriptDebuggerCommand::DeleteBreakpoint)) {
405 // need to watch this command and update the breakpoints model afterwards
406 watchedCommands.insert(id, command);
407 }
408 return id;
409}
410
411/*!
412 \reimp
413*/
414void QScriptDebuggerPrivate::handleResponse(
415 const QScriptDebuggerResponse &response, int commandId)
416{
417 Q_Q(QScriptDebugger);
418 if (watchedCommands.contains(commandId)) {
419 QScriptDebuggerCommand command = watchedCommands.take(commandId);
420 if (response.error() == QScriptDebuggerResponse::NoError) {
421 if (!breakpointsModel)
422 breakpointsModel = new QScriptBreakpointsModel(this, this, q);
423 switch (command.type()) {
424 case QScriptDebuggerCommand::SetBreakpoint: {
425 int breakpointId = response.resultAsInt();
426 QScriptBreakpointData data = command.breakpointData();
427 breakpointsModel->addBreakpoint(breakpointId, data);
428 } break;
429 case QScriptDebuggerCommand::SetBreakpointData: {
430 int breakpointId = command.breakpointId();
431 QScriptBreakpointData data = command.breakpointData();
432 breakpointsModel->modifyBreakpoint(breakpointId, data);
433 } break;
434 case QScriptDebuggerCommand::DeleteBreakpoint: {
435 int breakpointId = command.breakpointId();
436 breakpointsModel->removeBreakpoint(breakpointId);
437 } break;
438 default:
439 Q_ASSERT(false);
440 }
441 }
442 } else if (response.async()) {
443 interactive = false;
444 // disable/invalidate/enable stuff
445 if (continueAction)
446 continueAction->setEnabled(false);
447 if (stepIntoAction)
448 stepIntoAction->setEnabled(false);
449 if (stepOverAction)
450 stepOverAction->setEnabled(false);
451 if (stepOutAction)
452 stepOutAction->setEnabled(false);
453 if (runToCursorAction)
454 runToCursorAction->setEnabled(false);
455 if (runToNewScriptAction)
456 runToNewScriptAction->setEnabled(false);
457 if (interruptAction)
458 interruptAction->setEnabled(true);
459
460 // the timer is to avoid flicker when stepping
461 if (stackWidget) {
462 stackWidget->setUpdatesEnabled(false);
463 stackWidget->setEnabled(false);
464 if (updatesEnabledTimerId == -1)
465 updatesEnabledTimerId = q->startTimer(75);
466 }
467 if (localsWidget) {
468 localsWidget->setUpdatesEnabled(false);
469 localsWidget->setEnabled(false);
470 if (updatesEnabledTimerId == -1)
471 updatesEnabledTimerId = q->startTimer(75);
472 }
473 if (codeWidget)
474 codeWidget->invalidateExecutionLineNumbers();
475
476 emit q->started();
477 }
478
479 QScriptDebuggerResponseHandlerInterface *realHandler = responseHandlers.take(commandId);
480 if (realHandler)
481 realHandler->handleResponse(response, commandId);
482}
483
484/*!
485 \reimp
486
487 Handles a debugger event from the frontend.
488*/
489bool QScriptDebuggerPrivate::debuggerEvent(const QScriptDebuggerEvent &event)
490{
491 Q_Q(QScriptDebugger);
492 switch (event.type()) {
493 case QScriptDebuggerEvent::None:
494 case QScriptDebuggerEvent::UserEvent:
495 case QScriptDebuggerEvent::MaxUserEvent:
496 Q_ASSERT(false);
497 break;
498
499 case QScriptDebuggerEvent::Trace:
500 if (!debugOutputWidget && widgetFactory)
501 q->setDebugOutputWidget(widgetFactory->createDebugOutputWidget());
502 if (debugOutputWidget)
503 debugOutputWidget->message(QtDebugMsg, event.message());
504 return true; // trace doesn't stall execution
505
506 case QScriptDebuggerEvent::SteppingFinished: {
507 if (!consoleWidget && widgetFactory)
508 q->setConsoleWidget(widgetFactory->createConsoleWidget());
509 if (consoleWidget) {
510 QString msg = event.message();
511 if (!msg.isEmpty())
512 consoleWidget->message(QtDebugMsg, msg);
513 }
514 } break;
515
516 case QScriptDebuggerEvent::Interrupted:
517 case QScriptDebuggerEvent::LocationReached:
518 break;
519
520 case QScriptDebuggerEvent::Breakpoint: {
521 int bpId = event.breakpointId();
522 if (!consoleWidget && widgetFactory)
523 q->setConsoleWidget(widgetFactory->createConsoleWidget());
524 if (consoleWidget) {
525 consoleWidget->message(QtDebugMsg,
526 QString::fromLatin1("Breakpoint %0 at %1, line %2.")
527 .arg(bpId).arg(event.fileName())
528 .arg(event.lineNumber()));
529 }
530 if (breakpointsModel->breakpointData(bpId).isSingleShot())
531 breakpointsModel->removeBreakpoint(bpId);
532 } break;
533
534 case QScriptDebuggerEvent::Exception: {
535 if (event.hasExceptionHandler()) {
536 // Let the exception be handled like normal.
537 // We may want to add a "Break on all exceptions" option
538 // to be able to customize this behavior.
539 return true;
540 }
541 if (!consoleWidget && widgetFactory)
542 q->setConsoleWidget(widgetFactory->createConsoleWidget());
543 if (!errorLogWidget && widgetFactory)
544 q->setErrorLogWidget(widgetFactory->createErrorLogWidget());
545 if (consoleWidget || errorLogWidget) {
546 QString fn = event.fileName();
547 if (fn.isEmpty()) {
548 if (event.scriptId() != -1)
549 fn = QString::fromLatin1("<anonymous script, id=%0>").arg(event.scriptId());
550 else
551 fn = QString::fromLatin1("<native>");
552 }
553 QString msg = QString::fromLatin1("Uncaught exception at %0:%1: %2").arg(fn)
554 .arg(event.lineNumber()).arg(event.message());
555 if (consoleWidget)
556 consoleWidget->message(QtCriticalMsg, msg);
557 if (errorLogWidget)
558 errorLogWidget->message(QtCriticalMsg, msg);
559 }
560 } break;
561
562 case QScriptDebuggerEvent::InlineEvalFinished: {
563 QScriptDebuggerValue result = event.scriptValue();
564 Q_ASSERT(console != 0);
565 int action = console->evaluateAction();
566 console->setEvaluateAction(0);
567 switch (action) {
568 case 0: { // eval command
569 if (activeJob) {
570 if (activeJobHibernating) {
571 activeJobHibernating = false;
572 activeJob->evaluateFinished(result);
573 }
574 } else if (consoleWidget) {
575 // ### if the result is an object, need to do a tostring job on it
576// messageHandler->message(QtDebugMsg, result.toString());
577 if (result.type() != QScriptDebuggerValue::UndefinedValue)
578 consoleWidget->message(QtDebugMsg, event.message());
579 }
580 } break;
581 case 1: { // return command
582 QScriptDebuggerCommandSchedulerFrontend frontend(this, this);
583 frontend.scheduleForceReturn(console->currentFrameIndex(), result);
584 } return false;
585 }
586 if (!event.isNestedEvaluate()) {
587 // in the case when evaluate() was called while the
588 // engine was not running, we don't want to enter interactive mode
589 return true;
590 }
591 } break;
592
593 case QScriptDebuggerEvent::DebuggerInvocationRequest: {
594 if (!consoleWidget && widgetFactory)
595 q->setConsoleWidget(widgetFactory->createConsoleWidget());
596 if (consoleWidget) {
597 QString fn = event.fileName();
598 if (fn.isEmpty())
599 fn = QString::fromLatin1("<anonymous script, id=%0>").arg(event.scriptId());
600 consoleWidget->message(QtDebugMsg,
601 QString::fromLatin1("Debugger invoked from %1, line %2.")
602 .arg(fn).arg(event.lineNumber()));
603 }
604 } break;
605
606 case QScriptDebuggerEvent::ForcedReturn: {
607 } break;
608
609 }
610
611 if (widgetInPaintEvent) {
612 QString msg = QString::fromLatin1("Suspending evaluation in paintEvent() is not supported; resuming.");
613 if (!consoleWidget && widgetFactory)
614 q->setConsoleWidget(widgetFactory->createConsoleWidget());
615 if (!errorLogWidget && widgetFactory)
616 q->setErrorLogWidget(widgetFactory->createErrorLogWidget());
617 if (consoleWidget)
618 consoleWidget->message(QtWarningMsg, msg);
619 if (errorLogWidget)
620 errorLogWidget->message(QtCriticalMsg, msg);
621 return true;
622 }
623
624 if (activeJobHibernating) {
625 // evaluate() did not finish normally (e.g. due to a breakpoint),
626 // so cancel the job that's waiting for it
627 delete activeJob;
628 activeJob = 0;
629 activeJobHibernating = false;
630 }
631
632 startInteraction(event.type(), event.scriptId(), event.lineNumber());
633 return !interactive;
634}
635
636/*!
637 \reimp
638*/
639QString QScriptDebuggerPrivate::toolTip(int frameIndex, int lineNumber,
640 const QStringList &path)
641{
642 if (frameIndex == -1) {
643 if (stackWidget)
644 frameIndex = stackWidget->currentFrameIndex();
645 else
646 frameIndex = console->currentFrameIndex();
647 }
648 // ### cheating for now, need to use async API
649 QScriptEngineDebuggerFrontend *edf = static_cast<QScriptEngineDebuggerFrontend*>(frontend);
650 QScriptDebuggerBackend *backend = edf->backend();
651 QScriptContext *ctx = backend->context(frameIndex);
652 if (!ctx || path.isEmpty())
653 return QString();
654 QScriptContextInfo ctxInfo(ctx);
655 if (ctx->callee().isValid()
656 && ((lineNumber < ctxInfo.functionStartLineNumber())
657 || (lineNumber > ctxInfo.functionEndLineNumber()))) {
658 return QString();
659 }
660 QScriptValueList objects;
661 int pathIndex = 0;
662 if (path.at(0) == QLatin1String("this")) {
663 objects.append(ctx->thisObject());
664 ++pathIndex;
665 } else {
666#if QT_VERSION >= 0x040500
667 objects << ctx->scopeChain();
668#else
669 objects.append(ctx->activationObject());
670#endif
671 }
672 for (int i = 0; i < objects.size(); ++i) {
673 QScriptValue val = objects.at(i);
674 for (int j = pathIndex; val.isValid() && (j < path.size()); ++j) {
675 val = val.property(path.at(j));
676 }
677 if (val.isValid()) {
678 bool hadException = (ctx->state() == QScriptContext::ExceptionState);
679 QString str = val.toString();
680 if (!hadException && backend->engine()->hasUncaughtException())
681 backend->engine()->clearExceptions();
682 return str;
683 }
684 }
685 return QString();
686}
687
688/*!
689 \reimp
690*/
691QScriptCompletionTaskInterface *QScriptDebuggerPrivate::createCompletionTask(
692 const QString &contents, int cursorPosition, int frameIndex, int options)
693{
694 return new QScriptCompletionTask(
695 contents, cursorPosition, frameIndex, frontend,
696 (options & QScriptCompletionProviderInterface::ConsoleCommandCompletion) ? console : 0);
697}
698
699/*!
700 Slot called when a line has been entered in the console widget.
701*/
702void QScriptDebuggerPrivate::_q_onLineEntered(const QString &contents)
703{
704 QScriptDebuggerConsoleCommandJob *commandJob;
705 commandJob = console->consumeInput(contents, consoleWidget, this);
706 if (commandJob != 0) {
707 scheduleJob(commandJob);
708 consoleWidget->setLineContinuationMode(false);
709 } else if (console->hasIncompleteInput()) {
710 consoleWidget->setLineContinuationMode(true);
711 }
712}
713
714/*!
715 Slot called when the current index has changed in the stack widget.
716*/
717void QScriptDebuggerPrivate::_q_onCurrentFrameChanged(int frameIndex)
718{
719 loadLocals(frameIndex);
720 selectScriptForFrame(frameIndex);
721}
722
723/*!
724 Slot called when the current script has changed in the scripts widget.
725*/
726void QScriptDebuggerPrivate::_q_onCurrentScriptChanged(qint64 scriptId)
727{
728 if (codeWidget && (codeWidget->currentScriptId() != scriptId)) {
729 codeWidget->setCurrentScript(scriptId);
730 QScriptDebuggerCodeViewInterface *view = codeWidget->currentView();
731 if (view)
732 view->setExecutionLineNumber(-1, /*error=*/false);
733 }
734}
735
736void QScriptDebuggerPrivate::_q_onScriptLocationSelected(int lineNumber)
737{
738 QScriptDebuggerCodeViewInterface *view = codeWidget->currentView();
739 if (!view)
740 return;
741 view->gotoLine(lineNumber);
742}
743
744void QScriptDebuggerPrivate::_q_interrupt()
745{
746 executeConsoleCommand(QString::fromLatin1("interrupt"));
747}
748
749void QScriptDebuggerPrivate::_q_continue()
750{
751 executeConsoleCommand(QString::fromLatin1("continue"));
752}
753
754void QScriptDebuggerPrivate::_q_stepInto()
755{
756 executeConsoleCommand(QString::fromLatin1("step"));
757}
758
759void QScriptDebuggerPrivate::_q_stepOver()
760{
761 executeConsoleCommand(QString::fromLatin1("next"));
762}
763
764void QScriptDebuggerPrivate::_q_stepOut()
765{
766 executeConsoleCommand(QString::fromLatin1("finish"));
767}
768
769void QScriptDebuggerPrivate::_q_runToCursor()
770{
771 qint64 scriptId = codeWidget->currentScriptId();
772 int lineNumber = codeWidget->currentView()->cursorLineNumber();
773 QScriptDebuggerCommandSchedulerFrontend frontend(this, this);
774 frontend.scheduleRunToLocation(scriptId, lineNumber);
775}
776
777void QScriptDebuggerPrivate::_q_runToNewScript()
778{
779 QScriptDebuggerCommandSchedulerFrontend frontend(this, this);
780 frontend.scheduleRunToLocation(QString(), -1);
781}
782
783void QScriptDebuggerPrivate::_q_toggleBreakpoint()
784{
785 Q_ASSERT(codeWidget != 0);
786 QScriptDebuggerCodeViewInterface *view = codeWidget->currentView();
787 if (!view)
788 return;
789 qint64 scriptId = codeWidget->currentScriptId();
790 int lineNumber = view->cursorLineNumber();
791 Q_ASSERT(breakpointsModel != 0);
792 int bpId = breakpointsModel->resolveBreakpoint(scriptId, lineNumber);
793 if (bpId != -1) {
794 breakpointsModel->deleteBreakpoint(bpId);
795 } else {
796 QScriptBreakpointData data(scriptId, lineNumber);
797 if (scriptsModel)
798 data.setFileName(scriptsModel->scriptData(scriptId).fileName());
799 breakpointsModel->setBreakpoint(data);
800 }
801}
802
803void QScriptDebuggerPrivate::_q_clearDebugOutput()
804{
805 if (debugOutputWidget)
806 debugOutputWidget->clear();
807}
808
809void QScriptDebuggerPrivate::_q_clearErrorLog()
810{
811 if (errorLogWidget)
812 errorLogWidget->clear();
813}
814
815void QScriptDebuggerPrivate::_q_clearConsole()
816{
817 if (consoleWidget)
818 consoleWidget->clear();
819}
820
821void QScriptDebuggerPrivate::executeConsoleCommand(const QString &command)
822{
823 QString tmp = console->incompleteInput();
824 console->setIncompleteInput(QString());
825 QScriptDebuggerJob *job = console->consumeInput(console->commandPrefix() + command, debugOutputWidget, this);
826 console->setIncompleteInput(tmp);
827 if (job != 0) {
828 scheduleJob(job);
829 // once to send the command...
830 QCoreApplication::processEvents();
831 // ... and once to receive the response
832 QCoreApplication::processEvents();
833 }
834}
835
836void QScriptDebuggerPrivate::_q_findInScript()
837{
838 if (!codeFinderWidget && widgetFactory)
839 q_func()->setCodeFinderWidget(widgetFactory->createCodeFinderWidget());
840 if (codeFinderWidget) {
841 codeFinderWidget->show();
842 codeFinderWidget->setFocus(Qt::OtherFocusReason);
843 }
844}
845
846void QScriptDebuggerPrivate::_q_findNextInScript()
847{
848 findCode(codeFinderWidget->text(), codeFinderWidget->findOptions());
849}
850
851void QScriptDebuggerPrivate::_q_findPreviousInScript()
852{
853 int options = codeFinderWidget->findOptions();
854 options |= QTextDocument::FindBackward;
855 findCode(codeFinderWidget->text(), options);
856}
857
858void QScriptDebuggerPrivate::_q_onFindCodeRequest(
859 const QString &exp, int options)
860{
861 findCode(exp, options);
862 if (findNextInScriptAction)
863 findNextInScriptAction->setEnabled(!exp.isEmpty());
864 if (findPreviousInScriptAction)
865 findPreviousInScriptAction->setEnabled(!exp.isEmpty());
866}
867
868void QScriptDebuggerPrivate::findCode(const QString &exp, int options)
869{
870 QScriptDebuggerCodeViewInterface *view = codeWidget->currentView();
871 if (!view)
872 return;
873 int result = view->find(exp, options);
874 codeFinderWidget->setOK(((result & 0x1) != 0) || exp.isEmpty());
875 codeFinderWidget->setWrapped((result & 0x2) != 0);
876}
877
878void QScriptDebuggerPrivate::_q_goToLine()
879{
880 QScriptDebuggerCodeViewInterface *view = codeWidget->currentView();
881 if (!view)
882 return;
883 bool ok = false;
884 int lineNumber = QInputDialog::getInteger(0, QObject::tr("Go to Line"),
885 QObject::tr("Line:"),
886 view->cursorLineNumber(),
887 1, INT_MAX, 1, &ok);
888 if (ok)
889 view->gotoLine(lineNumber);
890}
891
892class QScriptDebuggerShowLineJob : public QScriptDebuggerCommandSchedulerJob
893{
894public:
895 QScriptDebuggerShowLineJob(qint64 scriptId, int lineNumber,
896 QScriptMessageHandlerInterface *messageHandler,
897 QScriptDebuggerCommandSchedulerInterface *scheduler)
898 : QScriptDebuggerCommandSchedulerJob(scheduler),
899 m_scriptId(scriptId), m_lineNumber(lineNumber),
900 m_messageHandler(messageHandler) {}
901
902 void start()
903 {
904 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
905 frontend.scheduleGetScriptData(m_scriptId);
906 }
907 void handleResponse(const QScriptDebuggerResponse &response, int /*commandId*/)
908 {
909 QScriptScriptData data = response.resultAsScriptData();
910 QString line = data.lines(m_lineNumber, 1).value(0);
911 m_messageHandler->message(QtDebugMsg, QString::fromLatin1("%0\t%1")
912 .arg(m_lineNumber).arg(line));
913 finish();
914 }
915
916private:
917 qint64 m_scriptId;
918 int m_lineNumber;
919 QScriptMessageHandlerInterface *m_messageHandler;
920};
921
922namespace {
923
924class SyncStackJob : public QScriptDebuggerCommandSchedulerJob
925{
926public:
927 SyncStackJob(QScriptDebuggerPrivate *debugger)
928 : QScriptDebuggerCommandSchedulerJob(debugger),
929 m_debugger(debugger), m_index(0) {}
930 void start()
931 {
932 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
933 frontend.scheduleGetContextInfo(m_index); // ### getContextInfos()
934 }
935 void handleResponse(const QScriptDebuggerResponse &response,
936 int)
937 {
938 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
939 if (response.error() != QScriptDebuggerResponse::InvalidContextIndex) {
940 m_infos.append(response.resultAsContextInfo());
941 frontend.scheduleGetContextInfo(++m_index);
942 } else {
943 m_debugger->stackModel->setContextInfos(m_infos);
944 if (m_debugger->stackWidget->currentFrameIndex() == -1)
945 m_debugger->stackWidget->setCurrentFrameIndex(0);
946 m_debugger->stackWidget->setUpdatesEnabled(true);
947 m_debugger->stackWidget->setEnabled(true);
948 finish();
949 }
950 }
951
952private:
953 QScriptDebuggerPrivate *m_debugger;
954 int m_index;
955 QList<QScriptContextInfo> m_infos;
956};
957
958class SyncScriptsJob : public QScriptDebuggerCommandSchedulerJob
959{
960public:
961 SyncScriptsJob(QScriptDebuggerPrivate *debugger)
962 : QScriptDebuggerCommandSchedulerJob(debugger),
963 m_debugger(debugger), m_index(-1) {}
964
965 void start()
966 {
967 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
968 frontend.scheduleScriptsCheckpoint();
969 }
970 void handleResponse(const QScriptDebuggerResponse &response,
971 int)
972 {
973 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
974 if (m_index == -1) {
975 QScriptScriptsDelta delta;
976 delta = qvariant_cast<QScriptScriptsDelta>(response.result());
977
978 const QList<qint64> &removed = delta.second;
979 for (int i = 0; i < removed.size(); ++i)
980 m_debugger->scriptsModel->removeScript(removed.at(i));
981
982 m_added = delta.first;
983 if (!m_added.isEmpty()) {
984 frontend.scheduleGetScriptData(m_added.at(++m_index));
985 } else {
986 m_debugger->scriptsModel->commit();
987 finish();
988 }
989 } else {
990 QScriptScriptData data = response.resultAsScriptData();
991 qint64 scriptId = m_added.at(m_index);
992 m_debugger->scriptsModel->addScript(scriptId, data);
993
994#if QT_VERSION >= 0x040500
995 // ### could be slow, might want to do this in a separate thread
996 QString xml = qt_scriptToXml(data.contents(), data.baseLineNumber());
997 QScriptXmlParser::Result extraInfo = QScriptXmlParser::parse(xml);
998 m_debugger->scriptsModel->addExtraScriptInfo(
999 scriptId, extraInfo.functionsInfo, extraInfo.executableLineNumbers);
1000#endif
1001
1002 if (++m_index < m_added.size())
1003 frontend.scheduleGetScriptData(m_added.at(m_index));
1004 else {
1005 m_debugger->scriptsModel->commit();
1006 finish();
1007 }
1008 }
1009 }
1010
1011private:
1012 QScriptDebuggerPrivate *m_debugger;
1013 int m_index;
1014 QList<qint64> m_added;
1015};
1016
1017class SyncBreakpointsJob : public QScriptDebuggerCommandSchedulerJob
1018{
1019public:
1020 SyncBreakpointsJob(QScriptDebuggerPrivate *debugger)
1021 : QScriptDebuggerCommandSchedulerJob(debugger),
1022 m_debugger(debugger) {}
1023 void start()
1024 {
1025 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
1026 frontend.scheduleGetBreakpoints();
1027 }
1028 void handleResponse(const QScriptDebuggerResponse &response,
1029 int)
1030 {
1031 QScriptBreakpointMap breakpoints = response.resultAsBreakpoints();
1032 QScriptBreakpointMap::const_iterator it;
1033 for (it = breakpoints.constBegin(); it != breakpoints.constEnd(); ++it) {
1034 int id = it.key();
1035 QScriptBreakpointData newData = it.value();
1036 QScriptBreakpointData existingData = m_debugger->breakpointsModel->breakpointData(id);
1037 if (existingData.isValid() && (existingData != newData))
1038 m_debugger->breakpointsModel->modifyBreakpoint(id, newData);
1039 }
1040 finish();
1041 }
1042
1043private:
1044 QScriptDebuggerPrivate *m_debugger;
1045 int m_index;
1046 QList<QScriptContextInfo> m_infos;
1047};
1048
1049class SyncLocalsJob : public QScriptDebuggerCommandSchedulerJob
1050{
1051public:
1052 SyncLocalsJob(QScriptDebuggerPrivate *debugger)
1053 : QScriptDebuggerCommandSchedulerJob(debugger),
1054 m_debugger(debugger) {}
1055
1056 void start()
1057 {
1058 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
1059 frontend.scheduleContextsCheckpoint();
1060 }
1061 void handleResponse(const QScriptDebuggerResponse &response,
1062 int)
1063 {
1064 QScriptContextsDelta delta = qvariant_cast<QScriptContextsDelta>(response.result());
1065 for (int i = 0; i < delta.first.size(); ++i) {
1066 QScriptDebuggerLocalsModel *model = m_debugger->localsModels.take(delta.first.at(i));
1067 delete model;
1068 }
1069 finish();
1070 }
1071
1072private:
1073 QScriptDebuggerPrivate *m_debugger;
1074};
1075
1076class LoadLocalsJob : public QScriptDebuggerCommandSchedulerJob
1077{
1078public:
1079 LoadLocalsJob(QScriptDebuggerPrivate *debugger, int frameIndex)
1080 : QScriptDebuggerCommandSchedulerJob(debugger),
1081 m_debugger(debugger), m_frameIndex(frameIndex), m_state(0) {}
1082
1083 void start()
1084 {
1085 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
1086 frontend.scheduleGetContextId(m_frameIndex);
1087 }
1088 void handleResponse(const QScriptDebuggerResponse &response,
1089 int)
1090 {
1091 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
1092 qint64 contextId = response.resultAsLongLong();
1093 QScriptDebuggerLocalsModel *model = m_debugger->localsModels.value(contextId);
1094 if (model) {
1095 model->sync(m_frameIndex);
1096 } else {
1097 model = m_debugger->createLocalsModel();
1098 m_debugger->localsModels.insert(contextId, model);
1099 model->init(m_frameIndex);
1100 }
1101 if (m_debugger->localsWidget) {
1102 if (m_debugger->localsWidget->localsModel() != model) // ### bug in qtreeview
1103 m_debugger->localsWidget->setLocalsModel(model);
1104 m_debugger->localsWidget->setUpdatesEnabled(true);
1105 m_debugger->localsWidget->setEnabled(true);
1106 }
1107 finish();
1108 }
1109
1110private:
1111 QScriptDebuggerPrivate *m_debugger;
1112 int m_frameIndex;
1113 int m_state;
1114};
1115
1116class EmitStoppedSignalJob : public QScriptDebuggerJob
1117{
1118public:
1119 EmitStoppedSignalJob(QScriptDebuggerPrivate *debugger)
1120 : m_debugger(debugger) {}
1121
1122 void start()
1123 {
1124 m_debugger->emitStoppedSignal();
1125 finish();
1126 }
1127
1128private:
1129 QScriptDebuggerPrivate *m_debugger;
1130};
1131
1132} // namespace
1133
1134void QScriptDebuggerPrivate::startInteraction(QScriptDebuggerEvent::Type type,
1135 qint64 scriptId, int lineNumber)
1136{
1137 Q_Q(QScriptDebugger);
1138 if (type != QScriptDebuggerEvent::InlineEvalFinished) {
1139 if (stackWidget)
1140 stackWidget->setCurrentFrameIndex(0);
1141 console->setCurrentFrameIndex(0);