|
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 "qscriptdebuggerbackend_p.h" |
|
43 #include "qscriptdebuggerbackend_p_p.h" |
|
44 #include "qscriptdebuggeragent_p.h" |
|
45 #include "qscriptdebuggercommandexecutor_p.h" |
|
46 #include "qscriptdebuggerevent_p.h" |
|
47 #include "qscriptdebuggervalue_p.h" |
|
48 #include "qscriptscriptdata_p.h" |
|
49 #include "qscriptbreakpointdata_p.h" |
|
50 #include "qscriptobjectsnapshot_p.h" |
|
51 |
|
52 #include <QtScript/qscriptengine.h> |
|
53 #include <QtScript/qscriptcontextinfo.h> |
|
54 #include <QtScript/qscriptvalueiterator.h> |
|
55 |
|
56 #include <QtCore/qcoreapplication.h> |
|
57 #include <QtCore/qdebug.h> |
|
58 |
|
59 Q_DECLARE_METATYPE(QScriptDebuggerValue) |
|
60 Q_DECLARE_METATYPE(QScriptDebuggerBackendPrivate*) |
|
61 |
|
62 QT_BEGIN_NAMESPACE |
|
63 |
|
64 /*! |
|
65 \since 4.5 |
|
66 \class QScriptDebuggerBackend |
|
67 \internal |
|
68 |
|
69 \brief The QScriptDebuggerBackend class is the base class of debugger back-ends. |
|
70 |
|
71 QScriptDebuggerBackend builds on the QScriptDebuggerAgent class. |
|
72 |
|
73 This class is usually used together with the QScriptDebuggerFrontend |
|
74 class, in order to form a (front-end, back-end) pair. |
|
75 |
|
76 Call attachTo() to attach to a QScriptEngine object. Call detach() |
|
77 to detach from the current engine. |
|
78 |
|
79 Call stepInto() to step into the next script statement; call stepOver() |
|
80 to step over the next script statement; and call stepOut() to step out |
|
81 of the currently executing script function. An event() will be generated |
|
82 when the stepping is completed. |
|
83 |
|
84 Call runToLocation() to execute script statements until a certain |
|
85 location has been reached. An event() will be generated when the location |
|
86 has been reached. |
|
87 |
|
88 Call interruptEvaluation() to request that evaluation should be |
|
89 interrupted. An event() will be generated upon the next script |
|
90 statement that is reached. |
|
91 |
|
92 Call continueEvalution() to allow script evaluation to continue. |
|
93 |
|
94 Call setBreakpoint() to set a breakpoint. A breakpoint event() will |
|
95 be generated when a breakpoint is hit. Call deleteBreakpoint() to |
|
96 delete a breakpoint. Call modifyBreakpoint() to change the state of |
|
97 an existing breakpoint. |
|
98 |
|
99 Call contextCount() to obtain the number of active contexts |
|
100 (frames). Call context() to obtain a pointer to a QScriptContext. |
|
101 |
|
102 \section1 Subclassing |
|
103 |
|
104 When subclassing QScriptDebuggerBackend, you must implement the pure |
|
105 virtual event() function. This function typically forwards the event |
|
106 to a QScriptDebuggerFrontend object. For most type of events, |
|
107 event() should block until the back-end is instructed to resume |
|
108 execution (e.g. until continueEvalution() is called). You must |
|
109 implement resume(), which is responsible for making event() return. |
|
110 |
|
111 \sa QScriptDebuggerFrontend, QScriptDebuggerEvent |
|
112 */ |
|
113 |
|
114 // helper class that's used to handle our custom Qt events |
|
115 class QScriptDebuggerBackendEventReceiver : public QObject |
|
116 { |
|
117 public: |
|
118 QScriptDebuggerBackendEventReceiver(QScriptDebuggerBackendPrivate *backend, |
|
119 QObject *parent = 0) |
|
120 : QObject(parent), m_backend(backend) {} |
|
121 ~QScriptDebuggerBackendEventReceiver() {} |
|
122 |
|
123 bool event(QEvent *e) |
|
124 { |
|
125 return m_backend->event(e); |
|
126 } |
|
127 |
|
128 private: |
|
129 QScriptDebuggerBackendPrivate *m_backend; |
|
130 }; |
|
131 |
|
132 |
|
133 QScriptDebuggerBackendPrivate::QScriptDebuggerBackendPrivate() |
|
134 : agent(0), commandExecutor(0), |
|
135 pendingEvaluateContextIndex(-1), pendingEvaluateLineNumber(-1), |
|
136 ignoreExceptions(false), |
|
137 nextScriptValueIteratorId(0), nextScriptObjectSnapshotId(0), |
|
138 eventReceiver(0), |
|
139 q_ptr(0) // q_ptr will be set later by QScriptDebuggerBackend constructor |
|
140 { |
|
141 } |
|
142 |
|
143 QScriptDebuggerBackendPrivate::~QScriptDebuggerBackendPrivate() |
|
144 { |
|
145 if (agent) |
|
146 agent->nullifyBackendPointer(); |
|
147 delete commandExecutor; |
|
148 delete eventReceiver; |
|
149 qDeleteAll(scriptValueIterators); |
|
150 qDeleteAll(scriptObjectSnapshots); |
|
151 } |
|
152 |
|
153 void QScriptDebuggerBackendPrivate::postEvent(QEvent *e) |
|
154 { |
|
155 if (!eventReceiver) { |
|
156 eventReceiver = new QScriptDebuggerBackendEventReceiver(this); |
|
157 eventReceiver->moveToThread(agent->engine()->thread()); |
|
158 } |
|
159 QCoreApplication::postEvent(eventReceiver, e); |
|
160 } |
|
161 |
|
162 bool QScriptDebuggerBackendPrivate::event(QEvent *e) |
|
163 { |
|
164 if (e->type() == QEvent::User+1) { |
|
165 QScriptDebuggerEventEvent *de = static_cast<QScriptDebuggerEventEvent*>(e); |
|
166 q_func()->event(de->event()); |
|
167 return true; |
|
168 } |
|
169 return false; |
|
170 } |
|
171 |
|
172 void QScriptDebuggerBackendPrivate::agentDestroyed(QScriptDebuggerAgent *ag) |
|
173 { |
|
174 // Since agents are owned by the script engine, this in practice means |
|
175 // that the engine has been destroyed. Invalidate our pointer so we |
|
176 // don't crash later. |
|
177 if (agent == ag) |
|
178 agent = 0; |
|
179 } |
|
180 |
|
181 /*! |
|
182 The agent calls this function when it has completed a step |
|
183 operation. |
|
184 */ |
|
185 void QScriptDebuggerBackendPrivate::stepped(qint64 scriptId, |
|
186 int lineNumber, |
|
187 int columnNumber, |
|
188 const QScriptValue &result) |
|
189 { |
|
190 Q_Q(QScriptDebuggerBackend); |
|
191 QScriptDebuggerEvent e(QScriptDebuggerEvent::SteppingFinished, |
|
192 scriptId, lineNumber, columnNumber); |
|
193 e.setFileName(agent->scriptData(scriptId).fileName()); |
|
194 QScriptDebuggerValue value(result); |
|
195 e.setScriptValue(value); |
|
196 if (!result.isUndefined()) |
|
197 e.setMessage(result.toString()); // for convenience -- we always need it |
|
198 q->event(e); |
|
199 } |
|
200 |
|
201 /*! |
|
202 The agent calls this function when it has run to a particular |
|
203 location. |
|
204 */ |
|
205 void QScriptDebuggerBackendPrivate::locationReached(qint64 scriptId, |
|
206 int lineNumber, |
|
207 int columnNumber) |
|
208 { |
|
209 Q_Q(QScriptDebuggerBackend); |
|
210 QScriptDebuggerEvent e(QScriptDebuggerEvent::LocationReached, |
|
211 scriptId, lineNumber, columnNumber); |
|
212 e.setFileName(agent->scriptData(scriptId).fileName()); |
|
213 q->event(e); |
|
214 } |
|
215 |
|
216 /*! |
|
217 The agent calls this function when evaluation has been interrupted. |
|
218 */ |
|
219 void QScriptDebuggerBackendPrivate::interrupted(qint64 scriptId, |
|
220 int lineNumber, |
|
221 int columnNumber) |
|
222 { |
|
223 Q_Q(QScriptDebuggerBackend); |
|
224 QScriptDebuggerEvent e(QScriptDebuggerEvent::Interrupted, |
|
225 scriptId, lineNumber, columnNumber); |
|
226 e.setFileName(agent->scriptData(scriptId).fileName()); |
|
227 q->event(e); |
|
228 } |
|
229 |
|
230 /*! |
|
231 The agent calls this function when a breakpoint has been triggered. |
|
232 */ |
|
233 void QScriptDebuggerBackendPrivate::breakpoint(qint64 scriptId, |
|
234 int lineNumber, |
|
235 int columnNumber, |
|
236 int breakpointId) |
|
237 { |
|
238 Q_Q(QScriptDebuggerBackend); |
|
239 QScriptDebuggerEvent e(QScriptDebuggerEvent::Breakpoint, |
|
240 scriptId, lineNumber, columnNumber); |
|
241 e.setFileName(agent->scriptData(scriptId).fileName()); |
|
242 e.setBreakpointId(breakpointId); |
|
243 q->event(e); |
|
244 } |
|
245 |
|
246 /*! |
|
247 The agent calls this function when an uncaught exception has |
|
248 occurred. |
|
249 */ |
|
250 void QScriptDebuggerBackendPrivate::exception(qint64 scriptId, |
|
251 const QScriptValue &exception, |
|
252 bool hasHandler) |
|
253 { |
|
254 Q_Q(QScriptDebuggerBackend); |
|
255 if (ignoreExceptions) { |
|
256 // don't care (it's caught by us) |
|
257 return; |
|
258 } |
|
259 QScriptDebuggerEvent e(QScriptDebuggerEvent::Exception); |
|
260 e.setScriptId(scriptId); |
|
261 e.setFileName(agent->scriptData(scriptId).fileName()); |
|
262 e.setMessage(exception.toString()); |
|
263 e.setHasExceptionHandler(hasHandler); |
|
264 int lineNumber = -1; |
|
265 QString fileName; |
|
266 if (exception.property(QLatin1String("lineNumber")).isNumber()) |
|
267 lineNumber = exception.property(QLatin1String("lineNumber")).toInt32(); |
|
268 if (exception.property(QLatin1String("fileName")).isString()) |
|
269 fileName = exception.property(QLatin1String("fileName")).toString(); |
|
270 if (lineNumber == -1) { |
|
271 QScriptContextInfo info(q->engine()->currentContext()); |
|
272 lineNumber = info.lineNumber(); |
|
273 fileName = info.fileName(); |
|
274 } |
|
275 if (lineNumber != -1) |
|
276 e.setLineNumber(lineNumber); |
|
277 if (!fileName.isEmpty()) |
|
278 e.setFileName(fileName); |
|
279 QScriptDebuggerValue value(exception); |
|
280 e.setScriptValue(value); |
|
281 q->event(e); |
|
282 } |
|
283 |
|
284 QScriptValue QScriptDebuggerBackendPrivate::trace(QScriptContext *context, |
|
285 QScriptEngine *engine) |
|
286 { |
|
287 QScriptValue data = context->callee().data(); |
|
288 QScriptDebuggerBackendPrivate *self = qscriptvalue_cast<QScriptDebuggerBackendPrivate*>(data); |
|
289 if (!self) |
|
290 return engine->undefinedValue(); |
|
291 QString str; |
|
292 for (int i = 0; i < context->argumentCount(); ++i) { |
|
293 if (i > 0) |
|
294 str.append(QLatin1Char(' ')); |
|
295 str.append(context->argument(i).toString()); |
|
296 } |
|
297 QScriptDebuggerEvent e(QScriptDebuggerEvent::Trace); |
|
298 e.setMessage(str); |
|
299 self->q_func()->event(e); |
|
300 return engine->undefinedValue(); |
|
301 } |
|
302 |
|
303 QScriptValue QScriptDebuggerBackendPrivate::qsassert(QScriptContext *context, |
|
304 QScriptEngine *engine) |
|
305 { |
|
306 QScriptValue arg = context->argument(0); |
|
307 if (arg.toBoolean()) |
|
308 return arg; |
|
309 QScriptContextInfo info(context->parentContext()); |
|
310 QString msg; |
|
311 QString fileName = info.fileName(); |
|
312 if (fileName.isEmpty()) |
|
313 fileName = QString::fromLatin1("<anonymous script, id=%0>").arg(info.scriptId()); |
|
314 msg.append(fileName); |
|
315 msg.append(QLatin1Char(':')); |
|
316 msg.append(QString::number(info.lineNumber())); |
|
317 msg.append(QString::fromLatin1(": Assertion failed")); |
|
318 for (int i = 1; i < context->argumentCount(); ++i) { |
|
319 if (i == 1) |
|
320 msg.append(QLatin1Char(':')); |
|
321 msg.append(QLatin1Char(' ')); |
|
322 msg.append(context->argument(i).toString()); |
|
323 } |
|
324 QScriptValue err = context->throwError(msg); |
|
325 err.setProperty(QString::fromLatin1("name"), QScriptValue(engine, QString::fromLatin1("AssertionError"))); |
|
326 return err; |
|
327 } |
|
328 |
|
329 QScriptValue QScriptDebuggerBackendPrivate::fileName(QScriptContext *context, |
|
330 QScriptEngine *engine) |
|
331 { |
|
332 QScriptContextInfo info(context->parentContext()); |
|
333 QString fn = info.fileName(); |
|
334 if (fn.isEmpty()) |
|
335 return engine->undefinedValue(); |
|
336 return QScriptValue(engine, fn); |
|
337 } |
|
338 |
|
339 QScriptValue QScriptDebuggerBackendPrivate::lineNumber(QScriptContext *context, |
|
340 QScriptEngine *engine) |
|
341 { |
|
342 QScriptContextInfo info(context->parentContext()); |
|
343 return QScriptValue(engine, info.lineNumber()); |
|
344 } |
|
345 |
|
346 /*! |
|
347 The agent calls this function when the engine has reached a |
|
348 "debugger" statement. |
|
349 */ |
|
350 void QScriptDebuggerBackendPrivate::debuggerInvocationRequest( |
|
351 qint64 scriptId, int lineNumber, int columnNumber) |
|
352 { |
|
353 Q_Q(QScriptDebuggerBackend); |
|
354 QScriptDebuggerEvent e(QScriptDebuggerEvent::DebuggerInvocationRequest, |
|
355 scriptId, lineNumber, columnNumber); |
|
356 e.setFileName(agent->scriptData(scriptId).fileName()); |
|
357 q->event(e); |
|
358 } |
|
359 |
|
360 void QScriptDebuggerBackendPrivate::forcedReturn( |
|
361 qint64 scriptId, int lineNumber, int columnNumber, |
|
362 const QScriptValue &value) |
|
363 { |
|
364 Q_Q(QScriptDebuggerBackend); |
|
365 QScriptDebuggerEvent e(QScriptDebuggerEvent::ForcedReturn, |
|
366 scriptId, lineNumber, columnNumber); |
|
367 e.setFileName(agent->scriptData(scriptId).fileName()); |
|
368 e.setScriptValue(QScriptDebuggerValue(value)); |
|
369 q->event(e); |
|
370 } |
|
371 |
|
372 /*! |
|
373 Creates a QScriptDebuggerBackend object. |
|
374 */ |
|
375 QScriptDebuggerBackend::QScriptDebuggerBackend() |
|
376 : d_ptr(new QScriptDebuggerBackendPrivate) |
|
377 { |
|
378 d_ptr->q_ptr = this; |
|
379 } |
|
380 |
|
381 /*! |
|
382 Destroys this QScriptDebuggerBackend. |
|
383 */ |
|
384 QScriptDebuggerBackend::~QScriptDebuggerBackend() |
|
385 { |
|
386 detach(); |
|
387 } |
|
388 |
|
389 /*! |
|
390 \internal |
|
391 */ |
|
392 QScriptDebuggerBackend::QScriptDebuggerBackend(QScriptDebuggerBackendPrivate &dd) |
|
393 : d_ptr(&dd) |
|
394 { |
|
395 d_ptr->q_ptr = this; |
|
396 } |
|
397 |
|
398 /*! |
|
399 Attaches this backend to the given \a engine. |
|
400 The backend automatically detaches from the old engine, if any. |
|
401 |
|
402 This function installs its own agent on the \a engine using |
|
403 QScriptEngine::setAgent(); any existing agent will be replaced. |
|
404 |
|
405 \sa detach(). engine() |
|
406 */ |
|
407 void QScriptDebuggerBackend::attachTo(QScriptEngine *engine) |
|
408 { |
|
409 Q_D(QScriptDebuggerBackend); |
|
410 detach(); |
|
411 d->agent = new QScriptDebuggerAgent(d, engine); |
|
412 QScriptValue global = engine->globalObject(); |
|
413 d->origTraceFunction = global.property(QString::fromLatin1("print")); |
|
414 global.setProperty(QString::fromLatin1("print"), traceFunction()); |
|
415 // global.setProperty(QString::fromLatin1("qAssert"), assertFunction()); |
|
416 d->origFileNameFunction = global.property(QString::fromLatin1("__FILE__")); |
|
417 global.setProperty(QString::fromLatin1("__FILE__"), fileNameFunction(), |
|
418 QScriptValue::PropertyGetter | QScriptValue::ReadOnly); |
|
419 d->origLineNumberFunction = global.property(QString::fromLatin1("__LINE__")); |
|
420 global.setProperty(QString::fromLatin1("__LINE__"), lineNumberFunction(), |
|
421 QScriptValue::PropertyGetter | QScriptValue::ReadOnly); |
|
422 engine->setAgent(d->agent); |
|
423 } |
|
424 |
|
425 /*! |
|
426 Detaches this backend from the current script engine. |
|
427 The backend's state (including breakpoints and information on loaded |
|
428 scripts) will be invalidated. |
|
429 |
|
430 \sa attach() |
|
431 */ |
|
432 void QScriptDebuggerBackend::detach() |
|
433 { |
|
434 Q_D(QScriptDebuggerBackend); |
|
435 if (d->agent) { |
|
436 QScriptEngine *eng = d->agent->engine(); |
|
437 if (eng && eng->agent() == d->agent) { |
|
438 eng->setAgent(0); |
|
439 QScriptValue global = eng->globalObject(); |
|
440 global.setProperty(QString::fromLatin1("print"), d->origTraceFunction); |
|
441 d->origTraceFunction = QScriptValue(); |
|
442 // global.setProperty(QString::fromLatin1("qAssert"), QScriptValue()); |
|
443 global.setProperty(QString::fromLatin1("__FILE__"), QScriptValue(), |
|
444 QScriptValue::PropertyGetter); |
|
445 global.setProperty(QString::fromLatin1("__FILE__"), d->origFileNameFunction); |
|
446 d->origFileNameFunction = QScriptValue(); |
|
447 global.setProperty(QString::fromLatin1("__LINE__"), QScriptValue(), |
|
448 QScriptValue::PropertyGetter); |
|
449 global.setProperty(QString::fromLatin1("__LINE__"), d->origLineNumberFunction); |
|
450 d->origLineNumberFunction = QScriptValue(); |
|
451 d->agent->nullifyBackendPointer(); |
|
452 d->agent = 0; // agent is owned by engine |
|
453 } |
|
454 } |
|
455 |
|
456 d->pendingEvaluateLineNumber = -1; |
|
457 d->ignoreExceptions = false; |
|
458 d->nextScriptValueIteratorId = 0; |
|
459 qDeleteAll(d->scriptValueIterators); |
|
460 d->scriptValueIterators.clear(); |
|
461 qDeleteAll(d->scriptObjectSnapshots); |
|
462 d->scriptObjectSnapshots.clear(); |
|
463 } |
|
464 |
|
465 /*! |
|
466 Returns the script engine that this backend is attached to, or 0 if |
|
467 the backend is not attached to an engine. |
|
468 |
|
469 \sa attachTo() |
|
470 */ |
|
471 QScriptEngine *QScriptDebuggerBackend::engine() const |
|
472 { |
|
473 Q_D(const QScriptDebuggerBackend); |
|
474 if (!d->agent) |
|
475 return 0; |
|
476 return d->agent->engine(); |
|
477 } |
|
478 |
|
479 /*! |
|
480 Steps into the next script statement. |
|
481 When stepping is complete, an event() will be generated. |
|
482 */ |
|
483 void QScriptDebuggerBackend::stepInto(int count) |
|
484 { |
|
485 Q_D(QScriptDebuggerBackend); |
|
486 if (d->agent) { |
|
487 d->agent->enterStepIntoMode(count); |
|
488 resume(); |
|
489 } |
|
490 } |
|
491 |
|
492 /*! |
|
493 Steps over the next script statement. |
|
494 When stepping is complete, an event() will be generated. |
|
495 */ |
|
496 void QScriptDebuggerBackend::stepOver(int count) |
|
497 { |
|
498 Q_D(QScriptDebuggerBackend); |
|
499 if (d->agent) { |
|
500 d->agent->enterStepOverMode(count); |
|
501 resume(); |
|
502 } |
|
503 } |
|
504 |
|
505 /*! |
|
506 Steps out of the current script function. |
|
507 When stepping is complete, an event() will be generated. |
|
508 */ |
|
509 void QScriptDebuggerBackend::stepOut() |
|
510 { |
|
511 Q_D(QScriptDebuggerBackend); |
|
512 if (d->agent) { |
|
513 d->agent->enterStepOutMode(); |
|
514 resume(); |
|
515 } |
|
516 } |
|
517 |
|
518 /*! |
|
519 Continues script evaluation. Evaluation will proceed without |
|
520 interruption until either 1) an uncaught exception occurs, 2) a |
|
521 breakpoint is triggered, or 3) interruptEvaluation() is called. |
|
522 In each case, a proper event() will be generated. |
|
523 */ |
|
524 void QScriptDebuggerBackend::continueEvalution() |
|
525 { |
|
526 Q_D(QScriptDebuggerBackend); |
|
527 if (d->agent) { |
|
528 d->agent->enterContinueMode(); |
|
529 resume(); |
|
530 } |
|
531 } |
|
532 |
|
533 /*! |
|
534 Interrupts script evaluation. When the next script statement is |
|
535 reached, an event() will be generated. |
|
536 */ |
|
537 void QScriptDebuggerBackend::interruptEvaluation() |
|
538 { |
|
539 Q_D(QScriptDebuggerBackend); |
|
540 if (d->agent) |
|
541 d->agent->enterInterruptMode(); |
|
542 } |
|
543 |
|
544 /*! |
|
545 Continues evaluation until the location defined by the given \a |
|
546 fileName and \a lineNumber is reached. When the location is reached, |
|
547 an event() will be generated. |
|
548 */ |
|
549 void QScriptDebuggerBackend::runToLocation(const QString &fileName, int lineNumber) |
|
550 { |
|
551 Q_D(QScriptDebuggerBackend); |
|
552 if (d->agent) { |
|
553 d->agent->enterRunToLocationMode(fileName, lineNumber); |
|
554 resume(); |
|
555 } |
|
556 } |
|
557 |
|
558 /*! |
|
559 Continues evaluation until the location defined by the given \a |
|
560 scriptId and \a lineNumber is reached. When the location is reached, |
|
561 an event() will be generated. |
|
562 */ |
|
563 void QScriptDebuggerBackend::runToLocation(qint64 scriptId, int lineNumber) |
|
564 { |
|
565 Q_D(QScriptDebuggerBackend); |
|
566 if (d->agent) { |
|
567 d->agent->enterRunToLocationMode(scriptId, lineNumber); |
|
568 resume(); |
|
569 } |
|
570 } |
|
571 |
|
572 void QScriptDebuggerBackend::returnToCaller(int contextIndex, const QScriptValue &value) |
|
573 { |
|
574 Q_D(QScriptDebuggerBackend); |
|
575 if (d->agent) { |
|
576 d->agent->enterReturnByForceMode(contextIndex, value); |
|
577 resume(); |
|
578 } |
|
579 } |
|
580 |
|
581 /*! |
|
582 Evaluates the given \a program. When evaluation is complete, an |
|
583 event() is generated. |
|
584 */ |
|
585 void QScriptDebuggerBackend::evaluate(int contextIndex, const QString &program, |
|
586 const QString &fileName, int lineNumber) |
|
587 { |
|
588 Q_D(QScriptDebuggerBackend); |
|
589 d->pendingEvaluateContextIndex = contextIndex; |
|
590 d->pendingEvaluateProgram = program; |
|
591 d->pendingEvaluateFileName = fileName; |
|
592 d->pendingEvaluateLineNumber = lineNumber; |
|
593 if (!engine()->isEvaluating()) |
|
594 doPendingEvaluate(/*postEvent=*/true); |
|
595 else |
|
596 resume(); |
|
597 } |
|
598 |
|
599 /*! |
|
600 Executes the pending evaluate, if any. |
|
601 */ |
|
602 void QScriptDebuggerBackend::doPendingEvaluate(bool postEvent) |
|
603 { |
|
604 Q_D(QScriptDebuggerBackend); |
|
605 QString program = d->pendingEvaluateProgram; |
|
606 if (program.isEmpty()) |
|
607 return; |
|
608 int contextIndex = d->pendingEvaluateContextIndex; |
|
609 QScriptContext *ctx = context(contextIndex); |
|
610 Q_ASSERT(ctx != 0); |
|
611 QString fileName = d->pendingEvaluateFileName; |
|
612 int lineNumber = d->pendingEvaluateLineNumber; |
|
613 d->pendingEvaluateProgram = QString(); |
|
614 d->pendingEvaluateFileName = QString(); |
|
615 d->pendingEvaluateLineNumber = -1; |
|
616 d->pendingEvaluateContextIndex = -1; |
|
617 |
|
618 // push a new context and initialize its scope chain etc. |
|
619 { |
|
620 QScriptContext *evalContext = engine()->pushContext(); |
|
621 QScriptValueList scopeChain = ctx->scopeChain(); |
|
622 if (scopeChain.isEmpty()) |
|
623 scopeChain.append(engine()->globalObject()); |
|
624 while (!scopeChain.isEmpty()) |
|
625 evalContext->pushScope(scopeChain.takeLast()); |
|
626 evalContext->setActivationObject(ctx->activationObject()); |
|
627 evalContext->setThisObject(ctx->thisObject()); |
|
628 } |
|
629 |
|
630 d->agent->enterContinueMode(); |
|
631 // set a flag so that any exception that happens in |
|
632 // the evaluate() is not sent to the debugger |
|
633 d->ignoreExceptions = true; |
|
634 bool hadException = engine()->hasUncaughtException(); |
|
635 QScriptValue ret = engine()->evaluate(program, fileName, lineNumber); |
|
636 d->ignoreExceptions = false; |
|
637 if (!hadException && engine()->hasUncaughtException()) |
|
638 engine()->clearExceptions(); |
|
639 engine()->popContext(); |
|
640 |
|
641 QScriptDebuggerValue retret(ret); |
|
642 QScriptDebuggerEvent e(QScriptDebuggerEvent::InlineEvalFinished); |
|
643 e.setScriptValue(retret); |
|
644 if (!ret.isUndefined()) |
|
645 e.setMessage(ret.toString()); // for convenience -- we always need it |
|
646 |
|
647 e.setNestedEvaluate(engine()->isEvaluating()); |
|
648 |
|
649 if (postEvent) { |
|
650 QScriptDebuggerEventEvent *de = new QScriptDebuggerEventEvent(e); |
|
651 d->postEvent(de); |
|
652 } else { |
|
653 event(e); |
|
654 } |
|
655 } |
|
656 |
|
657 /*! |
|
658 Sets a breakpoint defined by the given \a data, and returns a unique |
|
659 identifier for the new breakpoint. |
|
660 |
|
661 If the conditions of the breakpoint is satisfied at some point |
|
662 during script evaluation, a breakpoint event() will be generated. |
|
663 |
|
664 \sa deleteBreakpoint(), breakpoints() |
|
665 */ |
|
666 int QScriptDebuggerBackend::setBreakpoint(const QScriptBreakpointData &data) |
|
667 { |
|
668 Q_D(QScriptDebuggerBackend); |
|
669 if (!d->agent) |
|
670 return -1; |
|
671 if (!data.isValid()) |
|
672 return -1; |
|
673 return d->agent->setBreakpoint(data); |
|
674 } |
|
675 |
|
676 /*! |
|
677 Deletes the breakpoint identified by the given \a id. Returns true |
|
678 if the breakpoint was deleted (i.e. the \a id was valid), otherwise |
|
679 returns false. |
|
680 |
|
681 \sa setBreakpoint() |
|
682 */ |
|
683 bool QScriptDebuggerBackend::deleteBreakpoint(int id) |
|
684 { |
|
685 Q_D(QScriptDebuggerBackend); |
|
686 if (!d->agent) |
|
687 return false; |
|
688 return d->agent->deleteBreakpoint(id); |
|
689 } |
|
690 |
|
691 /*! |
|
692 Deletes all breakpoints. |
|
693 */ |
|
694 void QScriptDebuggerBackend::deleteAllBreakpoints() |
|
695 { |
|
696 Q_D(QScriptDebuggerBackend); |
|
697 if (d->agent) |
|
698 d->agent->deleteAllBreakpoints(); |
|
699 } |
|
700 |
|
701 /*! |
|
702 Returns the data associated with the breakpoint identified by the |
|
703 given \a id. |
|
704 */ |
|
705 QScriptBreakpointData QScriptDebuggerBackend::breakpointData(int id) const |
|
706 { |
|
707 Q_D(const QScriptDebuggerBackend); |
|
708 if (!d->agent) |
|
709 return QScriptBreakpointData(); |
|
710 return d->agent->breakpointData(id); |
|
711 } |
|
712 |
|
713 /*! |
|
714 Sets the \a data associated with the breakpoint identified by the |
|
715 given \a id. |
|
716 */ |
|
717 bool QScriptDebuggerBackend::setBreakpointData(int id, const QScriptBreakpointData &data) |
|
718 { |
|
719 Q_D(QScriptDebuggerBackend); |
|
720 if (d->agent) |
|
721 return d->agent->setBreakpointData(id, data); |
|
722 return false; |
|
723 } |
|
724 |
|
725 /*! |
|
726 Returns this backend's breakpoints. |
|
727 |
|
728 \sa setBreakpoint() |
|
729 */ |
|
730 QScriptBreakpointMap QScriptDebuggerBackend::breakpoints() const |
|
731 { |
|
732 Q_D(const QScriptDebuggerBackend); |
|
733 if (!d->agent) |
|
734 return QScriptBreakpointMap(); |
|
735 return d->agent->breakpoints(); |
|
736 } |
|
737 |
|
738 /*! |
|
739 Returns the scripts that this backend knows about. |
|
740 |
|
741 \sa scriptData() |
|
742 */ |
|
743 QScriptScriptMap QScriptDebuggerBackend::scripts() const |
|
744 { |
|
745 Q_D(const QScriptDebuggerBackend); |
|
746 if (!d->agent) |
|
747 return QScriptScriptMap(); |
|
748 return d->agent->scripts(); |
|
749 } |
|
750 |
|
751 /*! |
|
752 Returns the data for the script identified by the given \a id. |
|
753 |
|
754 \sa scripts() |
|
755 */ |
|
756 QScriptScriptData QScriptDebuggerBackend::scriptData(qint64 id) const |
|
757 { |
|
758 Q_D(const QScriptDebuggerBackend); |
|
759 if (!d->agent) |
|
760 return QScriptScriptData(); |
|
761 return d->agent->scriptData(id); |
|
762 } |
|
763 |
|
764 /*! |
|
765 Makes a checkpoint of the currently loaded scripts. |
|
766 |
|
767 \sa scriptsDelta() |
|
768 */ |
|
769 void QScriptDebuggerBackend::scriptsCheckpoint() |
|
770 { |
|
771 Q_D(QScriptDebuggerBackend); |
|
772 if (d->agent) |
|
773 d->agent->scriptsCheckpoint(); |
|
774 } |
|
775 |
|
776 /*! |
|
777 Returns the difference between the latest scripts checkpoint and the |
|
778 previous checkpoint. The first item in the pair is a list |
|
779 containing the identifiers of the scripts that were added. The |
|
780 second item in the pair is a list containing the identifiers of the |
|
781 scripts that were removed. |
|
782 |
|
783 \sa scriptsCheckpoint() |
|
784 */ |
|
785 QScriptScriptsDelta QScriptDebuggerBackend::scriptsDelta() const |
|
786 { |
|
787 Q_D(const QScriptDebuggerBackend); |
|
788 if (!d->agent) |
|
789 return QPair<QList<qint64>, QList<qint64> >(); |
|
790 return d->agent->scriptsDelta(); |
|
791 } |
|
792 |
|
793 qint64 QScriptDebuggerBackend::resolveScript(const QString &fileName) const |
|
794 { |
|
795 Q_D(const QScriptDebuggerBackend); |
|
796 if (!d->agent) |
|
797 return -1; |
|
798 return d->agent->resolveScript(fileName); |
|
799 } |
|
800 |
|
801 /*! |
|
802 Returns the number of contexts (frames). |
|
803 */ |
|
804 int QScriptDebuggerBackend::contextCount() const |
|
805 { |
|
806 if (!engine()) |
|
807 return 0; |
|
808 return contextIds().count(); |
|
809 } |
|
810 |
|
811 /*! |
|
812 Returns the context for the frame with the given \a index. |
|
813 */ |
|
814 QScriptContext *QScriptDebuggerBackend::context(int index) const |
|
815 { |
|
816 if (index < 0) |
|
817 return 0; |
|
818 QScriptContext *ctx = engine()->currentContext(); |
|
819 while (ctx) { |
|
820 if (index == 0) |
|
821 return ctx; |
|
822 ctx = ctx->parentContext(); |
|
823 --index; |
|
824 } |
|
825 return 0; |
|
826 } |
|
827 |
|
828 /*! |
|
829 Returns a backtrace of the current execution. |
|
830 */ |
|
831 QStringList QScriptDebuggerBackend::backtrace() const |
|
832 { |
|
833 if (!engine()) |
|
834 return QStringList(); |
|
835 return engine()->currentContext()->backtrace(); |
|
836 } |
|
837 |
|
838 QList<qint64> QScriptDebuggerBackend::contextIds() const |
|
839 { |
|
840 Q_D(const QScriptDebuggerBackend); |
|
841 if (!d->agent) |
|
842 return QList<qint64>(); |
|
843 return d->agent->contextIds(); |
|
844 } |
|
845 |
|
846 QScriptContextsDelta QScriptDebuggerBackend::contextsCheckpoint() |
|
847 { |
|
848 Q_D(QScriptDebuggerBackend); |
|
849 if (!d->agent) |
|
850 return QScriptContextsDelta(); |
|
851 return d->agent->contextsCheckpoint(); |
|
852 } |
|
853 |
|
854 int QScriptDebuggerBackend::newScriptObjectSnapshot() |
|
855 { |
|
856 Q_D(QScriptDebuggerBackend); |
|
857 int id = d->nextScriptObjectSnapshotId; |
|
858 ++d->nextScriptObjectSnapshotId; |
|
859 d->scriptObjectSnapshots[id] = new QScriptObjectSnapshot(); |
|
860 return id; |
|
861 } |
|
862 |
|
863 QScriptObjectSnapshot *QScriptDebuggerBackend::scriptObjectSnapshot(int id) const |
|
864 { |
|
865 Q_D(const QScriptDebuggerBackend); |
|
866 return d->scriptObjectSnapshots.value(id); |
|
867 } |
|
868 |
|
869 void QScriptDebuggerBackend::deleteScriptObjectSnapshot(int id) |
|
870 { |
|
871 Q_D(QScriptDebuggerBackend); |
|
872 QScriptObjectSnapshot *snap = d->scriptObjectSnapshots.take(id); |
|
873 delete snap; |
|
874 } |
|
875 |
|
876 int QScriptDebuggerBackend::newScriptValueIterator(const QScriptValue &object) |
|
877 { |
|
878 Q_D(QScriptDebuggerBackend); |
|
879 int id = d->nextScriptValueIteratorId; |
|
880 ++d->nextScriptValueIteratorId; |
|
881 d->scriptValueIterators[id] = new QScriptValueIterator(object); |
|
882 return id; |
|
883 } |
|
884 |
|
885 QScriptValueIterator *QScriptDebuggerBackend::scriptValueIterator(int id) const |
|
886 { |
|
887 Q_D(const QScriptDebuggerBackend); |
|
888 return d->scriptValueIterators.value(id); |
|
889 } |
|
890 |
|
891 void QScriptDebuggerBackend::deleteScriptValueIterator(int id) |
|
892 { |
|
893 Q_D(QScriptDebuggerBackend); |
|
894 QScriptValueIterator *it = d->scriptValueIterators.take(id); |
|
895 delete it; |
|
896 } |
|
897 |
|
898 bool QScriptDebuggerBackend::ignoreExceptions() const |
|
899 { |
|
900 Q_D(const QScriptDebuggerBackend); |
|
901 return d->ignoreExceptions; |
|
902 } |
|
903 |
|
904 void QScriptDebuggerBackend::setIgnoreExceptions(bool ignore) |
|
905 { |
|
906 Q_D(QScriptDebuggerBackend); |
|
907 d->ignoreExceptions = ignore; |
|
908 } |
|
909 |
|
910 /*! |
|
911 Returns a trace function. The trace function has similar semantics |
|
912 to the built-in print() function; however, instead of writing text |
|
913 to standard output, it generates a trace event containing the text. |
|
914 */ |
|
915 QScriptValue QScriptDebuggerBackend::traceFunction() const |
|
916 { |
|
917 Q_D(const QScriptDebuggerBackend); |
|
918 if (!engine()) |
|
919 return QScriptValue(); |
|
920 QScriptValue fun = engine()->newFunction(QScriptDebuggerBackendPrivate::trace); |
|
921 fun.setData(qScriptValueFromValue(engine(), const_cast<QScriptDebuggerBackendPrivate*>(d))); |
|
922 return fun; |
|
923 } |
|
924 |
|
925 QScriptValue QScriptDebuggerBackend::assertFunction() const |
|
926 { |
|
927 if (!engine()) |
|
928 return QScriptValue(); |
|
929 QScriptValue fun = engine()->newFunction(QScriptDebuggerBackendPrivate::qsassert); |
|
930 return fun; |
|
931 } |
|
932 |
|
933 QScriptValue QScriptDebuggerBackend::fileNameFunction() const |
|
934 { |
|
935 if (!engine()) |
|
936 return QScriptValue(); |
|
937 QScriptValue fun = engine()->newFunction(QScriptDebuggerBackendPrivate::fileName); |
|
938 return fun; |
|
939 } |
|
940 |
|
941 QScriptValue QScriptDebuggerBackend::lineNumberFunction() const |
|
942 { |
|
943 if (!engine()) |
|
944 return QScriptValue(); |
|
945 QScriptValue fun = engine()->newFunction(QScriptDebuggerBackendPrivate::lineNumber); |
|
946 return fun; |
|
947 } |
|
948 |
|
949 QScriptDebuggerCommandExecutor *QScriptDebuggerBackend::commandExecutor() const |
|
950 { |
|
951 Q_D(const QScriptDebuggerBackend); |
|
952 if (d->commandExecutor) |
|
953 return d->commandExecutor; |
|
954 QScriptDebuggerBackendPrivate *dd = const_cast<QScriptDebuggerBackendPrivate*>(d); |
|
955 dd->commandExecutor = new QScriptDebuggerCommandExecutor(); |
|
956 return dd->commandExecutor; |
|
957 } |
|
958 |
|
959 void QScriptDebuggerBackend::setCommandExecutor(QScriptDebuggerCommandExecutor *executor) |
|
960 { |
|
961 Q_D(QScriptDebuggerBackend); |
|
962 d->commandExecutor = executor; |
|
963 } |
|
964 |
|
965 /*! |
|
966 \fn void QScriptDebuggerBackend::resume() |
|
967 |
|
968 This function is called when control should be returned back to the |
|
969 back-end, i.e. when script evaluation should be resumed after an |
|
970 event has been delivered. |
|
971 |
|
972 Subclasses must reimplement this function to make event() return. |
|
973 |
|
974 \sa event() |
|
975 */ |
|
976 |
|
977 /*! |
|
978 \fn void QScriptDebuggerBackend::event(const QScriptDebuggerEvent &event) |
|
979 |
|
980 This function is called when the back-end has generated the given \a event. |
|
981 |
|
982 Subclasses must reimplement this function to handle the |
|
983 event. Typically the event is forwarded to a |
|
984 QScriptDebuggerFrontend, which will in turn forward it to its |
|
985 QScriptDebuggerClient. The client may then query the front-end for |
|
986 information about the execution state, and call e.g. |
|
987 continueEvalution() to resume execution. This function should block |
|
988 until resume() is called. |
|
989 |
|
990 \sa resume() |
|
991 */ |
|
992 |
|
993 QT_END_NAMESPACE |