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 "qscriptdebuggeragent_p.h"
41#include "qscriptdebuggeragent_p_p.h"
42#include "qscriptdebuggerbackend_p_p.h"
43
44#include <QtCore/qcoreapplication.h>
45#include <QtCore/qset.h>
46#include <QtScript/qscriptengine.h>
47
48QT_BEGIN_NAMESPACE
49
50/*!
51 \since 4.5
52 \class QScriptDebuggerAgent
53 \internal
54
55 This class implements a state machine that uses the low-level events
56 reported by the QScriptEngineAgent interface to implement debugging-
57 specific functionality such as stepping and breakpoints. It is used
58 internally by the QScriptDebuggerBackend class.
59*/
60
61QScriptDebuggerAgentPrivate::QScriptDebuggerAgentPrivate()
62 : state(NoState), stepDepth(0), stepCount(0),
63 targetScriptId(-1), targetLineNumber(-1), returnCounter(0),
64 nextBreakpointId(1), hitBreakpointId(0),
65 nextContextId(0), statementCounter(0)
66{
67}
68
69QScriptDebuggerAgentPrivate::~QScriptDebuggerAgentPrivate()
70{
71}
72
73QScriptDebuggerAgentPrivate *QScriptDebuggerAgentPrivate::get(
74 QScriptDebuggerAgent *q)
75{
76 if (!q)
77 return 0;
78 return q->d_func();
79}
80
81
82/*!
83 Constructs a new agent for the given \a engine. The agent will
84 report debugging-related events (e.g. step completion) to the given
85 \a backend.
86*/
87QScriptDebuggerAgent::QScriptDebuggerAgent(
88 QScriptDebuggerBackendPrivate *backend, QScriptEngine *engine)
89 : QScriptEngineAgent(engine), d_ptr(new QScriptDebuggerAgentPrivate())
90{
91 Q_D(QScriptDebuggerAgent);
92 d->backend = backend;
93
94 QScriptContext *ctx = engine->currentContext();
95 while (ctx) {
96 d->scriptIdStack.append(t: QList<qint64>());
97 d->contextIdStack.append(t: d->nextContextId);
98 ++d->nextContextId;
99 ctx = ctx->parentContext();
100 }
101}
102
103/*!
104 Destroys this QScriptDebuggerAgent.
105*/
106QScriptDebuggerAgent::~QScriptDebuggerAgent()
107{
108 Q_D(QScriptDebuggerAgent);
109 if (d->backend)
110 d->backend->agentDestroyed(this);
111 delete d;
112}
113
114/*!
115 Instructs the agent to perform a "step into" operation. This
116 function returns immediately. The agent will report step completion
117 at a later time, i.e. when script statements are evaluated.
118*/
119void QScriptDebuggerAgent::enterStepIntoMode(int count)
120{
121 Q_D(QScriptDebuggerAgent);
122 d->state = QScriptDebuggerAgentPrivate::SteppingIntoState;
123 d->stepCount = count;
124 d->stepResult = QScriptValue();
125}
126
127/*!
128 Instructs the agent to perform a "step over" operation. This
129 function returns immediately. The agent will report step completion
130 at a later time, i.e. when script statements are evaluated.
131*/
132void QScriptDebuggerAgent::enterStepOverMode(int count)
133{
134 Q_D(QScriptDebuggerAgent);
135 d->state = QScriptDebuggerAgentPrivate::SteppingOverState;
136 if (engine()->isEvaluating())
137 d->stepDepth = 0;
138 else
139 d->stepDepth = -1;
140 d->stepCount = count;
141 d->stepResult = QScriptValue();
142}
143
144/*!
145 Instructs the agent to perform a "step out" operation. This
146 function returns immediately. The agent will report step completion
147 at a later time, i.e. when script statements are evaluated.
148*/
149void QScriptDebuggerAgent::enterStepOutMode()
150{
151 Q_D(QScriptDebuggerAgent);
152 d->state = QScriptDebuggerAgentPrivate::SteppingOutState;
153 if (engine()->isEvaluating())
154 d->stepDepth = 0;
155 else
156 d->stepDepth = -1;
157}
158
159/*!
160 Instructs the agent to continue evaluation.
161 This function returns immediately.
162*/
163void QScriptDebuggerAgent::enterContinueMode()
164{
165 Q_D(QScriptDebuggerAgent);
166 d->state = QScriptDebuggerAgentPrivate::NoState;
167}
168
169/*!
170 Instructs the agent to interrupt evaluation.
171 This function returns immediately.
172*/
173void QScriptDebuggerAgent::enterInterruptMode()
174{
175 Q_D(QScriptDebuggerAgent);
176 d->state = QScriptDebuggerAgentPrivate::InterruptingState;
177}
178
179/*!
180 Instructs the agent to continue evaluation until the location
181 described by \a fileName and \a lineNumber is reached. This
182 function returns immediately.
183*/
184void QScriptDebuggerAgent::enterRunToLocationMode(const QString &fileName, int lineNumber)
185{
186 Q_D(QScriptDebuggerAgent);
187 d->targetFileName = fileName;
188 d->targetLineNumber = lineNumber;
189 d->targetScriptId = resolveScript(fileName);
190 d->state = QScriptDebuggerAgentPrivate::RunningToLocationState;
191}
192
193/*!
194 Instructs the agent to continue evaluation until the location
195 described by \a scriptId and \a lineNumber is reached. This
196 function returns immediately.
197*/
198void QScriptDebuggerAgent::enterRunToLocationMode(qint64 scriptId, int lineNumber)
199{
200 Q_D(QScriptDebuggerAgent);
201 d->targetScriptId = scriptId;
202 d->targetFileName = QString();
203 d->targetLineNumber = lineNumber;
204 d->state = QScriptDebuggerAgentPrivate::RunningToLocationState;
205}
206
207void QScriptDebuggerAgent::enterReturnByForceMode(int contextIndex, const QScriptValue &value)
208{
209 Q_D(QScriptDebuggerAgent);
210 d->returnCounter = contextIndex + 1;
211 d->returnValue = QScriptValue();
212 d->state = QScriptDebuggerAgentPrivate::ReturningByForceState;
213 // throw an exception; we will catch it when the proper frame is popped
214 engine()->currentContext()->throwValue(value);
215}
216
217/*!
218 Sets a breakpoint defined by the given \a data.
219 Returns an integer that uniquely identifies the new breakpoint,
220 or -1 if setting the breakpoint failed.
221*/
222int QScriptDebuggerAgent::setBreakpoint(const QScriptBreakpointData &data)
223{
224 Q_D(QScriptDebuggerAgent);
225 qint64 scriptId = data.scriptId();
226 if (scriptId != -1) {
227 if (!d->scripts.contains(akey: scriptId)) {
228 // that script has been unloaded, so invalidate the ID
229 scriptId = -1;
230 const_cast<QScriptBreakpointData&>(data).setScriptId(-1);
231 } else if (data.fileName().isEmpty()) {
232 QString fileName = d->scripts[scriptId].fileName();
233 const_cast<QScriptBreakpointData&>(data).setFileName(fileName);
234 }
235 }
236
237 int id = d->nextBreakpointId;
238 ++d->nextBreakpointId;
239
240 if (scriptId != -1) {
241 d->resolvedBreakpoints[scriptId].append(t: id);
242 } else {
243 QString fileName = data.fileName();
244 bool resolved = false;
245 QScriptScriptMap::const_iterator it;
246 for (it = d->scripts.constBegin(); it != d->scripts.constEnd(); ++it) {
247 if (it.value().fileName() == fileName) {
248 d->resolvedBreakpoints[it.key()].append(t: id);
249 resolved = true;
250 break;
251 }
252 }
253 if (!resolved)
254 d->unresolvedBreakpoints[fileName].append(t: id);
255 }
256
257 d->breakpoints.insert(akey: id, avalue: data);
258
259 return id;
260}
261
262/*!
263 Deletes the breakpoint with the given \a id.
264 Returns true if the breakpoint was deleted, false if
265 no such breakpoint exists.
266*/
267bool QScriptDebuggerAgent::deleteBreakpoint(int id)
268{
269 Q_D(QScriptDebuggerAgent);
270 if (!d->breakpoints.contains(akey: id))
271 return false;
272 d->breakpoints.remove(akey: id);
273 bool found = false;
274 {
275 QHash<qint64, QList<int> >::iterator it;
276 it = d->resolvedBreakpoints.begin();
277 for ( ; !found && (it != d->resolvedBreakpoints.end()); ) {
278 QList<int> &lst = it.value();
279 Q_ASSERT(!lst.isEmpty());
280 for (int i = 0; i < lst.size(); ++i) {
281 if (lst.at(i) == id) {
282 lst.removeAt(i);
283 found = true;
284 break;
285 }
286 }
287 if (lst.isEmpty())
288 it = d->resolvedBreakpoints.erase(it);
289 else
290 ++it;
291 }
292 }
293 if (!found) {
294 QHash<QString, QList<int> >::iterator it;
295 it = d->unresolvedBreakpoints.begin();
296 for ( ; !found && (it != d->unresolvedBreakpoints.end()); ) {
297 QList<int> &lst = it.value();
298 Q_ASSERT(!lst.isEmpty());
299 for (int i = 0; i < lst.size(); ++i) {
300 if (lst.at(i) == id) {
301 lst.removeAt(i);
302 found = true;
303 break;
304 }
305 }
306 if (lst.isEmpty())
307 it = d->unresolvedBreakpoints.erase(it);
308 else
309 ++it;
310 }
311 }
312 return found;
313}
314
315/*!
316 Deletes all breakpoints.
317*/
318void QScriptDebuggerAgent::deleteAllBreakpoints()
319{
320 Q_D(QScriptDebuggerAgent);
321 d->breakpoints.clear();
322 d->resolvedBreakpoints.clear();
323 d->unresolvedBreakpoints.clear();
324}
325
326/*!
327 Returns the data associated with the breakpoint with the given \a
328 id.
329*/
330QScriptBreakpointData QScriptDebuggerAgent::breakpointData(int id) const
331{
332 Q_D(const QScriptDebuggerAgent);
333 return d->breakpoints.value(akey: id);
334}
335
336/*!
337 Sets the data associated with the breakpoint with the given \a
338 id.
339*/
340bool QScriptDebuggerAgent::setBreakpointData(int id,
341 const QScriptBreakpointData &data)
342{
343 Q_D(QScriptDebuggerAgent);
344 if (!d->breakpoints.contains(akey: id))
345 return false;
346 d->breakpoints[id] = data;
347 return true;
348}
349
350/*!
351 Returns all breakpoints.
352*/
353QScriptBreakpointMap QScriptDebuggerAgent::breakpoints() const
354{
355 Q_D(const QScriptDebuggerAgent);
356 return d->breakpoints;
357}
358
359/*!
360 Returns all scripts.
361*/
362QScriptScriptMap QScriptDebuggerAgent::scripts() const
363{
364 Q_D(const QScriptDebuggerAgent);
365 return d->scripts;
366}
367
368/*!
369 Returns the data associated with the script with the given \a id.
370*/
371QScriptScriptData QScriptDebuggerAgent::scriptData(qint64 id) const
372{
373 Q_D(const QScriptDebuggerAgent);
374 return d->scripts.value(akey: id);
375}
376
377/*!
378 Checkpoints the current scripts.
379*/
380void QScriptDebuggerAgent::scriptsCheckpoint()
381{
382 Q_D(QScriptDebuggerAgent);
383 d->previousCheckpointScripts = d->checkpointScripts;
384 d->checkpointScripts = d->scripts;
385}
386
387/*!
388 Returns the difference between the current checkpoint and the
389 previous checkpoint. The first item in the pair is a list containing
390 the identifiers of the scripts that were added. The second item in
391 the pair is a list containing the identifiers of the scripts that
392 were removed.
393*/
394QPair<QList<qint64>, QList<qint64> > QScriptDebuggerAgent::scriptsDelta() const
395{
396 Q_D(const QScriptDebuggerAgent);
397 QSet<qint64> prevSet = d->previousCheckpointScripts.keys().toSet();
398 QSet<qint64> currSet = d->checkpointScripts.keys().toSet();
399 QSet<qint64> addedScriptIds = currSet - prevSet;
400 QSet<qint64> removedScriptIds = prevSet - currSet;
401 return qMakePair(x: addedScriptIds.toList(), y: removedScriptIds.toList());
402}
403
404/*!
405 Returns the identifier of the script that has the given \a fileName,
406 or -1 if there is no such script.
407*/
408qint64 QScriptDebuggerAgent::resolveScript(const QString &fileName) const
409{
410 Q_D(const QScriptDebuggerAgent);
411 QScriptScriptMap::const_iterator it;
412 for (it = d->scripts.constBegin(); it != d->scripts.constEnd(); ++it) {
413 if (it.value().fileName() == fileName)
414 return it.key();
415 }
416 return -1;
417}
418
419QList<qint64> QScriptDebuggerAgent::contextIds() const
420{
421 Q_D(const QScriptDebuggerAgent);
422 return d->contextIdStack;
423}
424
425QPair<QList<qint64>, QList<qint64> > QScriptDebuggerAgent::contextsCheckpoint()
426{
427 Q_D(QScriptDebuggerAgent);
428 int i = d->checkpointContextIdStack.size() - 1;
429 int j = d->contextIdStack.size() - 1;
430 for ( ; (i >= 0) && (j >= 0); --i, --j) {
431 if (d->checkpointContextIdStack.at(i) != d->contextIdStack.at(i: j))
432 break;
433 }
434 QList<qint64> removed = d->checkpointContextIdStack.mid(pos: 0, alength: i+1);
435 QList<qint64> added = d->contextIdStack.mid(pos: 0, alength: j+1);
436 d->checkpointContextIdStack = d->contextIdStack;
437 return qMakePair(x: removed, y: added);
438}
439
440void QScriptDebuggerAgent::nullifyBackendPointer()
441{
442 Q_D(QScriptDebuggerAgent);
443 d->backend = 0;
444}
445
446/*!
447 \reimp
448*/
449void QScriptDebuggerAgent::scriptLoad(qint64 id, const QString &program,
450 const QString &fileName, int baseLineNumber)
451{
452 Q_D(QScriptDebuggerAgent);
453 QScriptScriptData data = QScriptScriptData(program, fileName, baseLineNumber);
454 d->scripts.insert(akey: id, avalue: data);
455
456 if ((d->state == QScriptDebuggerAgentPrivate::RunningToLocationState)
457 && (d->targetScriptId == -1)
458 && ((d->targetFileName == fileName) || d->targetFileName.isEmpty())) {
459 d->targetScriptId = id;
460 }
461
462 if (!fileName.isEmpty()) {
463 QList<int> lst = d->unresolvedBreakpoints.take(akey: fileName);
464 if (!lst.isEmpty())
465 d->resolvedBreakpoints.insert(akey: id, avalue: lst);
466 }
467}
468
469/*!
470 \reimp
471*/
472void QScriptDebuggerAgent::scriptUnload(qint64 id)
473{
474 Q_D(QScriptDebuggerAgent);
475 QScriptScriptData data = d->scripts.take(akey: id);
476 QString fileName = data.fileName();
477
478 if ((d->state == QScriptDebuggerAgentPrivate::RunningToLocationState)
479 && (d->targetScriptId == id)) {
480 d->targetScriptId = -1;
481 d->targetFileName = fileName;
482 }
483
484 if (!fileName.isEmpty()) {
485 QList<int> lst = d->resolvedBreakpoints.take(akey: id);
486 if (!lst.isEmpty())
487 d->unresolvedBreakpoints.insert(akey: fileName, avalue: lst);
488 }
489}
490
491/*!
492 \reimp
493*/
494void QScriptDebuggerAgent::contextPush()
495{
496 Q_D(QScriptDebuggerAgent);
497 d->scriptIdStack.append(t: QList<qint64>());
498 d->contextIdStack.prepend(t: d->nextContextId);
499 ++d->nextContextId;
500}
501
502/*!
503 \reimp
504*/
505void QScriptDebuggerAgent::contextPop()
506{
507 Q_D(QScriptDebuggerAgent);
508 d->scriptIdStack.removeLast();
509 d->contextIdStack.removeFirst();
510}
511
512/*!
513 \reimp
514*/
515void QScriptDebuggerAgent::functionEntry(qint64 scriptId)
516{
517 Q_D(QScriptDebuggerAgent);
518 QList<qint64> &ids = d->scriptIdStack.last();
519 ids.append(t: scriptId);
520 if ((d->state == QScriptDebuggerAgentPrivate::SteppingOverState)
521 || (d->state == QScriptDebuggerAgentPrivate::SteppingOutState)) {
522 ++d->stepDepth;
523 }
524}
525
526/*!
527 \reimp
528*/
529void QScriptDebuggerAgent::functionExit(qint64 scriptId,
530 const QScriptValue &returnValue)
531{
532 Q_UNUSED(scriptId);
533 Q_D(QScriptDebuggerAgent);
534 QList<qint64> &ids = d->scriptIdStack.last();
535 ids.removeLast();
536 if (d->state == QScriptDebuggerAgentPrivate::SteppingOverState) {
537 --d->stepDepth;
538 } else if (d->state == QScriptDebuggerAgentPrivate::SteppingOutState) {
539 if (--d->stepDepth < 0) {
540 d->stepResult = returnValue;
541 d->state = QScriptDebuggerAgentPrivate::SteppedOutState;
542 }
543 } else if (d->state == QScriptDebuggerAgentPrivate::ReturningByForceState) {
544 if (--d->returnCounter == 0) {
545 d->returnValue = returnValue;
546 d->state = QScriptDebuggerAgentPrivate::ReturnedByForceState;
547 engine()->clearExceptions();
548 }
549 }
550}
551
552/*!
553 \reimp
554*/
555void QScriptDebuggerAgent::positionChange(qint64 scriptId,
556 int lineNumber, int columnNumber)
557{
558 Q_D(QScriptDebuggerAgent);
559 if (engine()->processEventsInterval() == -1) {
560 // see if it's time to call processEvents()
561 if ((++d->statementCounter % 25000) == 0) {
562 if (d->processEventsTimer.isValid()) {
563 if (d->processEventsTimer.elapsed() > 30) {
564 QCoreApplication::processEvents();
565 d->processEventsTimer.restart();
566 }
567 } else {
568 d->processEventsTimer.start();
569 }
570 }
571 }
572
573 // check breakpoints
574 {
575 QList<int> lst = d->resolvedBreakpoints.value(akey: scriptId);
576 for (int i = 0; i < lst.size(); ++i) {
577 int id = lst.at(i);
578 QScriptBreakpointData &data = d->breakpoints[id];
579 if (!data.isEnabled())
580 continue;
581 if (data.lineNumber() != lineNumber)
582 continue;
583 if (!data.condition().isEmpty()) {
584 // ### careful, evaluate() can cause an exception
585 // ### disable callbacks in nested evaluate?
586 QScriptDebuggerAgentPrivate::State was = d->state;
587 d->state = QScriptDebuggerAgentPrivate::NoState;
588 QScriptValue ret = engine()->evaluate(
589 program: data.condition(),
590 fileName: QString::fromLatin1(str: "Breakpoint %0 condition checker").arg(a: id));
591 if (!ret.isError())
592 d->state = was;
593 if (!ret.toBoolean())
594 continue;
595 }
596 if (!data.hit())
597 continue;
598 d->hitBreakpointId = id;
599 d->state = QScriptDebuggerAgentPrivate::BreakpointState;
600 }
601 }
602
603 switch (d->state) {
604 case QScriptDebuggerAgentPrivate::NoState:
605 case QScriptDebuggerAgentPrivate::SteppingOutState:
606 case QScriptDebuggerAgentPrivate::ReturningByForceState:
607 // Do nothing
608 break;
609
610 case QScriptDebuggerAgentPrivate::SteppingIntoState:
611 if (--d->stepCount == 0) {
612 d->state = QScriptDebuggerAgentPrivate::NoState;
613 if (d->backend)
614 d->backend->stepped(scriptId, lineNumber, columnNumber, result: QScriptValue());
615 }
616 break;
617
618 case QScriptDebuggerAgentPrivate::SteppingOverState:
619 if ((d->stepDepth > 0) || (--d->stepCount != 0))
620 break;
621 // fallthrough
622 case QScriptDebuggerAgentPrivate::SteppedOverState:
623 d->state = QScriptDebuggerAgentPrivate::NoState;
624 if (d->backend)
625 d->backend->stepped(scriptId, lineNumber, columnNumber, result: d->stepResult);
626 break;
627
628 case QScriptDebuggerAgentPrivate::SteppedOutState:
629 d->state = QScriptDebuggerAgentPrivate::NoState;
630 if (d->backend)
631 d->backend->stepped(scriptId, lineNumber, columnNumber, result: d->stepResult);
632 break;
633
634 case QScriptDebuggerAgentPrivate::RunningToLocationState:
635 if (((lineNumber == d->targetLineNumber) || (d->targetLineNumber == -1))
636 && (scriptId == d->targetScriptId)) {
637 d->state = QScriptDebuggerAgentPrivate::NoState;
638 if (d->backend)
639 d->backend->locationReached(scriptId, lineNumber, columnNumber);
640 }
641 break;
642
643 case QScriptDebuggerAgentPrivate::InterruptingState:
644 d->state = QScriptDebuggerAgentPrivate::NoState;
645 if (d->backend)
646 d->backend->interrupted(scriptId, lineNumber, columnNumber);
647 break;
648
649 case QScriptDebuggerAgentPrivate::BreakpointState:
650 d->state = QScriptDebuggerAgentPrivate::NoState;
651 if (d->backend)
652 d->backend->breakpoint(scriptId, lineNumber, columnNumber, breakpointId: d->hitBreakpointId);
653 if (d->breakpoints.value(akey: d->hitBreakpointId).isSingleShot())
654 deleteBreakpoint(id: d->hitBreakpointId);
655 break;
656
657 case QScriptDebuggerAgentPrivate::ReturnedByForceState:
658 d->state = QScriptDebuggerAgentPrivate::NoState;
659 if (d->backend)
660 d->backend->forcedReturn(scriptId, lineNumber, columnNumber, value: d->returnValue);
661 break;
662
663 case QScriptDebuggerAgentPrivate::SteppedIntoState:
664 case QScriptDebuggerAgentPrivate::ReachedLocationState:
665 case QScriptDebuggerAgentPrivate::InterruptedState:
666// ### deal with the case when code is evaluated while we're already paused
667// Q_ASSERT(false);
668 break;
669 }
670}
671
672/*!
673 \reimp
674*/
675void QScriptDebuggerAgent::exceptionThrow(qint64 scriptId,
676 const QScriptValue &exception,
677 bool hasHandler)
678{
679 Q_D(QScriptDebuggerAgent);
680 if (d->state == QScriptDebuggerAgentPrivate::ReturningByForceState) {
681 // we threw this exception ourselves, so ignore it for now
682 // (see functionExit()).
683 return;
684 }
685 if (d->backend)
686 d->backend->exception(scriptId, exception, hasHandler);
687}
688
689/*!
690 \reimp
691*/
692void QScriptDebuggerAgent::exceptionCatch(qint64 scriptId,
693 const QScriptValue &exception)
694{
695 Q_UNUSED(scriptId);
696 Q_UNUSED(exception);
697}
698
699/*!
700 \reimp
701*/
702bool QScriptDebuggerAgent::supportsExtension(Extension extension) const
703{
704 return (extension == DebuggerInvocationRequest);
705}
706
707/*!
708 \reimp
709*/
710QVariant QScriptDebuggerAgent::extension(Extension extension,
711 const QVariant &argument)
712{
713 Q_UNUSED(extension);
714 Q_D(QScriptDebuggerAgent);
715 Q_ASSERT(extension == DebuggerInvocationRequest);
716 QVariantList lst = argument.toList();
717 qint64 scriptId = lst.at(i: 0).toLongLong();
718 int lineNumber = lst.at(i: 1).toInt();
719 int columnNumber = lst.at(i: 2).toInt();
720 d->state = QScriptDebuggerAgentPrivate::NoState;
721 if (d->backend) {
722 d->backend->debuggerInvocationRequest(
723 scriptId, lineNumber, columnNumber);
724 }
725 return QVariant();
726}
727
728QT_END_NAMESPACE
729

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