source: trunk/src/script/api/qscriptengineagent.cpp@ 1036

Last change on this file since 1036 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

  • Property svn:eol-style set to native
File size: 17.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtScript module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL-ONLY$
10** GNU Lesser General Public License Usage
11** This file may be used under the terms of the GNU Lesser
12** General Public License version 2.1 as published by the Free Software
13** Foundation and appearing in the file LICENSE.LGPL included in the
14** packaging of this file. Please review the following information to
15** ensure the GNU Lesser General Public License version 2.1 requirements
16** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17**
18** If you have questions regarding the use of this file, please contact
19** Nokia at [email protected].
20** $QT_END_LICENSE$
21**
22****************************************************************************/
23
24#include "config.h"
25#include "qscriptengineagent.h"
26#include "qscriptengineagent_p.h"
27#include "qscriptengine.h"
28#include "qscriptengine_p.h"
29
30#include "CodeBlock.h"
31#include "Instruction.h"
32
33QT_BEGIN_NAMESPACE
34
35/*!
36 \since 4.4
37 \class QScriptEngineAgent
38
39 \brief The QScriptEngineAgent class provides an interface to report events pertaining to QScriptEngine execution.
40
41 \ingroup script
42
43
44 The QScriptEngineAgent class is the basis of tools that monitor and/or control the execution of a
45 QScriptEngine, such as debuggers and profilers.
46
47 To process script loading and unloading events, reimplement the
48 scriptLoad() and scriptUnload() functions. scriptLoad() is called
49 after the input to QScriptEngine::evaluate() has been parsed, right
50 before the given script is executed. The engine assigns each
51 script an ID, which is available as one of the arguments to
52 scriptLoad(); subsequently, other event handlers can use the ID to
53 identify a particular script. One common usage of scriptLoad() is
54 to retain the script text, filename and base line number (the
55 original input to QScriptEngine::evaluate()), so that other event
56 handlers can e.g. map a line number to the corresponding line of
57 text.
58
59 scriptUnload() is called when the QScriptEngine has no further use
60 for a script; the QScriptEngineAgent may at this point safely
61 discard any resources associated with the script (such as the
62 script text). Note that after scriptUnload() has been called, the
63 QScriptEngine may reuse the relevant script ID for new scripts
64 (i.e. as argument to a subsequent call to scriptLoad()).
65
66 Evaluating the following script will result in scriptUnload()
67 being called immediately after evaluation has completed:
68
69 \snippet doc/src/snippets/code/src_script_qscriptengineagent.cpp 0
70
71 Evaluating the following script will \b{not} result in a call to
72 scriptUnload() when evaluation has completed:
73
74 \snippet doc/src/snippets/code/src_script_qscriptengineagent.cpp 1
75
76 The script isn't unloaded because it defines a function (\c{cube})
77 that remains in the script environment after evaluation has
78 completed. If a subsequent script removed the \c{cube} function
79 (e.g. by setting it to \c{null}), scriptUnload() would be called
80 when the function is garbage collected. In general terms, a script
81 isn't unloaded until the engine has determined that none of its
82 contents is referenced.
83
84 To process script function calls and returns, reimplement the
85 functionEntry() and functionExit() functions. functionEntry() is
86 called when a script function is about to be executed;
87 functionExit() is called when a script function is about to return,
88 either normally or due to an exception.
89
90 To process individual script statements, reimplement
91 positionChange(). positionChange() is called each time the engine is
92 about to execute a new statement of a script, and thus offers the
93 finest level of script monitoring.
94
95 To process exceptions, reimplement exceptionThrow() and
96 exceptionCatch(). exceptionThrow() is called when a script exception
97 is thrown, before it has been handled. exceptionCatch() is called
98 when an exception handler is present, and execution is about to be
99 resumed at the handler code.
100
101 \sa QScriptEngine::setAgent(), QScriptContextInfo
102*/
103
104/*!
105 \enum QScriptEngineAgent::Extension
106
107 This enum specifies the possible extensions to a QScriptEngineAgent.
108
109 \value DebuggerInvocationRequest The agent handles \c{debugger} script statements.
110
111 \sa extension()
112*/
113
114
115void QScriptEngineAgentPrivate::attach()
116{
117 if (engine->originalGlobalObject()->debugger())
118 engine->originalGlobalObject()->setDebugger(0);
119 JSC::Debugger::attach(engine->originalGlobalObject());
120 if (!QScriptEnginePrivate::get(engine)->isEvaluating())
121 JSC::Debugger::recompileAllJSFunctions(engine->globalData);
122}
123
124void QScriptEngineAgentPrivate::detach()
125{
126 JSC::Debugger::detach(engine->originalGlobalObject());
127}
128
129void QScriptEngineAgentPrivate::returnEvent(const JSC::DebuggerCallFrame& frame, intptr_t sourceID, int lineno)
130{
131 Q_UNUSED(frame);
132 Q_UNUSED(lineno);
133 Q_UNUSED(sourceID);
134}
135
136void QScriptEngineAgentPrivate::exceptionThrow(const JSC::DebuggerCallFrame& frame, intptr_t sourceID, bool hasHandler)
137{
138 JSC::CallFrame *oldFrame = engine->currentFrame;
139 int oldAgentLineNumber = engine->agentLineNumber;
140 engine->currentFrame = frame.callFrame();
141 QScriptValue value(engine->scriptValueFromJSCValue(frame.exception()));
142 engine->agentLineNumber = value.property(QLatin1String("lineNumber")).toInt32();
143 q_ptr->exceptionThrow(sourceID, value, hasHandler);
144 engine->agentLineNumber = oldAgentLineNumber;
145 engine->currentFrame = oldFrame;
146 engine->setCurrentException(value);
147};
148
149void QScriptEngineAgentPrivate::exceptionCatch(const JSC::DebuggerCallFrame& frame, intptr_t sourceID)
150{
151 JSC::CallFrame *oldFrame = engine->currentFrame;
152 engine->currentFrame = frame.callFrame();
153 QScriptValue value(engine->scriptValueFromJSCValue(frame.exception()));
154 q_ptr->exceptionCatch(sourceID, value);
155 engine->currentFrame = oldFrame;
156 engine->clearCurrentException();
157}
158
159void QScriptEngineAgentPrivate::atStatement(const JSC::DebuggerCallFrame& frame, intptr_t sourceID, int lineno/*, int column*/)
160{
161 QScript::UStringSourceProviderWithFeedback *source = engine->loadedScripts.value(sourceID);
162 if (!source) {
163 // QTBUG-6108: We don't have the source for this script, so ignore.
164 return;
165 }
166// column = source->columnNumberFromOffset(column);
167 int column = 1;
168 JSC::CallFrame *oldFrame = engine->currentFrame;
169 int oldAgentLineNumber = engine->agentLineNumber;
170 engine->currentFrame = frame.callFrame();
171 engine->agentLineNumber = lineno;
172 q_ptr->positionChange(sourceID, lineno, column);
173 engine->currentFrame = oldFrame;
174 engine->agentLineNumber = oldAgentLineNumber;
175}
176
177void QScriptEngineAgentPrivate::functionExit(const JSC::JSValue& returnValue, intptr_t sourceID)
178{
179 QScriptValue result = engine->scriptValueFromJSCValue(returnValue);
180 q_ptr->functionExit(sourceID, result);
181 q_ptr->contextPop();
182}
183
184void QScriptEngineAgentPrivate::evaluateStop(const JSC::JSValue& returnValue, intptr_t sourceID)
185{
186 QScriptValue result = engine->scriptValueFromJSCValue(returnValue);
187 q_ptr->functionExit(sourceID, result);
188}
189
190void QScriptEngineAgentPrivate::didReachBreakpoint(const JSC::DebuggerCallFrame& frame,
191 intptr_t sourceID, int lineno/*, int column*/)
192{
193 if (q_ptr->supportsExtension(QScriptEngineAgent::DebuggerInvocationRequest)) {
194 QScript::UStringSourceProviderWithFeedback *source = engine->loadedScripts.value(sourceID);
195 if (!source) {
196 // QTBUG-6108: We don't have the source for this script, so ignore.
197 return;
198 }
199// column = source->columnNumberFromOffset(column);
200 int column = 1;
201 JSC::CallFrame *oldFrame = engine->currentFrame;
202 int oldAgentLineNumber = engine->agentLineNumber;
203 engine->currentFrame = frame.callFrame();
204 engine->agentLineNumber = lineno;
205 QList<QVariant> args;
206 args << qint64(sourceID) << lineno << column;
207 q_ptr->extension(QScriptEngineAgent::DebuggerInvocationRequest, args);
208 engine->currentFrame = oldFrame;
209 engine->agentLineNumber = oldAgentLineNumber;
210 }
211};
212
213/*!
214 Constructs a QScriptEngineAgent object for the given \a engine.
215
216 The engine takes ownership of the agent.
217
218 Call QScriptEngine::setAgent() to make this agent the active
219 agent.
220*/
221QScriptEngineAgent::QScriptEngineAgent(QScriptEngine *engine)
222 : d_ptr(new QScriptEngineAgentPrivate())
223{
224 d_ptr->q_ptr = this;
225 d_ptr->engine = QScriptEnginePrivate::get(engine);
226 d_ptr->engine->ownedAgents.append(this);
227}
228
229/*!
230 \internal
231*/
232QScriptEngineAgent::QScriptEngineAgent(QScriptEngineAgentPrivate &dd, QScriptEngine *engine)
233 : d_ptr(&dd)
234{
235 d_ptr->q_ptr = this;
236 d_ptr->engine = QScriptEnginePrivate::get(engine);
237}
238
239/*!
240 Destroys this QScriptEngineAgent.
241*/
242QScriptEngineAgent::~QScriptEngineAgent()
243{
244 d_ptr->engine->agentDeleted(this); //### TODO: Can this throw?
245}
246
247/*!
248
249 This function is called when the engine has parsed a script and has
250 associated it with the given \a id. The id can be used to identify
251 this particular script in subsequent event notifications.
252
253 \a program, \a fileName and \a baseLineNumber are the original
254 arguments to the QScriptEngine::evaluate() call that triggered this
255 event.
256
257 This function is called just before the script is about to be
258 evaluated.
259
260 You can reimplement this function to record information about the
261 script; for example, by retaining the script text, you can obtain
262 the line of text corresponding to a line number in a subsequent
263 call to positionChange().
264
265 The default implementation does nothing.
266
267 \sa scriptUnload()
268*/
269void QScriptEngineAgent::scriptLoad(qint64 id, const QString &program,
270 const QString &fileName, int baseLineNumber)
271{
272 Q_UNUSED(id);
273 Q_UNUSED(program);
274 Q_UNUSED(fileName);
275 Q_UNUSED(baseLineNumber);
276}
277
278/*!
279 This function is called when the engine has discarded the script
280 identified by the given \a id.
281
282 You can reimplement this function to clean up any resources you have
283 associated with the script.
284
285 The default implementation does nothing.
286
287 \sa scriptLoad()
288*/
289void QScriptEngineAgent::scriptUnload(qint64 id)
290{
291 Q_UNUSED(id);
292}
293
294/*!
295 This function is called when a new script context has been pushed.
296
297 The default implementation does nothing.
298
299 \sa contextPop(), functionEntry()
300*/
301void QScriptEngineAgent::contextPush()
302{
303}
304
305/*!
306 This function is called when the current script context is about to
307 be popped.
308
309 The default implementation does nothing.
310
311 \sa contextPush(), functionExit()
312*/
313void QScriptEngineAgent::contextPop()
314{
315}
316
317/*!
318 This function is called when a script function is called in the
319 engine. If the script function is not a native Qt Script function,
320 it resides in the script identified by \a scriptId; otherwise, \a
321 scriptId is -1.
322
323 This function is called just before execution of the script function
324 begins. You can obtain the QScriptContext associated with the
325 function call with QScriptEngine::currentContext(). The arguments
326 passed to the function are available.
327
328 Reimplement this function to handle this event. For example, a
329 debugger implementation could reimplement this function (and
330 functionExit()) to keep track of the call stack and provide
331 step-over functionality.
332
333 The default implementation does nothing.
334
335 \sa functionExit(), positionChange(), QScriptEngine::currentContext()
336*/
337void QScriptEngineAgent::functionEntry(qint64 scriptId)
338{
339 Q_UNUSED(scriptId);
340}
341
342/*!
343 This function is called when the currently executing script function
344 is about to return. If the script function is not a native Qt Script
345 function, it resides in the script identified by \a scriptId;
346 otherwise, \a scriptId is -1. The \a returnValue is the value that
347 the script function will return.
348
349 This function is called just before the script function returns.
350 You can still access the QScriptContext associated with the
351 script function call with QScriptEngine::currentContext().
352
353 If the engine's
354 \l{QScriptEngine::hasUncaughtException()}{hasUncaughtException}()
355 function returns true, the script function is exiting due to an
356 exception; otherwise, the script function is returning normally.
357
358 Reimplement this function to handle this event; typically you will
359 then also want to reimplement functionEntry().
360
361 The default implementation does nothing.
362
363 \sa functionEntry(), QScriptEngine::hasUncaughtException()
364*/
365void QScriptEngineAgent::functionExit(qint64 scriptId,
366 const QScriptValue &returnValue)
367{
368 Q_UNUSED(scriptId);
369 Q_UNUSED(returnValue);
370}
371
372/*!
373 This function is called when the engine is about to execute a new
374 statement in the script identified by \a scriptId. The statement
375 begins on the line and column specified by \a lineNumber
376 This event is not generated for native Qt Script functions.
377
378 Reimplement this function to handle this event. For example, a
379 debugger implementation could reimplement this function to provide
380 line-by-line stepping, and a profiler implementation could use it to
381 count the number of times each statement is executed.
382
383 The default implementation does nothing.
384
385 \note \a columnNumber is undefined
386
387 \sa scriptLoad(), functionEntry()
388*/
389void QScriptEngineAgent::positionChange(qint64 scriptId,
390 int lineNumber, int columnNumber)
391{
392 Q_UNUSED(scriptId);
393 Q_UNUSED(lineNumber);
394 Q_UNUSED(columnNumber);
395}
396
397/*!
398 This function is called when the given \a exception has occurred in
399 the engine, in the script identified by \a scriptId. If the
400 exception was thrown by a native Qt Script function, \a scriptId is
401 -1.
402
403 If \a hasHandler is true, there is a \c{catch} or \c{finally} block
404 that will handle the exception. If \a hasHandler is false, there is
405 no handler for the exception.
406
407 Reimplement this function if you want to handle this event. For
408 example, a debugger can notify the user when an uncaught exception
409 occurs (i.e. \a hasHandler is false).
410
411 The default implementation does nothing.
412
413 \sa exceptionCatch()
414*/
415void QScriptEngineAgent::exceptionThrow(qint64 scriptId,
416 const QScriptValue &exception,
417 bool hasHandler)
418{
419 Q_UNUSED(scriptId);
420 Q_UNUSED(exception);
421 Q_UNUSED(hasHandler);
422}
423
424/*!
425 This function is called when the given \a exception is about to be
426 caught, in the script identified by \a scriptId.
427
428 Reimplement this function if you want to handle this event.
429
430 The default implementation does nothing.
431
432 \sa exceptionThrow()
433*/
434void QScriptEngineAgent::exceptionCatch(qint64 scriptId,
435 const QScriptValue &exception)
436{
437 Q_UNUSED(scriptId);
438 Q_UNUSED(exception);
439}
440
441#if 0
442/*!
443 This function is called when a property of the given \a object has
444 been added, changed or removed.
445
446 Reimplement this function if you want to handle this event.
447
448 The default implementation does nothing.
449*/
450void QScriptEngineAgent::propertyChange(qint64 scriptId,
451 const QScriptValue &object,
452 const QString &propertyName,
453 PropertyChange change)
454{
455 Q_UNUSED(scriptId);
456 Q_UNUSED(object);
457 Q_UNUSED(propertyName);
458 Q_UNUSED(change);
459}
460#endif
461
462/*!
463 Returns true if the QScriptEngineAgent supports the given \a
464 extension; otherwise, false is returned. By default, no extensions
465 are supported.
466
467 \sa extension()
468*/
469bool QScriptEngineAgent::supportsExtension(Extension extension) const
470{
471 Q_UNUSED(extension);
472 return false;
473}
474
475/*!
476 This virtual function can be reimplemented in a QScriptEngineAgent
477 subclass to provide support for extensions. The optional \a argument
478 can be provided as input to the \a extension; the result must be
479 returned in the form of a QVariant. You can call supportsExtension()
480 to check if an extension is supported by the QScriptEngineAgent. By
481 default, no extensions are supported, and this function returns an
482 invalid QVariant.
483
484 If you implement the DebuggerInvocationRequest extension, Qt Script
485 will call this function when a \c{debugger} statement is encountered
486 in a script. The \a argument is a QVariantList containing three
487 items: The first item is the scriptId (a qint64), the second item is
488 the line number (an int), and the third item is the column number
489 (an int).
490
491 \sa supportsExtension()
492*/
493QVariant QScriptEngineAgent::extension(Extension extension,
494 const QVariant &argument)
495{
496 Q_UNUSED(extension);
497 Q_UNUSED(argument);
498 return QVariant();
499}
500
501/*!
502 Returns the QScriptEngine that this agent is associated with.
503*/
504QScriptEngine *QScriptEngineAgent::engine() const
505{
506 Q_D(const QScriptEngineAgent);
507 return QScriptEnginePrivate::get(d->engine);
508}
509
510QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.