Qt 4.8
qscriptdebuggeragent.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qscriptdebuggeragent_p.h"
45 
46 #include <QtCore/qcoreapplication.h>
47 #include <QtCore/qset.h>
48 #include <QtScript/qscriptengine.h>
49 
51 
67  : state(NoState), stepDepth(0), stepCount(0),
68  targetScriptId(-1), targetLineNumber(-1), returnCounter(0),
69  nextBreakpointId(1), hitBreakpointId(0),
70  nextContextId(0), statementCounter(0)
71 {
72 }
73 
75 {
76 }
77 
80 {
81  if (!q)
82  return 0;
83  return q->d_func();
84 }
85 
86 
94  : QScriptEngineAgent(engine), d_ptr(new QScriptDebuggerAgentPrivate())
95 {
97  d->backend = backend;
98 
99  QScriptContext *ctx = engine->currentContext();
100  while (ctx) {
101  d->scriptIdStack.append(QList<qint64>());
102  d->contextIdStack.append(d->nextContextId);
103  ++d->nextContextId;
104  ctx = ctx->parentContext();
105  }
106 }
107 
112 {
114  if (d->backend)
115  d->backend->agentDestroyed(this);
116  delete d;
117 }
118 
125 {
128  d->stepCount = count;
129  d->stepResult = QScriptValue();
130 }
131 
138 {
141  if (engine()->isEvaluating())
142  d->stepDepth = 0;
143  else
144  d->stepDepth = -1;
145  d->stepCount = count;
146  d->stepResult = QScriptValue();
147 }
148 
155 {
158  if (engine()->isEvaluating())
159  d->stepDepth = 0;
160  else
161  d->stepDepth = -1;
162 }
163 
169 {
172 }
173 
179 {
182 }
183 
190 {
192  d->targetFileName = fileName;
193  d->targetLineNumber = lineNumber;
194  d->targetScriptId = resolveScript(fileName);
196 }
197 
204 {
206  d->targetScriptId = scriptId;
207  d->targetFileName = QString();
208  d->targetLineNumber = lineNumber;
210 }
211 
212 void QScriptDebuggerAgent::enterReturnByForceMode(int contextIndex, const QScriptValue &value)
213 {
215  d->returnCounter = contextIndex + 1;
216  d->returnValue = QScriptValue();
218  // throw an exception; we will catch it when the proper frame is popped
219  engine()->currentContext()->throwValue(value);
220 }
221 
228 {
230  qint64 scriptId = data.scriptId();
231  if (scriptId != -1) {
232  if (!d->scripts.contains(scriptId)) {
233  // that script has been unloaded, so invalidate the ID
234  scriptId = -1;
235  const_cast<QScriptBreakpointData&>(data).setScriptId(-1);
236  } else if (data.fileName().isEmpty()) {
237  QString fileName = d->scripts[scriptId].fileName();
238  const_cast<QScriptBreakpointData&>(data).setFileName(fileName);
239  }
240  }
241 
242  int id = d->nextBreakpointId;
243  ++d->nextBreakpointId;
244 
245  if (scriptId != -1) {
246  d->resolvedBreakpoints[scriptId].append(id);
247  } else {
248  QString fileName = data.fileName();
249  bool resolved = false;
251  for (it = d->scripts.constBegin(); it != d->scripts.constEnd(); ++it) {
252  if (it.value().fileName() == fileName) {
253  d->resolvedBreakpoints[it.key()].append(id);
254  resolved = true;
255  break;
256  }
257  }
258  if (!resolved)
259  d->unresolvedBreakpoints[fileName].append(id);
260  }
261 
262  d->breakpoints.insert(id, data);
263 
264  return id;
265 }
266 
273 {
275  if (!d->breakpoints.contains(id))
276  return false;
277  d->breakpoints.remove(id);
278  bool found = false;
279  {
280  QHash<qint64, QList<int> >::iterator it;
281  it = d->resolvedBreakpoints.begin();
282  for ( ; !found && (it != d->resolvedBreakpoints.end()); ) {
283  QList<int> &lst = it.value();
284  Q_ASSERT(!lst.isEmpty());
285  for (int i = 0; i < lst.size(); ++i) {
286  if (lst.at(i) == id) {
287  lst.removeAt(i);
288  found = true;
289  break;
290  }
291  }
292  if (lst.isEmpty())
293  it = d->resolvedBreakpoints.erase(it);
294  else
295  ++it;
296  }
297  }
298  if (!found) {
299  QHash<QString, QList<int> >::iterator it;
300  it = d->unresolvedBreakpoints.begin();
301  for ( ; !found && (it != d->unresolvedBreakpoints.end()); ) {
302  QList<int> &lst = it.value();
303  Q_ASSERT(!lst.isEmpty());
304  for (int i = 0; i < lst.size(); ++i) {
305  if (lst.at(i) == id) {
306  lst.removeAt(i);
307  found = true;
308  break;
309  }
310  }
311  if (lst.isEmpty())
312  it = d->unresolvedBreakpoints.erase(it);
313  else
314  ++it;
315  }
316  }
317  return found;
318 }
319 
324 {
326  d->breakpoints.clear();
327  d->resolvedBreakpoints.clear();
328  d->unresolvedBreakpoints.clear();
329 }
330 
336 {
337  Q_D(const QScriptDebuggerAgent);
338  return d->breakpoints.value(id);
339 }
340 
347 {
349  if (!d->breakpoints.contains(id))
350  return false;
351  d->breakpoints[id] = data;
352  return true;
353 }
354 
359 {
360  Q_D(const QScriptDebuggerAgent);
361  return d->breakpoints;
362 }
363 
368 {
369  Q_D(const QScriptDebuggerAgent);
370  return d->scripts;
371 }
372 
377 {
378  Q_D(const QScriptDebuggerAgent);
379  return d->scripts.value(id);
380 }
381 
386 {
388  d->previousCheckpointScripts = d->checkpointScripts;
389  d->checkpointScripts = d->scripts;
390 }
391 
400 {
401  Q_D(const QScriptDebuggerAgent);
402  QSet<qint64> prevSet = d->previousCheckpointScripts.keys().toSet();
403  QSet<qint64> currSet = d->checkpointScripts.keys().toSet();
404  QSet<qint64> addedScriptIds = currSet - prevSet;
405  QSet<qint64> removedScriptIds = prevSet - currSet;
406  return qMakePair(addedScriptIds.toList(), removedScriptIds.toList());
407 }
408 
414 {
415  Q_D(const QScriptDebuggerAgent);
417  for (it = d->scripts.constBegin(); it != d->scripts.constEnd(); ++it) {
418  if (it.value().fileName() == fileName)
419  return it.key();
420  }
421  return -1;
422 }
423 
425 {
426  Q_D(const QScriptDebuggerAgent);
427  return d->contextIdStack;
428 }
429 
431 {
433  int i = d->checkpointContextIdStack.size() - 1;
434  int j = d->contextIdStack.size() - 1;
435  for ( ; (i >= 0) && (j >= 0); --i, --j) {
436  if (d->checkpointContextIdStack.at(i) != d->contextIdStack.at(j))
437  break;
438  }
439  QList<qint64> removed = d->checkpointContextIdStack.mid(0, i+1);
440  QList<qint64> added = d->contextIdStack.mid(0, j+1);
441  d->checkpointContextIdStack = d->contextIdStack;
442  return qMakePair(removed, added);
443 }
444 
446 {
448  d->backend = 0;
449 }
450 
455  const QString &fileName, int baseLineNumber)
456 {
458  QScriptScriptData data = QScriptScriptData(program, fileName, baseLineNumber);
459  d->scripts.insert(id, data);
460 
462  && (d->targetScriptId == -1)
463  && ((d->targetFileName == fileName) || d->targetFileName.isEmpty())) {
464  d->targetScriptId = id;
465  }
466 
467  if (!fileName.isEmpty()) {
468  QList<int> lst = d->unresolvedBreakpoints.take(fileName);
469  if (!lst.isEmpty())
470  d->resolvedBreakpoints.insert(id, lst);
471  }
472 }
473 
478 {
480  QScriptScriptData data = d->scripts.take(id);
481  QString fileName = data.fileName();
482 
484  && (d->targetScriptId == id)) {
485  d->targetScriptId = -1;
486  d->targetFileName = fileName;
487  }
488 
489  if (!fileName.isEmpty()) {
490  QList<int> lst = d->resolvedBreakpoints.take(id);
491  if (!lst.isEmpty())
492  d->unresolvedBreakpoints.insert(fileName, lst);
493  }
494 }
495 
500 {
502  d->scriptIdStack.append(QList<qint64>());
503  d->contextIdStack.prepend(d->nextContextId);
504  ++d->nextContextId;
505 }
506 
511 {
513  d->scriptIdStack.removeLast();
514  d->contextIdStack.removeFirst();
515 }
516 
521 {
523  QList<qint64> &ids = d->scriptIdStack.last();
524  ids.append(scriptId);
527  ++d->stepDepth;
528  }
529 }
530 
535  const QScriptValue &returnValue)
536 {
537  Q_UNUSED(scriptId);
539  QList<qint64> &ids = d->scriptIdStack.last();
540  ids.removeLast();
542  --d->stepDepth;
543  } else if (d->state == QScriptDebuggerAgentPrivate::SteppingOutState) {
544  if (--d->stepDepth < 0) {
545  d->stepResult = returnValue;
547  }
549  if (--d->returnCounter == 0) {
550  d->returnValue = returnValue;
552  engine()->clearExceptions();
553  }
554  }
555 }
556 
561  int lineNumber, int columnNumber)
562 {
564  if (engine()->processEventsInterval() == -1) {
565  // see if it's time to call processEvents()
566  if ((++d->statementCounter % 25000) == 0) {
567  if (!d->processEventsTimer.isNull()) {
568  if (d->processEventsTimer.elapsed() > 30) {
570  d->processEventsTimer.restart();
571  }
572  } else {
573  d->processEventsTimer.start();
574  }
575  }
576  }
577 
578  // check breakpoints
579  {
580  QList<int> lst = d->resolvedBreakpoints.value(scriptId);
581  for (int i = 0; i < lst.size(); ++i) {
582  int id = lst.at(i);
583  QScriptBreakpointData &data = d->breakpoints[id];
584  if (!data.isEnabled())
585  continue;
586  if (data.lineNumber() != lineNumber)
587  continue;
588  if (!data.condition().isEmpty()) {
589  // ### careful, evaluate() can cause an exception
590  // ### disable callbacks in nested evaluate?
593  QScriptValue ret = engine()->evaluate(
594  data.condition(),
595  QString::fromLatin1("Breakpoint %0 condition checker").arg(id));
596  if (!ret.isError())
597  d->state = was;
598  if (!ret.toBoolean())
599  continue;
600  }
601  if (!data.hit())
602  continue;
603  d->hitBreakpointId = id;
605  }
606  }
607 
608  switch (d->state) {
612  // Do nothing
613  break;
614 
616  if (--d->stepCount == 0) {
618  if (d->backend)
619  d->backend->stepped(scriptId, lineNumber, columnNumber, QScriptValue());
620  }
621  break;
622 
624  if ((d->stepDepth > 0) || (--d->stepCount != 0))
625  break;
626  // fallthrough
629  if (d->backend)
630  d->backend->stepped(scriptId, lineNumber, columnNumber, d->stepResult);
631  break;
632 
635  if (d->backend)
636  d->backend->stepped(scriptId, lineNumber, columnNumber, d->stepResult);
637  break;
638 
640  if (((lineNumber == d->targetLineNumber) || (d->targetLineNumber == -1))
641  && (scriptId == d->targetScriptId)) {
643  if (d->backend)
644  d->backend->locationReached(scriptId, lineNumber, columnNumber);
645  }
646  break;
647 
650  if (d->backend)
651  d->backend->interrupted(scriptId, lineNumber, columnNumber);
652  break;
653 
656  if (d->backend)
657  d->backend->breakpoint(scriptId, lineNumber, columnNumber, d->hitBreakpointId);
658  if (d->breakpoints.value(d->hitBreakpointId).isSingleShot())
659  deleteBreakpoint(d->hitBreakpointId);
660  break;
661 
664  if (d->backend)
665  d->backend->forcedReturn(scriptId, lineNumber, columnNumber, d->returnValue);
666  break;
667 
671 // ### deal with the case when code is evaluated while we're already paused
672 // Q_ASSERT(false);
673  break;
674  }
675 }
676 
681  const QScriptValue &exception,
682  bool hasHandler)
683 {
686  // we threw this exception ourselves, so ignore it for now
687  // (see functionExit()).
688  return;
689  }
690  if (d->backend)
691  d->backend->exception(scriptId, exception, hasHandler);
692 }
693 
698  const QScriptValue &exception)
699 {
700  Q_UNUSED(scriptId);
701  Q_UNUSED(exception);
702 }
703 
708 {
709  return (extension == DebuggerInvocationRequest);
710 }
711 
716  const QVariant &argument)
717 {
718  Q_UNUSED(extension);
720  Q_ASSERT(extension == DebuggerInvocationRequest);
721  QVariantList lst = argument.toList();
722  qint64 scriptId = lst.at(0).toLongLong();
723  int lineNumber = lst.at(1).toInt();
724  int columnNumber = lst.at(2).toInt();
726  if (d->backend) {
727  d->backend->debuggerInvocationRequest(
728  scriptId, lineNumber, columnNumber);
729  }
730  return QVariant();
731 }
732 
The QVariant class acts like a union for the most common Qt data types.
Definition: qvariant.h:92
double d
Definition: qnumeric_p.h:62
The QScriptContext class represents a Qt Script function invocation.
QScriptValue evaluate(const QString &program, const QString &fileName=QString(), int lineNumber=1)
Evaluates program, using lineNumber as the base line number, and returns the result of the evaluation...
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
QScriptScriptData scriptData(qint64 id) const
Returns the data associated with the script with the given id.
void enterRunToLocationMode(const QString &fileName, int lineNumber)
Instructs the agent to continue evaluation until the location described by fileName and lineNumber is...
QScriptDebuggerAgent(QScriptDebuggerBackendPrivate *backend, QScriptEngine *engine)
Constructs a new agent for the given engine.
#define it(className, varName)
QScriptEngine * engine() const
Returns the QScriptEngine that this agent is associated with.
void enterReturnByForceMode(int contextIndex, const QScriptValue &value)
bool isError() const
Returns true if this QScriptValue is an object of the Error class; otherwise returns false...
~QScriptDebuggerAgent()
Destroys this QScriptDebuggerAgent.
QList< QVariant > toList() const
Returns the variant as a QVariantList if the variant has type() List or StringList ; otherwise return...
Definition: qvariant.cpp:2751
void removeLast()
Removes the last item in the list.
Definition: qlist.h:287
void exceptionCatch(qint64 scriptId, const QScriptValue &exception)
Reimplemented Function
QScriptBreakpointMap breakpoints() const
Returns all breakpoints.
The QString class provides a Unicode character string.
Definition: qstring.h:83
static QScriptDebuggerAgentPrivate * get(QScriptDebuggerAgent *)
void enterStepIntoMode(int count=1)
Instructs the agent to perform a "step into" operation.
The QHash class is a template class that provides a hash-table-based dictionary.
Definition: qdatastream.h:66
#define Q_ASSERT(cond)
Definition: qglobal.h:1823
#define Q_D(Class)
Definition: qglobal.h:2482
QList< qint64 > contextIds() const
QVariant extension(Extension extension, const QVariant &argument=QVariant())
Reimplemented Function
const T value(const Key &key) const
Returns the value associated with the key.
Definition: qhash.h:606
void contextPush()
Reimplemented Function
void scriptLoad(qint64 id, const QString &program, const QString &fileName, int baseLineNumber)
Reimplemented Function
static void processEvents(QEventLoop::ProcessEventsFlags flags=QEventLoop::AllEvents)
Processes all pending events for the calling thread according to the specified flags until there are ...
bool isEmpty() const
Returns true if the list contains no items; otherwise returns false.
Definition: qlist.h:152
int toInt(bool *ok=0) const
Returns the variant as an int if the variant has type() Int , Bool , ByteArray , Char ...
Definition: qvariant.cpp:2625
The QScriptScriptData class holds data associated with a script.
bool supportsExtension(Extension extension) const
Reimplemented Function
void append(const T &t)
Inserts value at the end of the list.
Definition: qlist.h:507
The QScriptEngine class provides an environment for evaluating Qt Script code.
QScriptScriptMap scripts() const
Returns all scripts.
This class implements a state machine that uses the low-level events reported by the QScriptEngineAge...
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
qlonglong toLongLong(bool *ok=0) const
Returns the variant as a long long int if the variant has type() LongLong , Bool , ByteArray , Char , Double , Int , String , UInt , or ULongLong ; otherwise returns 0.
Definition: qvariant.cpp:2659
QList< T > toList() const
Definition: qset.h:296
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition: qstring.h:704
const T & at(int i) const
Returns the item at index position i in the list.
Definition: qlist.h:468
void positionChange(qint64 scriptId, int lineNumber, int columnNumber)
Reimplemented Function
void exceptionThrow(qint64 scriptId, const QScriptValue &exception, bool hasHandler)
Reimplemented Function
qint64 resolveScript(const QString &fileName) const
Returns the identifier of the script that has the given fileName, or -1 if there is no such script...
static const char * data(const QByteArray &arr)
void functionExit(qint64 scriptId, const QScriptValue &returnValue)
Reimplemented Function
__int64 qint64
Definition: qglobal.h:942
void scriptUnload(qint64 id)
Reimplemented Function
void functionEntry(qint64 scriptId)
Reimplemented Function
void enterInterruptMode()
Instructs the agent to interrupt evaluation.
The QScriptBreakpointData class contains data associated with a breakpoint.
bool toBoolean() const
Use toBool() instead.
void enterStepOverMode(int count=1)
Instructs the agent to perform a "step over" operation.
QString arg(qlonglong a, int fieldwidth=0, int base=10, const QChar &fillChar=QLatin1Char(' ')) const Q_REQUIRED_RESULT
Definition: qstring.cpp:7186
bool setBreakpointData(int id, const QScriptBreakpointData &data)
Sets the data associated with the breakpoint with the given id.
bool isEnabled() const
Returns true if the breakpoint is enabled, false otherwise.
static QString fromLatin1(const char *, int size=-1)
Returns a QString initialized with the first size characters of the Latin-1 string str...
Definition: qstring.cpp:4188
QScriptBreakpointData breakpointData(int id) const
Returns the data associated with the breakpoint with the given id.
#define ctx
Definition: qgl.cpp:6094
QPair< QList< qint64 >, QList< qint64 > > contextsCheckpoint()
Q_OUTOFLINE_TEMPLATE QPair< T1, T2 > qMakePair(const T1 &x, const T2 &y)
Definition: qpair.h:102
int setBreakpoint(const QScriptBreakpointData &data)
Sets a breakpoint defined by the given data.
int size() const
Returns the number of items in the list.
Definition: qlist.h:137
QScriptContext * currentContext() const
Returns the current context.
if(void) toggleToolbarShown
void enterStepOutMode()
Instructs the agent to perform a "step out" operation.
void clearExceptions()
Clears any uncaught exceptions in this engine.
QPair< QList< qint64 >, QList< qint64 > > scriptsDelta() const
Returns the difference between the current checkpoint and the previous checkpoint.
QScriptDebuggerBackendPrivate * backend
void contextPop()
Reimplemented Function
bool deleteBreakpoint(int id)
Deletes the breakpoint with the given id.
QString condition() const
Returns the condition of the breakpoint.
void scriptsCheckpoint()
Checkpoints the current scripts.
bool hit()
If the ignore count is 0, this function increments the hit count and returns true.
Extension
This enum specifies the possible extensions to a QScriptEngineAgent.
int lineNumber() const
Returns the breakpoint line number.
The QScriptValue class acts as a container for the Qt Script data types.
Definition: qscriptvalue.h:57
#define Q_UNUSED(x)
Indicates to the compiler that the parameter with the specified name is not used in the body of a fun...
Definition: qglobal.h:1729
static QString fileName(const QString &fileUrl)
The QScriptEngineAgent class provides an interface to report events pertaining to QScriptEngine execu...
void enterContinueMode()
Instructs the agent to continue evaluation.
QString fileName() const
void deleteAllBreakpoints()
Deletes all breakpoints.
QScriptContext * parentContext() const
Returns the parent context of this QScriptContext.
QScriptValue throwValue(const QScriptValue &value)
Throws an exception with the given value.
void removeAt(int i)
Removes the item at index position i.
Definition: qlist.h:480