/****************************************************************************+ −
**+ −
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).+ −
** All rights reserved.+ −
** Contact: Nokia Corporation (qt-info@nokia.com)+ −
**+ −
** This file is part of the QtScript module of the Qt Toolkit.+ −
**+ −
** $QT_BEGIN_LICENSE:LGPL$+ −
** No Commercial Usage+ −
** This file contains pre-release code and may not be distributed.+ −
** You may use this file in accordance with the terms and conditions+ −
** contained in the Technology Preview License Agreement accompanying+ −
** this package.+ −
**+ −
** GNU Lesser General Public License Usage+ −
** Alternatively, this file may be used under the terms of the GNU Lesser+ −
** General Public License version 2.1 as published by the Free Software+ −
** Foundation and appearing in the file LICENSE.LGPL included in the+ −
** packaging of this file. Please review the following information to+ −
** ensure the GNU Lesser General Public License version 2.1 requirements+ −
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.+ −
**+ −
** In addition, as a special exception, Nokia gives you certain additional+ −
** rights. These rights are described in the Nokia Qt LGPL Exception+ −
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.+ −
**+ −
** If you have questions regarding the use of this file, please contact+ −
** Nokia at qt-info@nokia.com.+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
** $QT_END_LICENSE$+ −
**+ −
****************************************************************************/+ −
+ −
#include "config.h"+ −
#include "qscriptengineagent.h"+ −
#include "qscriptengineagent_p.h"+ −
#include "qscriptengine.h"+ −
#include "qscriptengine_p.h"+ −
+ −
#include "CodeBlock.h"+ −
#include "Instruction.h"+ −
+ −
QT_BEGIN_NAMESPACE+ −
+ −
/*!+ −
\since 4.4+ −
\class QScriptEngineAgent+ −
+ −
\brief The QScriptEngineAgent class provides an interface to report events pertaining to QScriptEngine execution.+ −
+ −
\ingroup script+ −
+ −
+ −
The QScriptEngineAgent class is the basis of tools that monitor and/or control the execution of a+ −
QScriptEngine, such as debuggers and profilers.+ −
+ −
To process script loading and unloading events, reimplement the+ −
scriptLoad() and scriptUnload() functions. scriptLoad() is called+ −
after the input to QScriptEngine::evaluate() has been parsed, right+ −
before the given script is executed. The engine assigns each+ −
script an ID, which is available as one of the arguments to+ −
scriptLoad(); subsequently, other event handlers can use the ID to+ −
identify a particular script. One common usage of scriptLoad() is+ −
to retain the script text, filename and base line number (the+ −
original input to QScriptEngine::evaluate()), so that other event+ −
handlers can e.g. map a line number to the corresponding line of+ −
text.+ −
+ −
scriptUnload() is called when the QScriptEngine has no further use+ −
for a script; the QScriptEngineAgent may at this point safely+ −
discard any resources associated with the script (such as the+ −
script text). Note that after scriptUnload() has been called, the+ −
QScriptEngine may reuse the relevant script ID for new scripts+ −
(i.e. as argument to a subsequent call to scriptLoad()).+ −
+ −
Evaluating the following script will result in scriptUnload()+ −
being called immediately after evaluation has completed:+ −
+ −
\snippet doc/src/snippets/code/src_script_qscriptengineagent.cpp 0+ −
+ −
Evaluating the following script will \b{not} result in a call to+ −
scriptUnload() when evaluation has completed:+ −
+ −
\snippet doc/src/snippets/code/src_script_qscriptengineagent.cpp 1+ −
+ −
The script isn't unloaded because it defines a function (\c{cube})+ −
that remains in the script environment after evaluation has+ −
completed. If a subsequent script removed the \c{cube} function+ −
(e.g. by setting it to \c{null}), scriptUnload() would be called+ −
when the function is garbage collected. In general terms, a script+ −
isn't unloaded until the engine has determined that none of its+ −
contents is referenced.+ −
+ −
To process script function calls and returns, reimplement the+ −
functionEntry() and functionExit() functions. functionEntry() is+ −
called when a script function is about to be executed;+ −
functionExit() is called when a script function is about to return,+ −
either normally or due to an exception.+ −
+ −
To process individual script statements, reimplement+ −
positionChange(). positionChange() is called each time the engine is+ −
about to execute a new statement of a script, and thus offers the+ −
finest level of script monitoring.+ −
+ −
To process exceptions, reimplement exceptionThrow() and+ −
exceptionCatch(). exceptionThrow() is called when a script exception+ −
is thrown, before it has been handled. exceptionCatch() is called+ −
when an exception handler is present, and execution is about to be+ −
resumed at the handler code.+ −
+ −
\sa QScriptEngine::setAgent(), QScriptContextInfo+ −
*/+ −
+ −
/*!+ −
\enum QScriptEngineAgent::Extension+ −
+ −
This enum specifies the possible extensions to a QScriptEngineAgent.+ −
+ −
\value DebuggerInvocationRequest The agent handles \c{debugger} script statements.+ −
+ −
\sa extension()+ −
*/+ −
+ −
+ −
void QScriptEngineAgentPrivate::attach()+ −
{+ −
if (engine->originalGlobalObject()->debugger())+ −
engine->originalGlobalObject()->setDebugger(0);+ −
JSC::Debugger::attach(engine->originalGlobalObject());+ −
}+ −
+ −
void QScriptEngineAgentPrivate::detach()+ −
{+ −
JSC::Debugger::detach(engine->originalGlobalObject());+ −
}+ −
+ −
void QScriptEngineAgentPrivate::returnEvent(const JSC::DebuggerCallFrame& frame, intptr_t sourceID, int lineno)+ −
{+ −
Q_UNUSED(frame);+ −
Q_UNUSED(lineno);+ −
Q_UNUSED(sourceID);+ −
}+ −
+ −
void QScriptEngineAgentPrivate::exceptionThrow(const JSC::DebuggerCallFrame& frame, intptr_t sourceID, bool hasHandler)+ −
{+ −
JSC::CallFrame *oldFrame = engine->currentFrame;+ −
engine->currentFrame = frame.callFrame();+ −
QScriptValue value(engine->scriptValueFromJSCValue(frame.exception()));+ −
q_ptr->exceptionThrow(sourceID, value, hasHandler);+ −
engine->currentFrame = oldFrame;+ −
engine->setCurrentException(value);+ −
};+ −
+ −
void QScriptEngineAgentPrivate::exceptionCatch(const JSC::DebuggerCallFrame& frame, intptr_t sourceID)+ −
{+ −
JSC::CallFrame *oldFrame = engine->currentFrame;+ −
engine->currentFrame = frame.callFrame();+ −
QScriptValue value(engine->scriptValueFromJSCValue(frame.exception()));+ −
q_ptr->exceptionCatch(sourceID, value);+ −
engine->currentFrame = oldFrame;+ −
engine->clearCurrentException();+ −
}+ −
+ −
void QScriptEngineAgentPrivate::atStatement(const JSC::DebuggerCallFrame& frame, intptr_t sourceID, int lineno, int column)+ −
{+ −
QScript::UStringSourceProviderWithFeedback *source = engine->loadedScripts.value(sourceID);+ −
Q_ASSERT(source != 0);+ −
column = source->columnNumberFromOffset(column);+ −
JSC::CallFrame *oldFrame = engine->currentFrame;+ −
int oldAgentLineNumber = engine->agentLineNumber;+ −
engine->currentFrame = frame.callFrame();+ −
engine->agentLineNumber = lineno;+ −
q_ptr->positionChange(sourceID, lineno, column);+ −
engine->currentFrame = oldFrame;+ −
engine->agentLineNumber = oldAgentLineNumber;+ −
}+ −
+ −
void QScriptEngineAgentPrivate::functionExit(const JSC::JSValue& returnValue, intptr_t sourceID)+ −
{+ −
QScriptValue result = engine->scriptValueFromJSCValue(returnValue);+ −
q_ptr->functionExit(sourceID, result);+ −
q_ptr->contextPop();+ −
}+ −
+ −
void QScriptEngineAgentPrivate::evaluateStop(const JSC::JSValue& returnValue, intptr_t sourceID)+ −
{+ −
QScriptValue result = engine->scriptValueFromJSCValue(returnValue);+ −
q_ptr->functionExit(sourceID, result);+ −
}+ −
+ −
void QScriptEngineAgentPrivate::didReachBreakpoint(const JSC::DebuggerCallFrame& frame,+ −
intptr_t sourceID, int lineno, int column)+ −
{+ −
if (q_ptr->supportsExtension(QScriptEngineAgent::DebuggerInvocationRequest)) {+ −
QScript::UStringSourceProviderWithFeedback *source = engine->loadedScripts.value(sourceID);+ −
Q_ASSERT(source != 0);+ −
column = source->columnNumberFromOffset(column);+ −
JSC::CallFrame *oldFrame = engine->currentFrame;+ −
int oldAgentLineNumber = engine->agentLineNumber;+ −
engine->currentFrame = frame.callFrame();+ −
engine->agentLineNumber = lineno;+ −
QList<QVariant> args;+ −
args << qint64(sourceID) << lineno << column;+ −
q_ptr->extension(QScriptEngineAgent::DebuggerInvocationRequest, args);+ −
engine->currentFrame = oldFrame;+ −
engine->agentLineNumber = oldAgentLineNumber;+ −
}+ −
};+ −
+ −
/*!+ −
Constructs a QScriptEngineAgent object for the given \a engine.+ −
+ −
The engine takes ownership of the agent.+ −
+ −
Call QScriptEngine::setAgent() to make this agent the active+ −
agent.+ −
*/+ −
QScriptEngineAgent::QScriptEngineAgent(QScriptEngine *engine)+ −
: d_ptr(new QScriptEngineAgentPrivate())+ −
{+ −
d_ptr->q_ptr = this;+ −
d_ptr->engine = QScriptEnginePrivate::get(engine);+ −
d_ptr->engine->ownedAgents.append(this);+ −
}+ −
+ −
/*!+ −
\internal+ −
*/+ −
QScriptEngineAgent::QScriptEngineAgent(QScriptEngineAgentPrivate &dd, QScriptEngine *engine)+ −
: d_ptr(&dd)+ −
{+ −
d_ptr->q_ptr = this;+ −
d_ptr->engine = QScriptEnginePrivate::get(engine);+ −
}+ −
+ −
/*!+ −
Destroys this QScriptEngineAgent.+ −
*/+ −
QScriptEngineAgent::~QScriptEngineAgent()+ −
{+ −
d_ptr->engine->agentDeleted(this); //### TODO: Can this throw?+ −
}+ −
+ −
/*!+ −
+ −
This function is called when the engine has parsed a script and has+ −
associated it with the given \a id. The id can be used to identify+ −
this particular script in subsequent event notifications.+ −
+ −
\a program, \a fileName and \a baseLineNumber are the original+ −
arguments to the QScriptEngine::evaluate() call that triggered this+ −
event.+ −
+ −
This function is called just before the script is about to be+ −
evaluated.+ −
+ −
You can reimplement this function to record information about the+ −
script; for example, by retaining the script text, you can obtain+ −
the line of text corresponding to a line number in a subsequent+ −
call to positionChange().+ −
+ −
The default implementation does nothing.+ −
+ −
\sa scriptUnload()+ −
*/+ −
void QScriptEngineAgent::scriptLoad(qint64 id, const QString &program,+ −
const QString &fileName, int baseLineNumber)+ −
{+ −
Q_UNUSED(id);+ −
Q_UNUSED(program);+ −
Q_UNUSED(fileName);+ −
Q_UNUSED(baseLineNumber);+ −
}+ −
+ −
/*!+ −
This function is called when the engine has discarded the script+ −
identified by the given \a id.+ −
+ −
You can reimplement this function to clean up any resources you have+ −
associated with the script.+ −
+ −
The default implementation does nothing.+ −
+ −
\sa scriptLoad()+ −
*/+ −
void QScriptEngineAgent::scriptUnload(qint64 id)+ −
{+ −
Q_UNUSED(id);+ −
}+ −
+ −
/*!+ −
This function is called when a new script context has been pushed.+ −
+ −
The default implementation does nothing.+ −
+ −
\sa contextPop(), functionEntry()+ −
*/+ −
void QScriptEngineAgent::contextPush()+ −
{+ −
}+ −
+ −
/*!+ −
This function is called when the current script context is about to+ −
be popped.+ −
+ −
The default implementation does nothing.+ −
+ −
\sa contextPush(), functionExit()+ −
*/+ −
void QScriptEngineAgent::contextPop()+ −
{+ −
}+ −
+ −
/*!+ −
This function is called when a script function is called in the+ −
engine. If the script function is not a native Qt Script function,+ −
it resides in the script identified by \a scriptId; otherwise, \a+ −
scriptId is -1.+ −
+ −
This function is called just before execution of the script function+ −
begins. You can obtain the QScriptContext associated with the+ −
function call with QScriptEngine::currentContext(). The arguments+ −
passed to the function are available.+ −
+ −
Reimplement this function to handle this event. For example, a+ −
debugger implementation could reimplement this function (and+ −
functionExit()) to keep track of the call stack and provide+ −
step-over functionality.+ −
+ −
The default implementation does nothing.+ −
+ −
\sa functionExit(), positionChange(), QScriptEngine::currentContext()+ −
*/+ −
void QScriptEngineAgent::functionEntry(qint64 scriptId)+ −
{+ −
Q_UNUSED(scriptId);+ −
}+ −
+ −
/*!+ −
This function is called when the currently executing script function+ −
is about to return. If the script function is not a native Qt Script+ −
function, it resides in the script identified by \a scriptId;+ −
otherwise, \a scriptId is -1. The \a returnValue is the value that+ −
the script function will return.+ −
+ −
This function is called just before the script function returns.+ −
You can still access the QScriptContext associated with the+ −
script function call with QScriptEngine::currentContext().+ −
+ −
If the engine's+ −
\l{QScriptEngine::hasUncaughtException()}{hasUncaughtException}()+ −
function returns true, the script function is exiting due to an+ −
exception; otherwise, the script function is returning normally.+ −
+ −
Reimplement this function to handle this event; typically you will+ −
then also want to reimplement functionEntry().+ −
+ −
The default implementation does nothing.+ −
+ −
\sa functionEntry(), QScriptEngine::hasUncaughtException()+ −
*/+ −
void QScriptEngineAgent::functionExit(qint64 scriptId,+ −
const QScriptValue &returnValue)+ −
{+ −
Q_UNUSED(scriptId);+ −
Q_UNUSED(returnValue);+ −
}+ −
+ −
/*!+ −
This function is called when the engine is about to execute a new+ −
statement in the script identified by \a scriptId. The statement+ −
begins on the line and column specified by \a lineNumber and \a+ −
columnNumber. This event is not generated for native Qt Script+ −
functions.+ −
+ −
Reimplement this function to handle this event. For example, a+ −
debugger implementation could reimplement this function to provide+ −
line-by-line stepping, and a profiler implementation could use it to+ −
count the number of times each statement is executed.+ −
+ −
The default implementation does nothing.+ −
+ −
\sa scriptLoad(), functionEntry()+ −
*/+ −
void QScriptEngineAgent::positionChange(qint64 scriptId,+ −
int lineNumber, int columnNumber)+ −
{+ −
Q_UNUSED(scriptId);+ −
Q_UNUSED(lineNumber);+ −
Q_UNUSED(columnNumber);+ −
}+ −
+ −
/*!+ −
This function is called when the given \a exception has occurred in+ −
the engine, in the script identified by \a scriptId. If the+ −
exception was thrown by a native Qt Script function, \a scriptId is+ −
-1.+ −
+ −
If \a hasHandler is true, there is a \c{catch} or \c{finally} block+ −
that will handle the exception. If \a hasHandler is false, there is+ −
no handler for the exception.+ −
+ −
Reimplement this function if you want to handle this event. For+ −
example, a debugger can notify the user when an uncaught exception+ −
occurs (i.e. \a hasHandler is false).+ −
+ −
The default implementation does nothing.+ −
+ −
\sa exceptionCatch()+ −
*/+ −
void QScriptEngineAgent::exceptionThrow(qint64 scriptId,+ −
const QScriptValue &exception,+ −
bool hasHandler)+ −
{+ −
Q_UNUSED(scriptId);+ −
Q_UNUSED(exception);+ −
Q_UNUSED(hasHandler);+ −
}+ −
+ −
/*!+ −
This function is called when the given \a exception is about to be+ −
caught, in the script identified by \a scriptId.+ −
+ −
Reimplement this function if you want to handle this event.+ −
+ −
The default implementation does nothing.+ −
+ −
\sa exceptionThrow()+ −
*/+ −
void QScriptEngineAgent::exceptionCatch(qint64 scriptId,+ −
const QScriptValue &exception)+ −
{+ −
Q_UNUSED(scriptId);+ −
Q_UNUSED(exception);+ −
}+ −
+ −
#if 0+ −
/*!+ −
This function is called when a property of the given \a object has+ −
been added, changed or removed.+ −
+ −
Reimplement this function if you want to handle this event.+ −
+ −
The default implementation does nothing.+ −
*/+ −
void QScriptEngineAgent::propertyChange(qint64 scriptId,+ −
const QScriptValue &object,+ −
const QString &propertyName,+ −
PropertyChange change)+ −
{+ −
Q_UNUSED(scriptId);+ −
Q_UNUSED(object);+ −
Q_UNUSED(propertyName);+ −
Q_UNUSED(change);+ −
}+ −
#endif+ −
+ −
/*!+ −
Returns true if the QScriptEngineAgent supports the given \a+ −
extension; otherwise, false is returned. By default, no extensions+ −
are supported.+ −
+ −
\sa extension()+ −
*/+ −
bool QScriptEngineAgent::supportsExtension(Extension extension) const+ −
{+ −
Q_UNUSED(extension);+ −
return false;+ −
}+ −
+ −
/*!+ −
This virtual function can be reimplemented in a QScriptEngineAgent+ −
subclass to provide support for extensions. The optional \a argument+ −
can be provided as input to the \a extension; the result must be+ −
returned in the form of a QVariant. You can call supportsExtension()+ −
to check if an extension is supported by the QScriptEngineAgent. By+ −
default, no extensions are supported, and this function returns an+ −
invalid QVariant.+ −
+ −
If you implement the DebuggerInvocationRequest extension, Qt Script+ −
will call this function when a \c{debugger} statement is encountered+ −
in a script. The \a argument is a QVariantList containing three+ −
items: The first item is the scriptId (a qint64), the second item is+ −
the line number (an int), and the third item is the column number+ −
(an int).+ −
+ −
\sa supportsExtension()+ −
*/+ −
QVariant QScriptEngineAgent::extension(Extension extension,+ −
const QVariant &argument)+ −
{+ −
Q_UNUSED(extension);+ −
Q_UNUSED(argument);+ −
return QVariant();+ −
}+ −
+ −
/*!+ −
Returns the QScriptEngine that this agent is associated with.+ −
*/+ −
QScriptEngine *QScriptEngineAgent::engine() const+ −
{+ −
Q_D(const QScriptEngineAgent);+ −
return QScriptEnginePrivate::get(d->engine);+ −
}+ −
+ −
QT_END_NAMESPACE+ −