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