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