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

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