src/scripttools/debugging/qscriptenginedebuggerfrontend.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 "qscriptenginedebuggerfrontend_p.h"
       
    43 #include "qscriptdebuggerfrontend_p_p.h"
       
    44 #include "qscriptdebuggerbackend_p.h"
       
    45 #include "qscriptdebuggerbackend_p_p.h"
       
    46 #include "qscriptdebuggerevent_p.h"
       
    47 #include "qscriptdebuggercommandexecutor_p.h"
       
    48 #include "qscriptdebuggerresponse_p.h"
       
    49 
       
    50 #include <QtCore/qcoreapplication.h>
       
    51 #include <QtCore/qeventloop.h>
       
    52 #include <QtCore/qlist.h>
       
    53 #include <QtScript/qscriptvalue.h>
       
    54 
       
    55 QT_BEGIN_NAMESPACE
       
    56 
       
    57 /*!
       
    58   \class QScriptEngineDebuggerFrontend
       
    59   \since 4.5
       
    60   \internal
       
    61 
       
    62   \brief The QScriptEngineDebuggerFrontend class provides an in-process debugger frontend.
       
    63 
       
    64   This type of frontend is used when the QScriptEngine being debugged
       
    65   lives in the same process as the debugger.
       
    66 
       
    67   Call the attachTo() function to attach to an engine.
       
    68 */
       
    69 
       
    70 class QScriptDebuggerCommandEvent : public QEvent
       
    71 {
       
    72 public:
       
    73     QScriptDebuggerCommandEvent(int id, const QScriptDebuggerCommand &command)
       
    74         : QEvent(QEvent::Type(QEvent::User+3)), m_id(id), m_command(command) {}
       
    75     ~QScriptDebuggerCommandEvent() {}
       
    76     int id() const
       
    77         { return m_id; }
       
    78     const QScriptDebuggerCommand &command() const
       
    79         { return m_command; }
       
    80 private:
       
    81     int m_id;
       
    82     QScriptDebuggerCommand m_command;
       
    83 };
       
    84 
       
    85 class QScriptDebuggerCommandFinishedEvent : public QEvent
       
    86 {
       
    87 public:
       
    88     QScriptDebuggerCommandFinishedEvent(int id, const QScriptDebuggerResponse &response)
       
    89         : QEvent(QEvent::Type(QEvent::User+4)), m_id(id), m_response(response) {}
       
    90     ~QScriptDebuggerCommandFinishedEvent() {}
       
    91     int id() const
       
    92         { return m_id; }
       
    93     const QScriptDebuggerResponse &response() const
       
    94         { return m_response; }
       
    95 private:
       
    96     int m_id;
       
    97     QScriptDebuggerResponse m_response;
       
    98 };
       
    99 
       
   100 class QScriptEngineDebuggerBackendPrivate;
       
   101 class QScriptEngineDebuggerBackend : public QScriptDebuggerBackend
       
   102 {
       
   103 public:
       
   104     QScriptEngineDebuggerBackend(QScriptEngineDebuggerFrontendPrivate *frontend);
       
   105     ~QScriptEngineDebuggerBackend();
       
   106 
       
   107     void processCommand(int id, const QScriptDebuggerCommand &command);
       
   108     void resume();
       
   109 
       
   110 protected:
       
   111     void event(const QScriptDebuggerEvent &event);
       
   112 
       
   113 private:
       
   114     Q_DECLARE_PRIVATE(QScriptEngineDebuggerBackend)
       
   115     Q_DISABLE_COPY(QScriptEngineDebuggerBackend)
       
   116 };
       
   117 
       
   118 class QScriptEngineDebuggerBackendPrivate
       
   119     : public QScriptDebuggerBackendPrivate
       
   120 {
       
   121     Q_DECLARE_PUBLIC(QScriptEngineDebuggerBackend)
       
   122 public:
       
   123     QScriptEngineDebuggerBackendPrivate();
       
   124     ~QScriptEngineDebuggerBackendPrivate();
       
   125 
       
   126     bool event(QEvent *e);
       
   127 
       
   128     QScriptEngineDebuggerFrontendPrivate *frontend;
       
   129     QList<QEventLoop*> eventLoopPool;
       
   130     QList<QEventLoop*> eventLoopStack;
       
   131 };
       
   132 
       
   133 class QScriptEngineDebuggerFrontendPrivate
       
   134     : public QScriptDebuggerFrontendPrivate
       
   135 {
       
   136     Q_DECLARE_PUBLIC(QScriptEngineDebuggerFrontend)
       
   137 public:
       
   138     QScriptEngineDebuggerFrontendPrivate();
       
   139     ~QScriptEngineDebuggerFrontendPrivate();
       
   140 
       
   141     void postCommandFinished(int id, const QScriptDebuggerResponse &response);
       
   142     bool event(QEvent *e);
       
   143 
       
   144     QScriptEngineDebuggerBackend *backend;
       
   145 };
       
   146 
       
   147 QScriptEngineDebuggerBackendPrivate::QScriptEngineDebuggerBackendPrivate()
       
   148 {
       
   149     frontend = 0;
       
   150 }
       
   151 
       
   152 QScriptEngineDebuggerBackendPrivate::~QScriptEngineDebuggerBackendPrivate()
       
   153 {
       
   154     eventLoopPool << eventLoopStack;
       
   155     eventLoopStack.clear();
       
   156     while (!eventLoopPool.isEmpty()) {
       
   157         QEventLoop *eventLoop = eventLoopPool.takeFirst();
       
   158         if (eventLoop->isRunning()) {
       
   159             eventLoop->quit();
       
   160             eventLoop->deleteLater();
       
   161         } else {
       
   162             delete eventLoop;
       
   163         }
       
   164     }
       
   165 }
       
   166 
       
   167 /*!
       
   168   \reimp
       
   169 */
       
   170 bool QScriptEngineDebuggerBackendPrivate::event(QEvent *e)
       
   171 {
       
   172     Q_Q(QScriptEngineDebuggerBackend);
       
   173     if (e->type() == QEvent::User+3) {
       
   174         QScriptDebuggerCommandEvent *ce = static_cast<QScriptDebuggerCommandEvent*>(e);
       
   175         QScriptDebuggerCommandExecutor *executor = q->commandExecutor();
       
   176         QScriptDebuggerResponse response = executor->execute(q, ce->command());
       
   177         frontend->postCommandFinished(ce->id(), response);
       
   178         return true;
       
   179     }
       
   180     return QScriptDebuggerBackendPrivate::event(e);
       
   181 }
       
   182 
       
   183 /*!
       
   184   \internal
       
   185 
       
   186   Creates a new QScriptEngineDebuggerBackend object for the given \a
       
   187   engine.  The back-end will forward events to the given \a frontend.
       
   188 */
       
   189 QScriptEngineDebuggerBackend::QScriptEngineDebuggerBackend(
       
   190     QScriptEngineDebuggerFrontendPrivate *frontend)
       
   191     : QScriptDebuggerBackend(*new QScriptEngineDebuggerBackendPrivate)
       
   192 {
       
   193     Q_D(QScriptEngineDebuggerBackend);
       
   194     d->frontend = frontend;
       
   195 }
       
   196 
       
   197 QScriptEngineDebuggerBackend::~QScriptEngineDebuggerBackend()
       
   198 {
       
   199 }
       
   200 
       
   201 void QScriptEngineDebuggerBackend::processCommand(int id, const QScriptDebuggerCommand &command)
       
   202 {
       
   203     Q_D(QScriptEngineDebuggerBackend);
       
   204     d->postEvent(new QScriptDebuggerCommandEvent(id, command));
       
   205 }
       
   206 
       
   207 /*!
       
   208   \reimp
       
   209 */
       
   210 void QScriptEngineDebuggerBackend::event(const QScriptDebuggerEvent &event)
       
   211 {
       
   212     Q_D(QScriptEngineDebuggerBackend);
       
   213     if (d->eventLoopPool.isEmpty())
       
   214         d->eventLoopPool.append(new QEventLoop());
       
   215     QEventLoop *eventLoop = d->eventLoopPool.takeFirst();
       
   216     Q_ASSERT(!eventLoop->isRunning());
       
   217     d->eventLoopStack.prepend(eventLoop);
       
   218 
       
   219     d->frontend->postEvent(new QScriptDebuggerEventEvent(event));
       
   220 
       
   221     // Run an event loop until resume() is called.
       
   222     // This effectively stalls script execution and makes it possible
       
   223     // for the debugger to inspect the execution state in the meantime.
       
   224     eventLoop->exec();
       
   225 
       
   226     if (!d->eventLoopStack.isEmpty()) {
       
   227         // the event loop was quit directly (i.e. not via resume())
       
   228         d->eventLoopStack.takeFirst();
       
   229     }
       
   230     d->eventLoopPool.append(eventLoop);
       
   231     doPendingEvaluate(/*postEvent=*/false);
       
   232 }
       
   233 
       
   234 /*!
       
   235   \reimp
       
   236 */
       
   237 void QScriptEngineDebuggerBackend::resume()
       
   238 {
       
   239     Q_D(QScriptEngineDebuggerBackend);
       
   240     // quitting the event loops will cause event() to return (see above)
       
   241     while (!d->eventLoopStack.isEmpty()) {
       
   242         QEventLoop *eventLoop = d->eventLoopStack.takeFirst();
       
   243         if (eventLoop->isRunning())
       
   244             eventLoop->quit();
       
   245     }
       
   246 }
       
   247 
       
   248 QScriptEngineDebuggerFrontendPrivate::QScriptEngineDebuggerFrontendPrivate()
       
   249 {
       
   250     backend = 0;
       
   251 }
       
   252 
       
   253 QScriptEngineDebuggerFrontendPrivate::~QScriptEngineDebuggerFrontendPrivate()
       
   254 {
       
   255     delete backend;
       
   256 }
       
   257 
       
   258 void QScriptEngineDebuggerFrontendPrivate::postCommandFinished(
       
   259     int id, const QScriptDebuggerResponse &response)
       
   260 {
       
   261     postEvent(new QScriptDebuggerCommandFinishedEvent(id, response));
       
   262 }
       
   263 
       
   264 /*!
       
   265   \reimp
       
   266 */
       
   267 bool QScriptEngineDebuggerFrontendPrivate::event(QEvent *e)
       
   268 {
       
   269     Q_Q(QScriptEngineDebuggerFrontend);
       
   270     if (e->type() == QEvent::User+4) {
       
   271         QScriptDebuggerCommandFinishedEvent *fe = static_cast<QScriptDebuggerCommandFinishedEvent*>(e);
       
   272         q->notifyCommandFinished(fe->id(), fe->response());
       
   273         return true;
       
   274     }
       
   275     return QScriptDebuggerFrontendPrivate::event(e);
       
   276 }
       
   277 
       
   278 QScriptEngineDebuggerFrontend::QScriptEngineDebuggerFrontend()
       
   279     : QScriptDebuggerFrontend(*new QScriptEngineDebuggerFrontendPrivate)
       
   280 {
       
   281 }
       
   282 
       
   283 QScriptEngineDebuggerFrontend::~QScriptEngineDebuggerFrontend()
       
   284 {
       
   285     detach();
       
   286 }
       
   287 
       
   288 /*!
       
   289   Attaches this front-end to the given \a engine.
       
   290 */
       
   291 void QScriptEngineDebuggerFrontend::attachTo(QScriptEngine *engine)
       
   292 {
       
   293     Q_D(QScriptEngineDebuggerFrontend);
       
   294     if (d->backend)
       
   295         d->backend->detach();
       
   296     else
       
   297         d->backend = new QScriptEngineDebuggerBackend(d);
       
   298     d->backend->attachTo(engine);
       
   299 }
       
   300 
       
   301 /*!
       
   302   Detaches this front-end from the current script engine.
       
   303 */
       
   304 void QScriptEngineDebuggerFrontend::detach()
       
   305 {
       
   306     Q_D(QScriptEngineDebuggerFrontend);
       
   307     if (d->backend)
       
   308         d->backend->detach();
       
   309 }
       
   310 
       
   311 QScriptValue QScriptEngineDebuggerFrontend::traceFunction() const
       
   312 {
       
   313     Q_D(const QScriptEngineDebuggerFrontend);
       
   314     if (d->backend)
       
   315         d->backend->traceFunction();
       
   316     return QScriptValue();
       
   317 }
       
   318 
       
   319 QScriptDebuggerBackend *QScriptEngineDebuggerFrontend::backend() const
       
   320 {
       
   321     Q_D(const QScriptEngineDebuggerFrontend);
       
   322     return d->backend;
       
   323 }
       
   324 
       
   325 /*!
       
   326   \internal
       
   327 */
       
   328 void QScriptEngineDebuggerFrontend::processCommand(int id, const QScriptDebuggerCommand &command)
       
   329 {
       
   330     Q_D(QScriptEngineDebuggerFrontend);
       
   331     Q_ASSERT(d->backend != 0);
       
   332     d->backend->processCommand(id, command);
       
   333 }
       
   334 
       
   335 QT_END_NAMESPACE