summaryrefslogtreecommitdiff
path: root/src/script/api/qscriptengineagent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/script/api/qscriptengineagent.cpp')
-rw-r--r--src/script/api/qscriptengineagent.cpp510
1 files changed, 510 insertions, 0 deletions
diff --git a/src/script/api/qscriptengineagent.cpp b/src/script/api/qscriptengineagent.cpp
new file mode 100644
index 0000000..1eb3933
--- /dev/null
+++ b/src/script/api/qscriptengineagent.cpp
@@ -0,0 +1,510 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 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-ONLY$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** 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());
+ if (!QScriptEnginePrivate::get(engine)->isEvaluating())
+ JSC::Debugger::recompileAllJSFunctions(engine->globalData);
+}
+
+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;
+ int oldAgentLineNumber = engine->agentLineNumber;
+ engine->currentFrame = frame.callFrame();
+ QScriptValue value(engine->scriptValueFromJSCValue(frame.exception()));
+ engine->agentLineNumber = value.property(QLatin1String("lineNumber")).toInt32();
+ q_ptr->exceptionThrow(sourceID, value, hasHandler);
+ engine->agentLineNumber = oldAgentLineNumber;
+ 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);
+ if (!source) {
+ // QTBUG-6108: We don't have the source for this script, so ignore.
+ return;
+ }
+// column = source->columnNumberFromOffset(column);
+ int column = 1;
+ 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);
+ if (!source) {
+ // QTBUG-6108: We don't have the source for this script, so ignore.
+ return;
+ }
+// column = source->columnNumberFromOffset(column);
+ int column = 1;
+ 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
+ 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.
+
+ \note \a columnNumber is undefined
+
+ \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