1/****************************************************************************
2**
3** Copyright (C) 2018 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtSCriptTools module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qscriptdebuggerbackend_p.h"
41#include "qscriptdebuggerbackend_p_p.h"
42#include "qscriptdebuggeragent_p.h"
43#include "qscriptdebuggercommandexecutor_p.h"
44#include "qscriptdebuggerevent_p.h"
45#include "qscriptdebuggervalue_p.h"
46#include "qscriptscriptdata_p.h"
47#include "qscriptbreakpointdata_p.h"
48#include "qscriptobjectsnapshot_p.h"
49
50#include <QtScript/qscriptengine.h>
51#include <QtScript/qscriptcontextinfo.h>
52#include <QtScript/qscriptvalueiterator.h>
53
54#include <QtCore/qcoreapplication.h>
55#include <QtCore/qdebug.h>
56
57Q_DECLARE_METATYPE(QScriptDebuggerValue)
58Q_DECLARE_METATYPE(QScriptDebuggerBackendPrivate*)
59
60QT_BEGIN_NAMESPACE
61
62/*!
63 \since 4.5
64 \class QScriptDebuggerBackend
65 \internal
66
67 \brief The QScriptDebuggerBackend class is the base class of debugger back-ends.
68
69 QScriptDebuggerBackend builds on the QScriptDebuggerAgent class.
70
71 This class is usually used together with the QScriptDebuggerFrontend
72 class, in order to form a (front-end, back-end) pair.
73
74 Call attachTo() to attach to a QScriptEngine object. Call detach()
75 to detach from the current engine.
76
77 Call stepInto() to step into the next script statement; call stepOver()
78 to step over the next script statement; and call stepOut() to step out
79 of the currently executing script function. An event() will be generated
80 when the stepping is completed.
81
82 Call runToLocation() to execute script statements until a certain
83 location has been reached. An event() will be generated when the location
84 has been reached.
85
86 Call interruptEvaluation() to request that evaluation should be
87 interrupted. An event() will be generated upon the next script
88 statement that is reached.
89
90 Call continueEvalution() to allow script evaluation to continue.
91
92 Call setBreakpoint() to set a breakpoint. A breakpoint event() will
93 be generated when a breakpoint is hit. Call deleteBreakpoint() to
94 delete a breakpoint. Call modifyBreakpoint() to change the state of
95 an existing breakpoint.
96
97 Call contextCount() to obtain the number of active contexts
98 (frames). Call context() to obtain a pointer to a QScriptContext.
99
100 \section1 Subclassing
101
102 When subclassing QScriptDebuggerBackend, you must implement the pure
103 virtual event() function. This function typically forwards the event
104 to a QScriptDebuggerFrontend object. For most type of events,
105 event() should block until the back-end is instructed to resume
106 execution (e.g. until continueEvalution() is called). You must
107 implement resume(), which is responsible for making event() return.
108
109 \sa QScriptDebuggerFrontend, QScriptDebuggerEvent
110*/
111
112// helper class that's used to handle our custom Qt events
113class QScriptDebuggerBackendEventReceiver : public QObject
114{
115public:
116 QScriptDebuggerBackendEventReceiver(QScriptDebuggerBackendPrivate *backend,
117 QObject *parent = 0)
118 : QObject(parent), m_backend(backend) {}
119 ~QScriptDebuggerBackendEventReceiver() {}
120
121 bool event(QEvent *e)
122 {
123 return m_backend->event(e);
124 }
125
126private:
127 QScriptDebuggerBackendPrivate *m_backend;
128};
129
130
131QScriptDebuggerBackendPrivate::QScriptDebuggerBackendPrivate()
132 : agent(0), commandExecutor(0),
133 pendingEvaluateContextIndex(-1), pendingEvaluateLineNumber(-1),
134 ignoreExceptions(false),
135 nextScriptValueIteratorId(0), nextScriptObjectSnapshotId(0),
136 eventReceiver(0),
137 q_ptr(0) // q_ptr will be set later by QScriptDebuggerBackend constructor
138{
139}
140
141QScriptDebuggerBackendPrivate::~QScriptDebuggerBackendPrivate()
142{
143 if (agent)
144 agent->nullifyBackendPointer();
145 delete commandExecutor;
146 delete eventReceiver;
147 qDeleteAll(c: scriptValueIterators);
148 qDeleteAll(c: scriptObjectSnapshots);
149}
150
151void QScriptDebuggerBackendPrivate::postEvent(QEvent *e)
152{
153 if (!eventReceiver) {
154 eventReceiver = new QScriptDebuggerBackendEventReceiver(this);
155 Q_ASSERT(agent != 0);
156 eventReceiver->moveToThread(thread: agent->engine()->thread());
157 }
158 QCoreApplication::postEvent(receiver: eventReceiver, event: e);
159}
160
161bool QScriptDebuggerBackendPrivate::event(QEvent *e)
162{
163 if (e->type() == QEvent::User+1) {
164 QScriptDebuggerEventEvent *de = static_cast<QScriptDebuggerEventEvent*>(e);
165 q_func()->event(event: de->event());
166 return true;
167 }
168 return false;
169}
170
171void QScriptDebuggerBackendPrivate::agentDestroyed(QScriptDebuggerAgent *ag)
172{
173 // Since agents are owned by the script engine, this in practice means
174 // that the engine has been destroyed. Invalidate our pointer so we
175 // don't crash later.
176 if (agent == ag)
177 agent = 0;
178}
179
180/*!
181 The agent calls this function when it has completed a step
182 operation.
183*/
184void QScriptDebuggerBackendPrivate::stepped(qint64 scriptId,
185 int lineNumber,
186 int columnNumber,
187 const QScriptValue &result)
188{
189 Q_Q(QScriptDebuggerBackend);
190 QScriptDebuggerEvent e(QScriptDebuggerEvent::SteppingFinished,
191 scriptId, lineNumber, columnNumber);
192 e.setFileName(agent->scriptData(id: scriptId).fileName());
193 QScriptDebuggerValue value(result);
194 e.setScriptValue(value);
195 if (!result.isUndefined())
196 e.setMessage(result.toString()); // for convenience -- we always need it
197 q->event(event: e);
198}
199
200/*!
201 The agent calls this function when it has run to a particular
202 location.
203*/
204void QScriptDebuggerBackendPrivate::locationReached(qint64 scriptId,
205 int lineNumber,
206 int columnNumber)
207{
208 Q_Q(QScriptDebuggerBackend);
209 QScriptDebuggerEvent e(QScriptDebuggerEvent::LocationReached,
210 scriptId, lineNumber, columnNumber);
211 e.setFileName(agent->scriptData(id: scriptId).fileName());
212 q->event(event: e);
213}
214
215/*!
216 The agent calls this function when evaluation has been interrupted.
217*/
218void QScriptDebuggerBackendPrivate::interrupted(qint64 scriptId,
219 int lineNumber,
220 int columnNumber)
221{
222 Q_Q(QScriptDebuggerBackend);
223 QScriptDebuggerEvent e(QScriptDebuggerEvent::Interrupted,
224 scriptId, lineNumber, columnNumber);
225 e.setFileName(agent->scriptData(id: scriptId).fileName());
226 q->event(event: e);
227}
228
229/*!
230 The agent calls this function when a breakpoint has been triggered.
231*/
232void QScriptDebuggerBackendPrivate::breakpoint(qint64 scriptId,
233 int lineNumber,
234 int columnNumber,
235 int breakpointId)
236{
237 Q_Q(QScriptDebuggerBackend);
238 QScriptDebuggerEvent e(QScriptDebuggerEvent::Breakpoint,
239 scriptId, lineNumber, columnNumber);
240 e.setFileName(agent->scriptData(id: scriptId).fileName());
241 e.setBreakpointId(breakpointId);
242 q->event(event: e);
243}
244
245/*!
246 The agent calls this function when an uncaught exception has
247 occurred.
248*/
249void QScriptDebuggerBackendPrivate::exception(qint64 scriptId,
250 const QScriptValue &exception,
251 bool hasHandler)
252{
253 Q_Q(QScriptDebuggerBackend);
254 if (ignoreExceptions) {
255 // don't care (it's caught by us)
256 return;
257 }
258 QScriptDebuggerEvent e(QScriptDebuggerEvent::Exception);
259 e.setScriptId(scriptId);
260 e.setFileName(agent->scriptData(id: scriptId).fileName());
261 e.setMessage(exception.toString());
262 e.setHasExceptionHandler(hasHandler);
263 int lineNumber = -1;
264 QString fileName;
265 if (exception.property(name: QLatin1String("lineNumber")).isNumber())
266 lineNumber = exception.property(name: QLatin1String("lineNumber")).toInt32();
267 if (exception.property(name: QLatin1String("fileName")).isString())
268 fileName = exception.property(name: QLatin1String("fileName")).toString();
269 if (lineNumber == -1) {
270 QScriptContextInfo info(q->engine()->currentContext());
271 lineNumber = info.lineNumber();
272 fileName = info.fileName();
273 }
274 if (lineNumber != -1)
275 e.setLineNumber(lineNumber);
276 if (!fileName.isEmpty())
277 e.setFileName(fileName);
278 QScriptDebuggerValue value(exception);
279 e.setScriptValue(value);
280 q->event(event: e);
281}
282
283QScriptValue QScriptDebuggerBackendPrivate::trace(QScriptContext *context,
284 QScriptEngine *engine)
285{
286 QScriptValue data = context->callee().data();
287 QScriptDebuggerBackendPrivate *self = qscriptvalue_cast<QScriptDebuggerBackendPrivate*>(value: data);
288 if (!self)
289 return engine->undefinedValue();
290 QString str;
291 for (int i = 0; i < context->argumentCount(); ++i) {
292 if (i > 0)
293 str.append(c: QLatin1Char(' '));
294 str.append(s: context->argument(index: i).toString());
295 }
296 QScriptDebuggerEvent e(QScriptDebuggerEvent::Trace);
297 e.setMessage(str);
298 self->q_func()->event(event: e);
299 return engine->undefinedValue();
300}
301
302QScriptValue QScriptDebuggerBackendPrivate::qsassert(QScriptContext *context,
303 QScriptEngine *engine)
304{
305 QScriptValue arg = context->argument(index: 0);
306 if (arg.toBoolean())
307 return arg;
308 QScriptContextInfo info(context->parentContext());
309 QString msg;
310 QString fileName = info.fileName();
311 if (fileName.isEmpty())
312 fileName = QString::fromLatin1(str: "<anonymous script, id=%0>").arg(a: info.scriptId());
313 msg.append(s: fileName);
314 msg.append(c: QLatin1Char(':'));
315 msg.append(s: QString::number(info.lineNumber()));
316 msg.append(s: QString::fromLatin1(str: ": Assertion failed"));
317 for (int i = 1; i < context->argumentCount(); ++i) {
318 if (i == 1)
319 msg.append(c: QLatin1Char(':'));
320 msg.append(c: QLatin1Char(' '));
321 msg.append(s: context->argument(index: i).toString());
322 }
323 QScriptValue err = context->throwError(text: msg);
324 err.setProperty(name: QString::fromLatin1(str: "name"), value: QScriptValue(engine, QString::fromLatin1(str: "AssertionError")));
325 return err;
326}
327
328QScriptValue QScriptDebuggerBackendPrivate::fileName(QScriptContext *context,
329 QScriptEngine *engine)
330{
331 QScriptContextInfo info(context->parentContext());
332 QString fn = info.fileName();
333 if (fn.isEmpty())
334 return engine->undefinedValue();
335 return QScriptValue(engine, fn);
336}
337
338QScriptValue QScriptDebuggerBackendPrivate::lineNumber(QScriptContext *context,
339 QScriptEngine *engine)
340{
341 QScriptContextInfo info(context->parentContext());
342 return QScriptValue(engine, info.lineNumber());
343}
344
345/*!
346 The agent calls this function when the engine has reached a
347 "debugger" statement.
348*/
349void QScriptDebuggerBackendPrivate::debuggerInvocationRequest(
350 qint64 scriptId, int lineNumber, int columnNumber)
351{
352 Q_Q(QScriptDebuggerBackend);
353 QScriptDebuggerEvent e(QScriptDebuggerEvent::DebuggerInvocationRequest,
354 scriptId, lineNumber, columnNumber);
355 e.setFileName(agent->scriptData(id: scriptId).fileName());
356 q->event(event: e);
357}
358
359void QScriptDebuggerBackendPrivate::forcedReturn(
360 qint64 scriptId, int lineNumber, int columnNumber,
361 const QScriptValue &value)
362{
363 Q_Q(QScriptDebuggerBackend);
364 QScriptDebuggerEvent e(QScriptDebuggerEvent::ForcedReturn,
365 scriptId, lineNumber, columnNumber);
366 e.setFileName(agent->scriptData(id: scriptId).fileName());
367 e.setScriptValue(QScriptDebuggerValue(value));
368 q->event(event: e);
369}
370
371/*!
372 Creates a QScriptDebuggerBackend object.
373*/
374QScriptDebuggerBackend::QScriptDebuggerBackend()
375 : d_ptr(new QScriptDebuggerBackendPrivate)
376{
377 d_ptr->q_ptr = this;
378}
379
380/*!
381 Destroys this QScriptDebuggerBackend.
382*/
383QScriptDebuggerBackend::~QScriptDebuggerBackend()
384{
385 detach();
386}
387
388/*!
389 \internal
390*/
391QScriptDebuggerBackend::QScriptDebuggerBackend(QScriptDebuggerBackendPrivate &dd)
392 : d_ptr(&dd)
393{
394 d_ptr->q_ptr = this;
395}
396
397/*!
398 Attaches this backend to the given \a engine.
399 The backend automatically detaches from the old engine, if any.
400
401 This function installs its own agent on the \a engine using
402 QScriptEngine::setAgent(); any existing agent will be replaced.
403
404 \sa detach(), engine()
405*/
406void QScriptDebuggerBackend::attachTo(QScriptEngine *engine)
407{
408 Q_D(QScriptDebuggerBackend);
409 detach();
410 d->agent = new QScriptDebuggerAgent(d, engine);
411 QScriptValue global = engine->globalObject();
412 d->origTraceFunction = global.property(name: QString::fromLatin1(str: "print"));
413 global.setProperty(name: QString::fromLatin1(str: "print"), value: traceFunction());
414// global.setProperty(QString::fromLatin1("qAssert"), assertFunction());
415 d->origFileNameFunction = global.property(name: QString::fromLatin1(str: "__FILE__"));
416 global.setProperty(name: QString::fromLatin1(str: "__FILE__"), value: fileNameFunction(),
417 flags: QScriptValue::PropertyGetter | QScriptValue::ReadOnly);
418 d->origLineNumberFunction = global.property(name: QString::fromLatin1(str: "__LINE__"));
419 global.setProperty(name: QString::fromLatin1(str: "__LINE__"), value: lineNumberFunction(),
420 flags: QScriptValue::PropertyGetter | QScriptValue::ReadOnly);
421 engine->setAgent(d->agent);
422}
423
424/*!
425 Detaches this backend from the current script engine.
426 The backend's state (including breakpoints and information on loaded
427 scripts) will be invalidated.
428
429 \sa attach()
430*/
431void QScriptDebuggerBackend::detach()
432{
433 Q_D(QScriptDebuggerBackend);
434 if (d->agent) {
435 QScriptEngine *eng = d->agent->engine();
436 if (eng && eng->agent() == d->agent) {
437 eng->setAgent(0);
438 QScriptValue global = eng->globalObject();
439 global.setProperty(name: QString::fromLatin1(str: "print"), value: d->origTraceFunction);
440 d->origTraceFunction = QScriptValue();
441// global.setProperty(QString::fromLatin1("qAssert"), QScriptValue());
442 global.setProperty(name: QString::fromLatin1(str: "__FILE__"), value: QScriptValue(),
443 flags: QScriptValue::PropertyGetter);
444 global.setProperty(name: QString::fromLatin1(str: "__FILE__"), value: d->origFileNameFunction);
445 d->origFileNameFunction = QScriptValue();
446 global.setProperty(name: QString::fromLatin1(str: "__LINE__"), value: QScriptValue(),
447 flags: QScriptValue::PropertyGetter);
448 global.setProperty(name: QString::fromLatin1(str: "__LINE__"), value: d->origLineNumberFunction);
449 d->origLineNumberFunction = QScriptValue();
450 d->agent->nullifyBackendPointer();
451 d->agent = 0; // agent is owned by engine
452 }
453 }
454
455 d->pendingEvaluateLineNumber = -1;
456 d->ignoreExceptions = false;
457 d->nextScriptValueIteratorId = 0;
458 qDeleteAll(c: d->scriptValueIterators);
459 d->scriptValueIterators.clear();
460 qDeleteAll(c: d->scriptObjectSnapshots);
461 d->scriptObjectSnapshots.clear();
462}
463
464/*!
465 Returns the script engine that this backend is attached to, or 0 if
466 the backend is not attached to an engine.
467
468 \sa attachTo()
469*/
470QScriptEngine *QScriptDebuggerBackend::engine() const
471{
472 Q_D(const QScriptDebuggerBackend);
473 if (!d->agent)
474 return 0;
475 return d->agent->engine();
476}
477
478/*!
479 Steps into the next script statement.
480 When stepping is complete, an event() will be generated.
481*/
482void QScriptDebuggerBackend::stepInto(int count)
483{
484 Q_D(QScriptDebuggerBackend);
485 if (d->agent) {
486 d->agent->enterStepIntoMode(count);
487 resume();
488 }
489}
490
491/*!
492 Steps over the next script statement.
493 When stepping is complete, an event() will be generated.
494*/
495void QScriptDebuggerBackend::stepOver(int count)
496{
497 Q_D(QScriptDebuggerBackend);
498 if (d->agent) {
499 d->agent->enterStepOverMode(count);
500 resume();
501 }
502}
503
504/*!
505 Steps out of the current script function.
506 When stepping is complete, an event() will be generated.
507*/
508void QScriptDebuggerBackend::stepOut()
509{
510 Q_D(QScriptDebuggerBackend);
511 if (d->agent) {
512 d->agent->enterStepOutMode();
513 resume();
514 }
515}
516
517/*!
518 Continues script evaluation. Evaluation will proceed without
519 interruption until either 1) an uncaught exception occurs, 2) a
520 breakpoint is triggered, or 3) interruptEvaluation() is called.
521 In each case, a proper event() will be generated.
522*/
523void QScriptDebuggerBackend::continueEvalution()
524{
525 Q_D(QScriptDebuggerBackend);
526 if (d->agent) {
527 d->agent->enterContinueMode();
528 resume();
529 }
530}
531
532/*!
533 Interrupts script evaluation. When the next script statement is
534 reached, an event() will be generated.
535*/
536void QScriptDebuggerBackend::interruptEvaluation()
537{
538 Q_D(QScriptDebuggerBackend);
539 if (d->agent)
540 d->agent->enterInterruptMode();
541}
542
543/*!
544 Continues evaluation until the location defined by the given \a
545 fileName and \a lineNumber is reached. When the location is reached,
546 an event() will be generated.
547*/
548void QScriptDebuggerBackend::runToLocation(const QString &fileName, int lineNumber)
549{
550 Q_D(QScriptDebuggerBackend);
551 if (d->agent) {
552 d->agent->enterRunToLocationMode(fileName, lineNumber);
553 resume();
554 }
555}
556
557/*!
558 Continues evaluation until the location defined by the given \a
559 scriptId and \a lineNumber is reached. When the location is reached,
560 an event() will be generated.
561*/
562void QScriptDebuggerBackend::runToLocation(qint64 scriptId, int lineNumber)
563{
564 Q_D(QScriptDebuggerBackend);
565 if (d->agent) {
566 d->agent->enterRunToLocationMode(scriptId, lineNumber);
567 resume();
568 }
569}
570
571void QScriptDebuggerBackend::returnToCaller(int contextIndex, const QScriptValue &value)
572{
573 Q_D(QScriptDebuggerBackend);
574 if (d->agent) {
575 d->agent->enterReturnByForceMode(contextIndex, value);
576 resume();
577 }
578}
579
580/*!
581 Evaluates the given \a program. When evaluation is complete, an
582 event() is generated.
583*/
584void QScriptDebuggerBackend::evaluate(int contextIndex, const QString &program,
585 const QString &fileName, int lineNumber)
586{
587 Q_D(QScriptDebuggerBackend);
588 d->pendingEvaluateContextIndex = contextIndex;
589 d->pendingEvaluateProgram = program;
590 d->pendingEvaluateFileName = fileName;
591 d->pendingEvaluateLineNumber = lineNumber;
592 if (!engine()->isEvaluating())
593 doPendingEvaluate(/*postEvent=*/true);
594 else
595 resume();
596}
597
598/*!
599 Executes the pending evaluate, if any.
600*/
601void QScriptDebuggerBackend::doPendingEvaluate(bool postEvent)
602{
603 Q_D(QScriptDebuggerBackend);
604 QString program = d->pendingEvaluateProgram;
605 if (program.isEmpty())
606 return;
607 int contextIndex = d->pendingEvaluateContextIndex;
608 QScriptContext *ctx = context(index: contextIndex);
609 Q_ASSERT(ctx != 0);
610 QString fileName = d->pendingEvaluateFileName;
611 int lineNumber = d->pendingEvaluateLineNumber;
612 d->pendingEvaluateProgram = QString();
613 d->pendingEvaluateFileName = QString();
614 d->pendingEvaluateLineNumber = -1;
615 d->pendingEvaluateContextIndex = -1;
616
617 // push a new context and initialize its scope chain etc.
618 {
619 QScriptContext *evalContext = engine()->pushContext();
620 QScriptValueList scopeChain = ctx->scopeChain();
621 if (scopeChain.isEmpty())
622 scopeChain.append(t: engine()->globalObject());
623 while (!scopeChain.isEmpty())
624 evalContext->pushScope(object: scopeChain.takeLast());
625 evalContext->setActivationObject(ctx->activationObject());
626 evalContext->setThisObject(ctx->thisObject());
627 }
628
629 d->agent->enterContinueMode();
630 // set a flag so that any exception that happens in
631 // the evaluate() is not sent to the debugger
632 d->ignoreExceptions = true;
633 bool hadException = engine()->hasUncaughtException();
634 QScriptValue ret = engine()->evaluate(program, fileName, lineNumber);
635 d->ignoreExceptions = false;
636 if (!hadException && engine()->hasUncaughtException())
637 engine()->clearExceptions();
638 engine()->popContext();
639
640 QScriptDebuggerValue retret(ret);
641 QScriptDebuggerEvent e(QScriptDebuggerEvent::InlineEvalFinished);
642 e.setScriptValue(retret);
643 if (!ret.isUndefined())
644 e.setMessage(ret.toString()); // for convenience -- we always need it
645
646 e.setNestedEvaluate(engine()->isEvaluating());
647
648 if (postEvent) {
649 QScriptDebuggerEventEvent *de = new QScriptDebuggerEventEvent(e);
650 d->postEvent(e: de);
651 } else {
652 event(event: e);
653 }
654}
655
656/*!
657 Sets a breakpoint defined by the given \a data, and returns a unique
658 identifier for the new breakpoint.
659
660 If the conditions of the breakpoint is satisfied at some point
661 during script evaluation, a breakpoint event() will be generated.
662
663 \sa deleteBreakpoint(), breakpoints()
664*/
665int QScriptDebuggerBackend::setBreakpoint(const QScriptBreakpointData &data)
666{
667 Q_D(QScriptDebuggerBackend);
668 if (!d->agent)
669 return -1;
670 if (!data.isValid())
671 return -1;
672 return d->agent->setBreakpoint(data);
673}
674
675/*!
676 Deletes the breakpoint identified by the given \a id. Returns true
677 if the breakpoint was deleted (i.e. the \a id was valid), otherwise
678 returns false.
679
680 \sa setBreakpoint()
681*/
682bool QScriptDebuggerBackend::deleteBreakpoint(int id)
683{
684 Q_D(QScriptDebuggerBackend);
685 if (!d->agent)
686 return false;
687 return d->agent->deleteBreakpoint(id);
688}
689
690/*!
691 Deletes all breakpoints.
692*/
693void QScriptDebuggerBackend::deleteAllBreakpoints()
694{
695 Q_D(QScriptDebuggerBackend);
696 if (d->agent)
697 d->agent->deleteAllBreakpoints();
698}
699
700/*!
701 Returns the data associated with the breakpoint identified by the
702 given \a id.
703*/
704QScriptBreakpointData QScriptDebuggerBackend::breakpointData(int id) const
705{
706 Q_D(const QScriptDebuggerBackend);
707 if (!d->agent)
708 return QScriptBreakpointData();
709 return d->agent->breakpointData(id);
710}
711
712/*!
713 Sets the \a data associated with the breakpoint identified by the
714 given \a id.
715*/
716bool QScriptDebuggerBackend::setBreakpointData(int id, const QScriptBreakpointData &data)
717{
718 Q_D(QScriptDebuggerBackend);
719 if (d->agent)
720 return d->agent->setBreakpointData(id, data);
721 return false;
722}
723
724/*!
725 Returns this backend's breakpoints.
726
727 \sa setBreakpoint()
728*/
729QScriptBreakpointMap QScriptDebuggerBackend::breakpoints() const
730{
731 Q_D(const QScriptDebuggerBackend);
732 if (!d->agent)
733 return QScriptBreakpointMap();
734 return d->agent->breakpoints();
735}
736
737/*!
738 Returns the scripts that this backend knows about.
739
740 \sa scriptData()
741*/
742QScriptScriptMap QScriptDebuggerBackend::scripts() const
743{
744 Q_D(const QScriptDebuggerBackend);
745 if (!d->agent)
746 return QScriptScriptMap();
747 return d->agent->scripts();
748}
749
750/*!
751 Returns the data for the script identified by the given \a id.
752
753 \sa scripts()
754*/
755QScriptScriptData QScriptDebuggerBackend::scriptData(qint64 id) const
756{
757 Q_D(const QScriptDebuggerBackend);
758 if (!d->agent)
759 return QScriptScriptData();
760 return d->agent->scriptData(id);
761}
762
763/*!
764 Makes a checkpoint of the currently loaded scripts.
765
766 \sa scriptsDelta()
767*/
768void QScriptDebuggerBackend::scriptsCheckpoint()
769{
770 Q_D(QScriptDebuggerBackend);
771 if (d->agent)
772 d->agent->scriptsCheckpoint();
773}
774
775/*!
776 Returns the difference between the latest scripts checkpoint and the
777 previous checkpoint. The first item in the pair is a list
778 containing the identifiers of the scripts that were added. The
779 second item in the pair is a list containing the identifiers of the
780 scripts that were removed.
781
782 \sa scriptsCheckpoint()
783*/
784QScriptScriptsDelta QScriptDebuggerBackend::scriptsDelta() const
785{
786 Q_D(const QScriptDebuggerBackend);
787 if (!d->agent)
788 return QPair<QList<qint64>, QList<qint64> >();
789 return d->agent->scriptsDelta();
790}
791
792qint64 QScriptDebuggerBackend::resolveScript(const QString &fileName) const
793{
794 Q_D(const QScriptDebuggerBackend);
795 if (!d->agent)
796 return -1;
797 return d->agent->resolveScript(fileName);
798}
799
800/*!
801 Returns the number of contexts (frames).
802*/
803int QScriptDebuggerBackend::contextCount() const
804{
805 if (!engine())
806 return 0;
807 return contextIds().count();
808}
809
810/*!
811 Returns the context for the frame with the given \a index.
812*/
813QScriptContext *QScriptDebuggerBackend::context(int index) const
814{
815 if (index < 0)
816 return 0;
817 QScriptContext *ctx = engine()->currentContext();
818 while (ctx) {
819 if (index == 0)
820 return ctx;
821 ctx = ctx->parentContext();
822 --index;
823 }
824 return 0;
825}
826
827/*!
828 Returns a backtrace of the current execution.
829*/
830QStringList QScriptDebuggerBackend::backtrace() const
831{
832 if (!engine())
833 return QStringList();
834 return engine()->currentContext()->backtrace();
835}
836
837QList<qint64> QScriptDebuggerBackend::contextIds() const
838{
839 Q_D(const QScriptDebuggerBackend);
840 if (!d->agent)
841 return QList<qint64>();
842 return d->agent->contextIds();
843}
844
845QScriptContextsDelta QScriptDebuggerBackend::contextsCheckpoint()
846{
847 Q_D(QScriptDebuggerBackend);
848 if (!d->agent)
849 return QScriptContextsDelta();
850 return d->agent->contextsCheckpoint();
851}
852
853int QScriptDebuggerBackend::newScriptObjectSnapshot()
854{
855 Q_D(QScriptDebuggerBackend);
856 int id = d->nextScriptObjectSnapshotId;
857 ++d->nextScriptObjectSnapshotId;
858 d->scriptObjectSnapshots[id] = new QScriptObjectSnapshot();
859 return id;
860}
861
862QScriptObjectSnapshot *QScriptDebuggerBackend::scriptObjectSnapshot(int id) const
863{
864 Q_D(const QScriptDebuggerBackend);
865 return d->scriptObjectSnapshots.value(akey: id);
866}
867
868void QScriptDebuggerBackend::deleteScriptObjectSnapshot(int id)
869{
870 Q_D(QScriptDebuggerBackend);
871 QScriptObjectSnapshot *snap = d->scriptObjectSnapshots.take(akey: id);
872 delete snap;
873}
874
875int QScriptDebuggerBackend::newScriptValueIterator(const QScriptValue &object)
876{
877 Q_D(QScriptDebuggerBackend);
878 int id = d->nextScriptValueIteratorId;
879 ++d->nextScriptValueIteratorId;
880 d->scriptValueIterators[id] = new QScriptValueIterator(object);
881 return id;
882}
883
884QScriptValueIterator *QScriptDebuggerBackend::scriptValueIterator(int id) const
885{
886 Q_D(const QScriptDebuggerBackend);
887 return d->scriptValueIterators.value(akey: id);
888}
889
890void QScriptDebuggerBackend::deleteScriptValueIterator(int id)
891{
892 Q_D(QScriptDebuggerBackend);
893 QScriptValueIterator *it = d->scriptValueIterators.take(akey: id);
894 delete it;
895}
896
897bool QScriptDebuggerBackend::ignoreExceptions() const
898{
899 Q_D(const QScriptDebuggerBackend);
900 return d->ignoreExceptions;
901}
902
903void QScriptDebuggerBackend::setIgnoreExceptions(bool ignore)
904{
905 Q_D(QScriptDebuggerBackend);
906 d->ignoreExceptions = ignore;
907}
908
909/*!
910 Returns a trace function. The trace function has similar semantics
911 to the built-in print() function; however, instead of writing text
912 to standard output, it generates a trace event containing the text.
913*/
914QScriptValue QScriptDebuggerBackend::traceFunction() const
915{
916 Q_D(const QScriptDebuggerBackend);
917 if (!engine())
918 return QScriptValue();
919 QScriptValue fun = engine()->newFunction(signature: QScriptDebuggerBackendPrivate::trace);
920 fun.setData(qScriptValueFromValue(engine: engine(), t: const_cast<QScriptDebuggerBackendPrivate*>(d)));
921 return fun;
922}
923
924QScriptValue QScriptDebuggerBackend::assertFunction() const
925{
926 if (!engine())
927 return QScriptValue();
928 QScriptValue fun = engine()->newFunction(signature: QScriptDebuggerBackendPrivate::qsassert);
929 return fun;
930}
931
932QScriptValue QScriptDebuggerBackend::fileNameFunction() const
933{
934 if (!engine())
935 return QScriptValue();
936 QScriptValue fun = engine()->newFunction(signature: QScriptDebuggerBackendPrivate::fileName);
937 return fun;
938}
939
940QScriptValue QScriptDebuggerBackend::lineNumberFunction() const
941{
942 if (!engine())
943 return QScriptValue();
944 QScriptValue fun = engine()->newFunction(signature: QScriptDebuggerBackendPrivate::lineNumber);
945 return fun;
946}
947
948QScriptDebuggerCommandExecutor *QScriptDebuggerBackend::commandExecutor() const
949{
950 Q_D(const QScriptDebuggerBackend);
951 if (d->commandExecutor)
952 return d->commandExecutor;
953 QScriptDebuggerBackendPrivate *dd = const_cast<QScriptDebuggerBackendPrivate*>(d);
954 dd->commandExecutor = new QScriptDebuggerCommandExecutor();
955 return dd->commandExecutor;
956}
957
958void QScriptDebuggerBackend::setCommandExecutor(QScriptDebuggerCommandExecutor *executor)
959{
960 Q_D(QScriptDebuggerBackend);
961 d->commandExecutor = executor;
962}
963
964/*!
965 \fn void QScriptDebuggerBackend::resume()
966
967 This function is called when control should be returned back to the
968 back-end, i.e. when script evaluation should be resumed after an
969 event has been delivered.
970
971 Subclasses must reimplement this function to make event() return.
972
973 \sa event()
974*/
975
976/*!
977 \fn void QScriptDebuggerBackend::event(const QScriptDebuggerEvent &event)
978
979 This function is called when the back-end has generated the given \a event.
980
981 Subclasses must reimplement this function to handle the
982 event. Typically the event is forwarded to a
983 QScriptDebuggerFrontend, which will in turn forward it to its
984 QScriptDebuggerClient. The client may then query the front-end for
985 information about the execution state, and call e.g.
986 continueEvalution() to resume execution. This function should block
987 until resume() is called.
988
989 \sa resume()
990*/
991
992QT_END_NAMESPACE
993

source code of qtscript/src/scripttools/debugging/qscriptdebuggerbackend.cpp