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

Last change on this file since 636 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

  • Property svn:eol-style set to native
File size: 16.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 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}
121
122void QScriptEngineAgentPrivate::detach()
123{
124 JSC::Debugger::detach(engine->originalGlobalObject());
125}
126
127void QScriptEngineAgentPrivate::returnEvent(const JSC::DebuggerCallFrame& frame, intptr_t sourceID, int lineno)
128{
129 Q_UNUSED(frame);
130 Q_UNUSED(lineno);
131 Q_UNUSED(sourceID);
132}
133
134void QScriptEngineAgentPrivate::exceptionThrow(const JSC::DebuggerCallFrame& frame, intptr_t sourceID, bool hasHandler)
135{
136 JSC::CallFrame *oldFrame = engine->currentFrame;
137 engine->currentFrame = frame.callFrame();
138 QScriptValue value(engine->scriptValueFromJSCValue(frame.exception()));
139 q_ptr->exceptionThrow(sourceID, value, hasHandler);
140 engine->currentFrame = oldFrame;
141 engine->setCurrentException(value);
142};
143
144void QScriptEngineAgentPrivate::exceptionCatch(const JSC::DebuggerCallFrame& frame, intptr_t sourceID)
145{
146 JSC::CallFrame *oldFrame = engine->currentFrame;
147 engine->currentFrame = frame.callFrame();
148 QScriptValue value(engine->scriptValueFromJSCValue(frame.exception()));
149 q_ptr->exceptionCatch(sourceID, value);
150 engine->currentFrame = oldFrame;
151 engine->clearCurrentException();
152}
153
154void QScriptEngineAgentPrivate::atStatement(const JSC::DebuggerCallFrame& frame, intptr_t sourceID, int lineno, int column)
155{
156 QScript::UStringSourceProviderWithFeedback *source = engine->loadedScripts.value(sourceID);
157 if (!source) {
158 // QTBUG-6108: We don't have the source for this script, so ignore.
159 return;
160 }
161 column = source->columnNumberFromOffset(column);
162 JSC::CallFrame *oldFrame = engine->currentFrame;
163 int oldAgentLineNumber = engine->agentLineNumber;
164 engine->currentFrame = frame.callFrame();
165 engine->agentLineNumber = lineno;
166 q_ptr->positionChange(sourceID, lineno, column);
167 engine->currentFrame = oldFrame;
168 engine->agentLineNumber = oldAgentLineNumber;
169}
170
171void QScriptEngineAgentPrivate::functionExit(const JSC::JSValue& returnValue, intptr_t sourceID)
172{
173 QScriptValue result = engine->scriptValueFromJSCValue(returnValue);
174 q_ptr->functionExit(sourceID, result);
175 q_ptr->contextPop();
176}
177
178void QScriptEngineAgentPrivate::evaluateStop(const JSC::JSValue& returnValue, intptr_t sourceID)
179{
180 QScriptValue result = engine->scriptValueFromJSCValue(returnValue);
181 q_ptr->functionExit(sourceID, result);
182}
183
184void QScriptEngineAgentPrivate::didReachBreakpoint(const JSC::DebuggerCallFrame& frame,
185 intptr_t sourceID, int lineno, int column)
186{
187 if (q_ptr->supportsExtension(QScriptEngineAgent::DebuggerInvocationRequest)) {
188 QScript::UStringSourceProviderWithFeedback *source = engine->loadedScripts.value(sourceID);
189 if (!source) {
190 // QTBUG-6108: We don't have the source for this script, so ignore.
191 return;
192 }
193 column = source->columnNumberFromOffset(column);
194 JSC::CallFrame *oldFrame = engine->currentFrame;
195 int oldAgentLineNumber = engine->agentLineNumber;
196 engine->currentFrame = frame.callFrame();
197 engine->agentLineNumber = lineno;
198 QList<QVariant> args;
199 args << qint64(sourceID) << lineno << column;
200 q_ptr->extension(QScriptEngineAgent::DebuggerInvocationRequest, args);
201 engine->currentFrame = oldFrame;
202 engine->agentLineNumber = oldAgentLineNumber;
203 }
204};
205
206/*!
207 Constructs a QScriptEngineAgent object for the given \a engine.
208
209 The engine takes ownership of the agent.
210
211 Call QScriptEngine::setAgent() to make this agent the active
212 agent.
213*/
214QScriptEngineAgent::QScriptEngineAgent(QScriptEngine *engine)
215 : d_ptr(new QScriptEngineAgentPrivate())
216{
217 d_ptr->q_ptr = this;
218 d_ptr->engine = QScriptEnginePrivate::get(engine);
219 d_ptr->engine->ownedAgents.append(this);
220}
221
222/*!
223 \internal
224*/
225QScriptEngineAgent::QScriptEngineAgent(QScriptEngineAgentPrivate &dd, QScriptEngine *engine)
226 : d_ptr(&dd)
227{
228 d_ptr->q_ptr = this;
229 d_ptr->engine = QScriptEnginePrivate::get(engine);
230}
231
232/*!
233 Destroys this QScriptEngineAgent.
234*/
235QScriptEngineAgent::~QScriptEngineAgent()
236{
237 d_ptr->engine->agentDeleted(this); //### TODO: Can this throw?
238}
239
240/*!
241
242 This function is called when the engine has parsed a script and has
243 associated it with the given \a id. The id can be used to identify
244 this particular script in subsequent event notifications.
245
246 \a program, \a fileName and \a baseLineNumber are the original
247 arguments to the QScriptEngine::evaluate() call that triggered this
248 event.
249
250 This function is called just before the script is about to be
251 evaluated.
252
253 You can reimplement this function to record information about the
254 script; for example, by retaining the script text, you can obtain
255 the line of text corresponding to a line number in a subsequent
256 call to positionChange().
257
258 The default implementation does nothing.
259
260 \sa scriptUnload()
261*/
262void QScriptEngineAgent::scriptLoad(qint64 id, const QString &program,
263 const QString &fileName, int baseLineNumber)
264{
265 Q_UNUSED(id);
266 Q_UNUSED(program);
267 Q_UNUSED(fileName);
268 Q_UNUSED(baseLineNumber);
269}
270
271/*!
272 This function is called when the engine has discarded the script
273 identified by the given \a id.
274
275 You can reimplement this function to clean up any resources you have
276 associated with the script.
277
278 The default implementation does nothing.
279
280 \sa scriptLoad()
281*/
282void QScriptEngineAgent::scriptUnload(qint64 id)
283{
284 Q_UNUSED(id);
285}
286
287/*!
288 This function is called when a new script context has been pushed.
289
290 The default implementation does nothing.
291
292 \sa contextPop(), functionEntry()
293*/
294void QScriptEngineAgent::contextPush()
295{
296}
297
298/*!
299 This function is called when the current script context is about to
300 be popped.
301
302 The default implementation does nothing.
303
304 \sa contextPush(), functionExit()
305*/
306void QScriptEngineAgent::contextPop()
307{
308}
309
310/*!
311 This function is called when a script function is called in the
312 engine. If the script function is not a native Qt Script function,
313 it resides in the script identified by \a scriptId; otherwise, \a
314 scriptId is -1.
315
316 This function is called just before execution of the script function
317 begins. You can obtain the QScriptContext associated with the
318 function call with QScriptEngine::currentContext(). The arguments
319 passed to the function are available.
320
321 Reimplement this function to handle this event. For example, a
322 debugger implementation could reimplement this function (and
323 functionExit()) to keep track of the call stack and provide
324 step-over functionality.
325
326 The default implementation does nothing.
327
328 \sa functionExit(), positionChange(), QScriptEngine::currentContext()
329*/
330void QScriptEngineAgent::functionEntry(qint64 scriptId)
331{
332 Q_UNUSED(scriptId);
333}
334
335/*!
336 This function is called when the currently executing script function
337 is about to return. If the script function is not a native Qt Script
338 function, it resides in the script identified by \a scriptId;
339 otherwise, \a scriptId is -1. The \a returnValue is the value that
340 the script function will return.
341
342 This function is called just before the script function returns.
343 You can still access the QScriptContext associated with the
344 script function call with QScriptEngine::currentContext().
345
346 If the engine's
347 \l{QScriptEngine::hasUncaughtException()}{hasUncaughtException}()
348 function returns true, the script function is exiting due to an
349 exception; otherwise, the script function is returning normally.
350
351 Reimplement this function to handle this event; typically you will
352 then also want to reimplement functionEntry().
353
354 The default implementation does nothing.
355
356 \sa functionEntry(), QScriptEngine::hasUncaughtException()
357*/
358void QScriptEngineAgent::functionExit(qint64 scriptId,
359 const QScriptValue &returnValue)
360{
361 Q_UNUSED(scriptId);
362 Q_UNUSED(returnValue);
363}
364
365/*!
366 This function is called when the engine is about to execute a new
367 statement in the script identified by \a scriptId. The statement
368 begins on the line and column specified by \a lineNumber and \a
369 columnNumber. This event is not generated for native Qt Script
370 functions.
371
372 Reimplement this function to handle this event. For example, a
373 debugger implementation could reimplement this function to provide
374 line-by-line stepping, and a profiler implementation could use it to
375 count the number of times each statement is executed.
376
377 The default implementation does nothing.
378
379 \sa scriptLoad(), functionEntry()
380*/
381void QScriptEngineAgent::positionChange(qint64 scriptId,
382 int lineNumber, int columnNumber)
383{
384 Q_UNUSED(scriptId);
385 Q_UNUSED(lineNumber);
386 Q_UNUSED(columnNumber);
387}
388
389/*!
390 This function is called when the given \a exception has occurred in
391 the engine, in the script identified by \a scriptId. If the
392 exception was thrown by a native Qt Script function, \a scriptId is
393 -1.
394
395 If \a hasHandler is true, there is a \c{catch} or \c{finally} block
396 that will handle the exception. If \a hasHandler is false, there is
397 no handler for the exception.
398
399 Reimplement this function if you want to handle this event. For
400 example, a debugger can notify the user when an uncaught exception
401 occurs (i.e. \a hasHandler is false).
402
403 The default implementation does nothing.
404
405 \sa exceptionCatch()
406*/
407void QScriptEngineAgent::exceptionThrow(qint64 scriptId,
408 const QScriptValue &exception,
409 bool hasHandler)
410{
411 Q_UNUSED(scriptId);
412 Q_UNUSED(exception);
413 Q_UNUSED(hasHandler);
414}
415
416/*!
417 This function is called when the given \a exception is about to be
418 caught, in the script identified by \a scriptId.
419
420 Reimplement this function if you want to handle this event.
421
422 The default implementation does nothing.
423
424 \sa exceptionThrow()
425*/
426void QScriptEngineAgent::exceptionCatch(qint64 scriptId,
427 const QScriptValue &exception)
428{
429 Q_UNUSED(scriptId);
430 Q_UNUSED(exception);
431}
432
433#if 0
434/*!
435 This function is called when a property of the given \a object has
436 been added, changed or removed.
437
438 Reimplement this function if you want to handle this event.
439
440 The default implementation does nothing.
441*/
442void QScriptEngineAgent::propertyChange(qint64 scriptId,
443 const QScriptValue &object,
444 const QString &propertyName,
445 PropertyChange change)
446{
447 Q_UNUSED(scriptId);
448 Q_UNUSED(object);
449 Q_UNUSED(propertyName);
450 Q_UNUSED(change);
451}
452#endif
453
454/*!
455 Returns true if the QScriptEngineAgent supports the given \a
456 extension; otherwise, false is returned. By default, no extensions
457 are supported.
458
459 \sa extension()
460*/
461bool QScriptEngineAgent::supportsExtension(Extension extension) const
462{
463 Q_UNUSED(extension);
464 return false;
465}
466
467/*!
468 This virtual function can be reimplemented in a QScriptEngineAgent
469 subclass to provide support for extensions. The optional \a argument
470 can be provided as input to the \a extension; the result must be
471 returned in the form of a QVariant. You can call supportsExtension()
472 to check if an extension is supported by the QScriptEngineAgent. By
473 default, no extensions are supported, and this function returns an
474 invalid QVariant.
475
476 If you implement the DebuggerInvocationRequest extension, Qt Script
477 will call this function when a \c{debugger} statement is encountered
478 in a script. The \a argument is a QVariantList containing three
479 items: The first item is the scriptId (a qint64), the second item is
480 the line number (an int), and the third item is the column number
481 (an int).
482
483 \sa supportsExtension()
484*/
485QVariant QScriptEngineAgent::extension(Extension extension,
486 const QVariant &argument)
487{
488 Q_UNUSED(extension);
489 Q_UNUSED(argument);
490 return QVariant();
491}
492
493/*!
494 Returns the QScriptEngine that this agent is associated with.
495*/
496QScriptEngine *QScriptEngineAgent::engine() const
497{
498 Q_D(const QScriptEngineAgent);
499 return QScriptEnginePrivate::get(d->engine);
500}
501
502QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.