util/src/script/api/qscriptengineagent.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 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 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 qt-info@nokia.com.
       
    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 
       
    33 QT_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 
       
   115 void QScriptEngineAgentPrivate::attach()
       
   116 {
       
   117     if (engine->originalGlobalObject()->debugger())
       
   118         engine->originalGlobalObject()->setDebugger(0);
       
   119     JSC::Debugger::attach(engine->originalGlobalObject());
       
   120 }
       
   121 
       
   122 void QScriptEngineAgentPrivate::detach()
       
   123 {
       
   124     JSC::Debugger::detach(engine->originalGlobalObject());
       
   125 }
       
   126 
       
   127 void 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 
       
   134 void 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 
       
   144 void 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 
       
   154 void 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 
       
   171 void 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 
       
   178 void QScriptEngineAgentPrivate::evaluateStop(const JSC::JSValue& returnValue, intptr_t sourceID)
       
   179 {
       
   180     QScriptValue result = engine->scriptValueFromJSCValue(returnValue);
       
   181     q_ptr->functionExit(sourceID, result);
       
   182 }
       
   183 
       
   184 void 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 */
       
   214 QScriptEngineAgent::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 */
       
   225 QScriptEngineAgent::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 */
       
   235 QScriptEngineAgent::~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 */
       
   262 void 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 */
       
   282 void 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 */
       
   294 void 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 */
       
   306 void 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 */
       
   330 void 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 */
       
   358 void 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 */
       
   381 void 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 */
       
   407 void 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 */
       
   426 void 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 */
       
   442 void 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 */
       
   461 bool 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 */
       
   485 QVariant 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 */
       
   496 QScriptEngine *QScriptEngineAgent::engine() const
       
   497 {
       
   498     Q_D(const QScriptEngineAgent);
       
   499     return QScriptEnginePrivate::get(d->engine);
       
   500 }
       
   501 
       
   502 QT_END_NAMESPACE