| /**************************************************************************** |
| ** |
| ** Copyright (C) 2018 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the test suite of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| |
| #include <QtTest/QtTest> |
| |
| #include <QtScript/qscriptengineagent.h> |
| #include <QtScript/qscriptengine.h> |
| #include <QtScript/qscriptprogram.h> |
| #include <qscriptvalueiterator.h> |
| |
| QT_BEGIN_NAMESPACE |
| extern bool qt_script_isJITEnabled(); |
| QT_END_NAMESPACE |
| |
| class tst_QScriptEngineAgent : public QObject |
| { |
| Q_OBJECT |
| Q_PROPERTY(double testProperty READ testProperty WRITE setTestProperty) |
| |
| public: |
| tst_QScriptEngineAgent(); |
| virtual ~tst_QScriptEngineAgent(); |
| |
| double testProperty() const { return m_testProperty; } |
| void setTestProperty(double val) { m_testProperty = val; } |
| |
| public slots: |
| double testSlot(double arg) { return arg; } |
| |
| signals: |
| void testSignal(double arg); |
| |
| private slots: |
| void unloadRecursion(); |
| void scriptLoadAndUnload_statement(); |
| void scriptLoadAndUnload(); |
| void scriptLoadAndUnload_eval(); |
| void contextPushAndPop(); |
| void functionEntryAndExit_semicolon(); |
| void functionEntryAndExit_expression(); |
| void functionEntryAndExit_functionCall(); |
| void functionEntryAndExit_functionCallWithoutReturn(); |
| void functionEntryAndExit_functionDefinition(); |
| void functionEntryAndExit_native(); |
| void functionEntryAndExit_native2(); |
| void functionEntryAndExit_nativeThrowing(); |
| void functionEntryAndExit_builtin_data(); |
| void functionEntryAndExit_builtin(); |
| void functionEntryAndExit_objects(); |
| void functionEntryAndExit_slots(); |
| void functionEntryAndExit_property_set(); |
| void functionEntryAndExit_property_get(); |
| void functionEntryAndExit_call(); |
| void functionEntryAndExit_functionReturn_construct(); |
| void functionEntryAndExit_functionReturn_call(); |
| void functionEntryAndExit_objectCall(); |
| void positionChange_1(); |
| void positionChange_2(); |
| void positionChange_3(); |
| void exceptionThrowAndCatch(); |
| void eventOrder_assigment(); |
| void eventOrder_functionDefinition(); |
| void eventOrder_throwError(); |
| void eventOrder_throwAndCatch(); |
| void eventOrder_functions(); |
| void eventOrder_throwCatchFinally(); |
| void eventOrder_signalsHandling(); |
| void recursiveObserve(); |
| void multipleAgents(); |
| void syntaxError(); |
| void extension_invoctaion(); |
| void extension(); |
| void isEvaluatingInExtension(); |
| void hasUncaughtException(); |
| void evaluateProgram(); |
| void evaluateProgram_SyntaxError(); |
| void evaluateNullProgram(); |
| void QTBUG6108(); |
| void backtraces_data(); |
| void backtraces(); |
| |
| private: |
| double m_testProperty; |
| }; |
| |
| tst_QScriptEngineAgent::tst_QScriptEngineAgent() |
| { |
| } |
| |
| tst_QScriptEngineAgent::~tst_QScriptEngineAgent() |
| { |
| } |
| |
| struct ScriptEngineEvent |
| { |
| enum Type { |
| ScriptLoad, |
| ScriptUnload,//1 |
| ContextPush, |
| ContextPop, //3 |
| FunctionEntry, //4 |
| FunctionExit, //5 |
| PositionChange, |
| ExceptionThrow,//7 |
| ExceptionCatch, |
| DebuggerInvocationRequest |
| }; |
| |
| Type type; |
| |
| qint64 scriptId; |
| QString script; |
| QString fileName; |
| int lineNumber; |
| int columnNumber; |
| QScriptValue value; |
| bool hasExceptionHandler; |
| |
| ScriptEngineEvent(qint64 scriptId, |
| const QString &script, const QString &fileName, |
| int lineNumber) |
| : type(ScriptLoad), scriptId(scriptId), |
| script(script), fileName(fileName), |
| lineNumber(lineNumber) |
| { } |
| |
| ScriptEngineEvent(Type type, qint64 scriptId = -777) |
| : type(type), scriptId(scriptId) |
| { } |
| |
| ScriptEngineEvent(Type type, qint64 scriptId, |
| const QScriptValue &value) |
| : type(type), scriptId(scriptId), |
| value(value) |
| { } |
| |
| ScriptEngineEvent(qint64 scriptId, |
| int lineNumber, int columnNumber) |
| : type(PositionChange), scriptId(scriptId), |
| lineNumber(lineNumber), columnNumber(columnNumber) |
| { } |
| |
| ScriptEngineEvent(qint64 scriptId, |
| const QScriptValue &exception, bool hasHandler) |
| : type(ExceptionThrow), scriptId(scriptId), |
| value(exception), hasExceptionHandler(hasHandler) |
| { } |
| |
| static QString typeToQString(Type t) |
| { |
| switch (t) { |
| case ScriptEngineEvent::ScriptLoad: return "ScriptLoad"; |
| case ScriptEngineEvent::ScriptUnload: return "ScriptUnload"; |
| case ScriptEngineEvent::ContextPush: return "ContextPush"; |
| case ScriptEngineEvent::ContextPop: return "ContextPop"; |
| case ScriptEngineEvent::FunctionEntry: return "FunctionEntry"; |
| case ScriptEngineEvent::FunctionExit: return "FunctionExit"; |
| case ScriptEngineEvent::PositionChange: return "PositionChange"; |
| case ScriptEngineEvent::ExceptionThrow: return "ExceptionThrow"; |
| case ScriptEngineEvent::ExceptionCatch: return "ExceptionCatch"; |
| case ScriptEngineEvent::DebuggerInvocationRequest: return "DebuggerInvocationRequest"; |
| } |
| } |
| }; |
| |
| class ScriptEngineSpy : public QScriptEngineAgent, public QList<ScriptEngineEvent> |
| { |
| public: |
| enum IgnoreFlag { |
| IgnoreScriptLoad = 0x001, |
| IgnoreScriptUnload = 0x002, |
| IgnoreFunctionEntry = 0x004, |
| IgnoreFunctionExit = 0x008, |
| IgnorePositionChange = 0x010, |
| IgnoreExceptionThrow = 0x020, |
| IgnoreExceptionCatch = 0x040, |
| IgnoreContextPush = 0x0100, |
| IgnoreContextPop = 0x0200, |
| IgnoreDebuggerInvocationRequest = 0x0400 |
| }; |
| |
| ScriptEngineSpy(QScriptEngine *engine, int ignores = 0); |
| ~ScriptEngineSpy(); |
| |
| void enableIgnoreFlags(int flags) |
| { m_ignores |= flags; } |
| void disableIgnoreFlags(int flags) |
| { m_ignores &= ~flags; } |
| |
| protected: |
| void scriptLoad(qint64 id, const QString &script, |
| const QString &fileName, int lineNumber); |
| void scriptUnload(qint64 id); |
| |
| void contextPush(); |
| void contextPop(); |
| |
| void functionEntry(qint64 scriptId); |
| void functionExit(qint64 scriptId, const QScriptValue &returnValue); |
| |
| void positionChange(qint64 scriptId, |
| int lineNumber, int columnNumber); |
| |
| void exceptionThrow(qint64 scriptId, const QScriptValue &exception, |
| bool hasHandler); |
| void exceptionCatch(qint64 scriptId, const QScriptValue &exception); |
| |
| bool supportsExtension(Extension ext) const; |
| QVariant extension(Extension ext, const QVariant &arg); |
| |
| private: |
| int m_ignores; |
| }; |
| |
| ScriptEngineSpy::ScriptEngineSpy(QScriptEngine *engine, int ignores) |
| : QScriptEngineAgent(engine) |
| { |
| m_ignores = ignores; |
| engine->setAgent(this); |
| } |
| |
| ScriptEngineSpy::~ScriptEngineSpy() |
| { |
| } |
| |
| void ScriptEngineSpy::scriptLoad(qint64 id, const QString &script, |
| const QString &fileName, int lineNumber) |
| { |
| if (!(m_ignores & IgnoreScriptLoad)) |
| append(ScriptEngineEvent(id, script, fileName, lineNumber)); |
| } |
| |
| void ScriptEngineSpy::scriptUnload(qint64 id) |
| { |
| if (!(m_ignores & IgnoreScriptUnload)) |
| append(ScriptEngineEvent(ScriptEngineEvent::ScriptUnload, id)); |
| } |
| |
| void ScriptEngineSpy::contextPush() |
| { |
| if (!(m_ignores & IgnoreContextPush)) |
| append(ScriptEngineEvent(ScriptEngineEvent::ContextPush)); |
| } |
| |
| void ScriptEngineSpy::contextPop() |
| { |
| if (!(m_ignores & IgnoreContextPop)) |
| append(ScriptEngineEvent(ScriptEngineEvent::ContextPop)); |
| } |
| |
| void ScriptEngineSpy::functionEntry(qint64 scriptId) |
| { |
| if (!(m_ignores & IgnoreFunctionEntry)) |
| append(ScriptEngineEvent(ScriptEngineEvent::FunctionEntry, scriptId)); |
| } |
| |
| void ScriptEngineSpy::functionExit(qint64 scriptId, |
| const QScriptValue &returnValue) |
| { |
| if (!(m_ignores & IgnoreFunctionExit)) |
| append(ScriptEngineEvent(ScriptEngineEvent::FunctionExit, scriptId, returnValue)); |
| } |
| |
| void ScriptEngineSpy::positionChange(qint64 scriptId, |
| int lineNumber, int columnNumber) |
| { |
| if (!(m_ignores & IgnorePositionChange)) |
| append(ScriptEngineEvent(scriptId, lineNumber, columnNumber)); |
| } |
| |
| void ScriptEngineSpy::exceptionThrow(qint64 scriptId, |
| const QScriptValue &exception, bool hasHandler) |
| { |
| if (!(m_ignores & IgnoreExceptionThrow)) |
| append(ScriptEngineEvent(scriptId, exception, hasHandler)); |
| } |
| |
| void ScriptEngineSpy::exceptionCatch(qint64 scriptId, |
| const QScriptValue &exception) |
| { |
| if (!(m_ignores & IgnoreExceptionCatch)) |
| append(ScriptEngineEvent(ScriptEngineEvent::ExceptionCatch, scriptId, exception)); |
| } |
| |
| bool ScriptEngineSpy::supportsExtension(Extension ext) const |
| { |
| if (ext == DebuggerInvocationRequest) |
| return !(m_ignores & IgnoreDebuggerInvocationRequest); |
| return false; |
| } |
| |
| QVariant ScriptEngineSpy::extension(Extension ext, const QVariant &arg) |
| { |
| if (ext == DebuggerInvocationRequest) { |
| QVariantList lst = arg.toList(); |
| qint64 scriptId = lst.at(0).toLongLong(); |
| int lineNumber = lst.at(1).toInt(); |
| int columnNumber = lst.at(2).toInt(); |
| ScriptEngineEvent evt(scriptId, lineNumber, columnNumber); |
| evt.type = ScriptEngineEvent::DebuggerInvocationRequest; |
| append(evt); |
| return QString::fromLatin1("extension(DebuggerInvocationRequest)"); |
| } |
| return QVariant(); |
| } |
| |
| static void collectScriptObjects(QScriptEngine *engine) |
| { |
| // We call garbage collection few times to collect objects that |
| // are unreferenced after first gc. We try to force full gc. |
| engine->collectGarbage(); |
| engine->collectGarbage(); |
| engine->collectGarbage(); |
| } |
| |
| class EvaluatingAgent : public QScriptEngineAgent { |
| public: |
| EvaluatingAgent(QScriptEngine *engine) |
| : QScriptEngineAgent(engine) |
| , count(0) |
| {} |
| |
| virtual void scriptUnload(qint64) |
| { |
| if (++count > 10) // recursion breaker. |
| return; |
| // check if recursive evaluation works |
| engine()->evaluate(";"); |
| collectScriptObjects(engine()); |
| } |
| |
| bool isOk() const { return count > 10; } |
| private: |
| int count; |
| }; |
| |
| void tst_QScriptEngineAgent::unloadRecursion() |
| { |
| QScriptEngine engine; |
| EvaluatingAgent *agent = new EvaluatingAgent(&engine); |
| engine.setAgent(agent); |
| engine.evaluate(";"); |
| collectScriptObjects(&engine); |
| QVERIFY(agent->isOk()); |
| } |
| |
| void tst_QScriptEngineAgent::scriptLoadAndUnload_statement() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreScriptLoad |
| | ScriptEngineSpy::IgnoreScriptUnload)); |
| QCOMPARE(eng.agent(), (QScriptEngineAgent*)spy); |
| { |
| spy->clear(); |
| QString code = ";"; |
| QString fileName = "foo.qs"; |
| int lineNumber = 123; |
| eng.evaluate(code, fileName, lineNumber); |
| |
| // Script object have to be garbage collected first. |
| collectScriptObjects(&eng); |
| QCOMPARE(spy->count(), 2); |
| |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).script, code); |
| QCOMPARE(spy->at(0).fileName, fileName); |
| QCOMPARE(spy->at(0).lineNumber, lineNumber); |
| |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::ScriptUnload); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| } |
| |
| { |
| spy->clear(); |
| QString code = ";"; |
| QString fileName = "bar.qs"; |
| int lineNumber = 456; |
| eng.evaluate(code, fileName, lineNumber); |
| |
| // Script object have to be garbage collected first. |
| collectScriptObjects(&eng); |
| QCOMPARE(spy->count(), 2); |
| |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).script, code); |
| QCOMPARE(spy->at(0).fileName, fileName); |
| QCOMPARE(spy->at(0).lineNumber, lineNumber); |
| |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::ScriptUnload); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| } |
| delete spy; |
| } |
| |
| void tst_QScriptEngineAgent::scriptLoadAndUnload() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreScriptLoad |
| | ScriptEngineSpy::IgnoreScriptUnload)); |
| QCOMPARE(eng.agent(), (QScriptEngineAgent*)spy); |
| { |
| spy->clear(); |
| QString code = "function foo() { print('ciao'); }"; |
| QString fileName = "baz.qs"; |
| int lineNumber = 789; |
| eng.evaluate(code, fileName, lineNumber); |
| |
| QCOMPARE(spy->count(), 1); |
| |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).script, code); |
| QCOMPARE(spy->at(0).fileName, fileName); |
| QCOMPARE(spy->at(0).lineNumber, lineNumber); |
| |
| code = "foo = null"; |
| eng.evaluate(code); |
| collectScriptObjects(&eng); // foo() is GC'ed |
| QCOMPARE(spy->count(), 4); |
| |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::ScriptLoad); |
| QVERIFY(spy->at(1).scriptId != -1); |
| QVERIFY(spy->at(1).scriptId != spy->at(0).scriptId); |
| QCOMPARE(spy->at(1).script, code); |
| QCOMPARE(spy->at(1).lineNumber, 1); |
| |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::ScriptUnload); |
| QCOMPARE(spy->at(2).scriptId, spy->at(1).scriptId); |
| |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::ScriptUnload); |
| QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId); |
| } |
| |
| { |
| spy->clear(); |
| QString code = "function foo() { return function() { print('ciao'); } }"; |
| QString fileName = "foo.qs"; |
| int lineNumber = 123; |
| eng.evaluate(code, fileName, lineNumber); |
| |
| QCOMPARE(spy->count(), 1); |
| |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).script, code); |
| QCOMPARE(spy->at(0).fileName, fileName); |
| QCOMPARE(spy->at(0).lineNumber, lineNumber); |
| |
| code = "bar = foo(); foo = null"; |
| eng.evaluate(code); |
| collectScriptObjects(&eng); |
| QCOMPARE(spy->count(), 3); |
| |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::ScriptLoad); |
| QVERIFY(spy->at(1).scriptId != -1); |
| QVERIFY(spy->at(1).scriptId != spy->at(0).scriptId); |
| QCOMPARE(spy->at(1).script, code); |
| |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::ScriptUnload); |
| QCOMPARE(spy->at(2).scriptId, spy->at(1).scriptId); |
| |
| collectScriptObjects(&eng); // foo() is not GC'ed |
| QCOMPARE(spy->count(), 3); |
| |
| code = "bar = null"; |
| eng.evaluate(code); |
| collectScriptObjects(&eng); // foo() is GC'ed |
| QCOMPARE(spy->count(), 6); |
| } |
| delete spy; |
| } |
| |
| void tst_QScriptEngineAgent::scriptLoadAndUnload_eval() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreScriptLoad |
| | ScriptEngineSpy::IgnoreScriptUnload)); |
| { |
| spy->clear(); |
| eng.evaluate("eval('function foo() { print(123); }')"); |
| |
| QEXPECT_FAIL("","QTBUG-6140 Eval is threaded in different way that in old backend", Abort); |
| QCOMPARE(spy->count(), 3); |
| |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad); |
| QVERIFY(spy->at(0).scriptId != -1); |
| |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::ScriptLoad); |
| QVERIFY(spy->at(1).scriptId != -1); |
| QVERIFY(spy->at(1).scriptId != spy->at(0).scriptId); |
| |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::ScriptUnload); |
| QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId); |
| } |
| delete spy; |
| } |
| |
| void tst_QScriptEngineAgent::contextPushAndPop() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreContextPush |
| | ScriptEngineSpy::IgnoreContextPop)); |
| |
| { |
| spy->clear(); |
| eng.pushContext(); |
| eng.popContext(); |
| QCOMPARE(spy->count(), 2); |
| |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ContextPush); |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::ContextPop); |
| } |
| } |
| |
| static QScriptValue nativeFunctionReturningArg(QScriptContext *ctx, QScriptEngine *) |
| { |
| return ctx->argument(0); |
| } |
| |
| static QScriptValue nativeFunctionThrowingError(QScriptContext *ctx, QScriptEngine *) |
| { |
| return ctx->throwError(ctx->argument(0).toString()); |
| } |
| |
| static QScriptValue nativeFunctionCallingArg(QScriptContext *ctx, QScriptEngine *) |
| { |
| return ctx->argument(0).call(); |
| } |
| |
| /** check behaiviour of ';' */ |
| void tst_QScriptEngineAgent::functionEntryAndExit_semicolon() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry |
| | ScriptEngineSpy::IgnoreFunctionExit)); |
| { |
| spy->clear(); |
| eng.evaluate(";"); |
| |
| QCOMPARE(spy->count(), 2); |
| |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); |
| QVERIFY(spy->at(0).scriptId != -1); |
| |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(1).value.isUndefined()); |
| } |
| delete spy; |
| } |
| |
| /** check behaiviour of expression */ |
| void tst_QScriptEngineAgent::functionEntryAndExit_expression() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry |
| | ScriptEngineSpy::IgnoreFunctionExit)); |
| { |
| spy->clear(); |
| eng.evaluate("1 + 2"); |
| |
| QCOMPARE(spy->count(), 2); |
| |
| // evaluate() entry |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); |
| QVERIFY(spy->at(0).scriptId != -1); |
| |
| // evaluate() exit |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(1).value.isNumber()); |
| QCOMPARE(spy->at(1).value.toNumber(), qsreal(3)); |
| } |
| delete spy; |
| } |
| |
| /** check behaiviour of standard function call */ |
| void tst_QScriptEngineAgent::functionEntryAndExit_functionCall() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry |
| | ScriptEngineSpy::IgnoreFunctionExit)); |
| { |
| spy->clear(); |
| QVERIFY(eng.evaluate("(function() { return 123; } )()").toNumber()==123); |
| |
| QCOMPARE(spy->count(), 4); |
| |
| // evaluate() entry |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); |
| QVERIFY(spy->at(0).scriptId != -1); |
| |
| // anonymous function entry |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| |
| // anonymous function exit |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(2).value.isNumber()); |
| QCOMPARE(spy->at(2).value.toNumber(), qsreal(123)); |
| |
| // evaluate() exit |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(3).value.isNumber()); |
| QCOMPARE(spy->at(3).value.toNumber(), qsreal(123)); |
| } |
| delete spy; |
| } |
| |
| /** check behaiviour of standard function call */ |
| void tst_QScriptEngineAgent::functionEntryAndExit_functionCallWithoutReturn() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry |
| | ScriptEngineSpy::IgnoreFunctionExit)); |
| { |
| spy->clear(); |
| eng.evaluate("(function() { var a = 123; } )()"); |
| |
| QCOMPARE(spy->count(), 4); |
| |
| // evaluate() entry |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); |
| QVERIFY(spy->at(0).scriptId != -1); |
| |
| // anonymous function entry |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| |
| // anonymous function exit |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId); |
| |
| // evaluate() exit |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId); |
| } |
| delete spy; |
| } |
| |
| /** check behaiviour of function definition */ |
| void tst_QScriptEngineAgent::functionEntryAndExit_functionDefinition() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry |
| | ScriptEngineSpy::IgnoreFunctionExit)); |
| { |
| spy->clear(); |
| eng.evaluate("function foo() { return 456; }"); |
| QCOMPARE(spy->count(), 2); |
| |
| // evaluate() entry |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); |
| QVERIFY(spy->at(0).scriptId != -1); |
| |
| // evaluate() exit |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(1).value.isUndefined()); |
| |
| eng.evaluate("foo()"); |
| QCOMPARE(spy->count(), 6); |
| |
| // evaluate() entry |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionEntry); |
| QVERIFY(spy->at(2).scriptId != spy->at(0).scriptId); |
| |
| // foo() entry |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId); |
| |
| // foo() exit |
| QCOMPARE(spy->at(4).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(4).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(4).value.isNumber()); |
| QCOMPARE(spy->at(4).value.toNumber(), qsreal(456)); |
| |
| // evaluate() exit |
| QCOMPARE(spy->at(5).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(5).scriptId, spy->at(2).scriptId); |
| QVERIFY(spy->at(5).value.isNumber()); |
| QCOMPARE(spy->at(5).value.toNumber(), qsreal(456)); |
| } |
| delete spy; |
| } |
| |
| /** check behaiviour of native function */ |
| void tst_QScriptEngineAgent::functionEntryAndExit_native() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry |
| | ScriptEngineSpy::IgnoreFunctionExit)); |
| // native functions |
| { |
| QScriptValue fun = eng.newFunction(nativeFunctionReturningArg); |
| eng.globalObject().setProperty("nativeFunctionReturningArg", fun); |
| |
| spy->clear(); |
| eng.evaluate("nativeFunctionReturningArg(123)"); |
| |
| QCOMPARE(spy->count(), 4); |
| |
| // evaluate() entry |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); |
| |
| // native function entry |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(1).scriptId, qint64(-1)); |
| |
| // native function exit |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(2).scriptId, qint64(-1)); |
| QVERIFY(spy->at(2).value.isNumber()); |
| QCOMPARE(spy->at(2).value.toNumber(), qsreal(123)); |
| |
| // evaluate() exit |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(3).value.isNumber()); |
| QCOMPARE(spy->at(3).value.toNumber(), qsreal(123)); |
| } |
| delete spy; |
| } |
| |
| /** check behaiviour of native function */ |
| void tst_QScriptEngineAgent::functionEntryAndExit_native2() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry |
| | ScriptEngineSpy::IgnoreFunctionExit)); |
| { |
| QScriptValue fun = eng.newFunction(nativeFunctionCallingArg); |
| eng.globalObject().setProperty("nativeFunctionCallingArg", fun); |
| |
| spy->clear(); |
| eng.evaluate("nativeFunctionCallingArg(function() { return 123; })"); |
| QCOMPARE(spy->count(), 6); |
| |
| // evaluate() entry |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); |
| |
| // native function entry |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(1).scriptId, qint64(-1)); |
| |
| // script function entry |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId); |
| |
| // script function exit |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId); |
| |
| // native function exit |
| QCOMPARE(spy->at(4).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(4).scriptId, qint64(-1)); |
| QVERIFY(spy->at(4).value.isNumber()); |
| QCOMPARE(spy->at(4).value.toNumber(), qsreal(123)); |
| |
| // evaluate() exit |
| QCOMPARE(spy->at(5).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(5).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(5).value.isNumber()); |
| QCOMPARE(spy->at(5).value.toNumber(), qsreal(123)); |
| } |
| delete spy; |
| } |
| |
| /** check behavior of native function throwing error*/ |
| void tst_QScriptEngineAgent::functionEntryAndExit_nativeThrowing() |
| { |
| /* This function was changed from old backend. JSC return more Entrys / Exits, (exactly +1) |
| in exception creation time */ |
| |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry |
| | ScriptEngineSpy::IgnoreFunctionExit)); |
| { |
| QScriptValue fun = eng.newFunction(nativeFunctionThrowingError); |
| eng.globalObject().setProperty("nativeFunctionThrowingError", fun); |
| |
| spy->clear(); |
| eng.evaluate("nativeFunctionThrowingError('ciao')"); |
| QCOMPARE(spy->count(), 6); |
| |
| // evaluate() entry |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); |
| |
| // native function entry |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(1).scriptId, qint64(-1)); |
| |
| // Exception constructor entry |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(2).scriptId, qint64(-1)); |
| |
| // Exception constructor exit |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(3).scriptId, qint64(-1)); |
| QVERIFY(spy->at(3).value.isError()); |
| |
| // native function exit |
| QCOMPARE(spy->at(4).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(4).scriptId, qint64(-1)); |
| QVERIFY(spy->at(4).value.isError()); |
| |
| // evaluate() exit |
| QCOMPARE(spy->at(5).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(5).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(5).value.isError()); |
| } |
| delete spy; |
| } |
| |
| void tst_QScriptEngineAgent::functionEntryAndExit_builtin_data() |
| { |
| QTest::addColumn<QString>("script"); |
| QTest::addColumn<QString>("result"); |
| |
| QTest::newRow("string native") << "'ciao'.toString()" << "ciao"; |
| QTest::newRow("string object") << "String('ciao').toString()" << "ciao"; |
| QTest::newRow("number native") << "(123).toString()" << "123"; |
| QTest::newRow("number object") << "Number(123).toString()" << "123"; |
| QTest::newRow("array native") << "['s','a'].toString()" << "s, a"; |
| QTest::newRow("array object") << "Array('s', 'a').toString()" << "s,a"; |
| QTest::newRow("boolean native") << "false.toString()" << "false"; |
| QTest::newRow("boolean object") << "Boolean(true).toString()" << "true"; |
| QTest::newRow("regexp native") << "/a/.toString()" << "/a/"; |
| QTest::newRow("regexp object") << "RegExp('a').toString()" << "/a/"; |
| } |
| |
| /** check behaiviour of built-in function */ |
| void tst_QScriptEngineAgent::functionEntryAndExit_builtin() |
| { |
| QSKIP("The test fails on platforms others than Linux. The issue will be fixed with next JSC update"); |
| QFETCH(QString, script); |
| QFETCH(QString, result); |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry |
| | ScriptEngineSpy::IgnoreFunctionExit)); |
| { |
| spy->clear(); |
| eng.evaluate(script); |
| |
| if (qt_script_isJITEnabled()) { |
| QEXPECT_FAIL("string native", "QTBUG-6187 Some events are missing when JIT is enabled", Abort); |
| QEXPECT_FAIL("number native", "QTBUG-6187 Some events are missing when JIT is enabled", Abort); |
| QEXPECT_FAIL("array native", "QTBUG-6187 Some events are missing when JIT is enabled", Abort); |
| QEXPECT_FAIL("boolean native", "QTBUG-6187 Some events are missing when JIT is enabled", Abort); |
| QEXPECT_FAIL("regexp native", "QTBUG-6187 Some events are missing when JIT is enabled", Abort); |
| } |
| QCOMPARE(spy->count(), 4); |
| |
| // evaluate() entry |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); |
| |
| // built-in native function entry |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(1).scriptId, qint64(-1)); |
| |
| // built-in native function exit |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(2).scriptId, qint64(-1)); |
| QCOMPARE(spy->at(2).value.toString(), QString(result)); |
| |
| // evaluate() exit |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(3).value.isString()); |
| QCOMPARE(spy->at(3).value.toString(), QString(result)); |
| } |
| delete spy; |
| } |
| |
| /** check behaiviour of object creation*/ |
| void tst_QScriptEngineAgent::functionEntryAndExit_objects() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry |
| | ScriptEngineSpy::IgnoreFunctionExit)); |
| { |
| spy->clear(); |
| eng.evaluate("Array(); Boolean(); Date(); Function(); Number(); Object(); RegExp(); String()"); |
| QCOMPARE(spy->count(), 18); |
| |
| // evaluate() entry |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); |
| |
| // Array constructor entry |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(1).scriptId, qint64(-1)); |
| |
| // Array constructor exit |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(2).scriptId, qint64(-1)); |
| QVERIFY(spy->at(2).value.isArray()); |
| |
| // Boolean constructor entry |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(3).scriptId, qint64(-1)); |
| |
| // Boolean constructor exit |
| QCOMPARE(spy->at(4).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(4).scriptId, qint64(-1)); |
| QVERIFY(spy->at(4).value.isBoolean()); |
| |
| // Date constructor entry |
| QCOMPARE(spy->at(5).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(5).scriptId, qint64(-1)); |
| |
| // Date constructor exit |
| QCOMPARE(spy->at(6).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(6).scriptId, qint64(-1)); |
| QVERIFY(spy->at(6).value.isString()); |
| |
| // Function constructor entry |
| QCOMPARE(spy->at(7).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(7).scriptId, qint64(-1)); |
| |
| // Function constructor exit |
| QCOMPARE(spy->at(8).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(8).scriptId, qint64(-1)); |
| QVERIFY(spy->at(8).value.isFunction()); |
| |
| // Number constructor entry |
| QCOMPARE(spy->at(9).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(9).scriptId, qint64(-1)); |
| |
| // Number constructor exit |
| QCOMPARE(spy->at(10).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(10).scriptId, qint64(-1)); |
| QVERIFY(spy->at(10).value.isNumber()); |
| |
| // Object constructor entry |
| QCOMPARE(spy->at(11).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(11).scriptId, qint64(-1)); |
| |
| // Object constructor exit |
| QCOMPARE(spy->at(12).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(12).scriptId, qint64(-1)); |
| QVERIFY(spy->at(12).value.isObject()); |
| |
| // RegExp constructor entry |
| QCOMPARE(spy->at(13).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(13).scriptId, qint64(-1)); |
| |
| // RegExp constructor exit |
| QCOMPARE(spy->at(14).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(14).scriptId, qint64(-1)); |
| QVERIFY(spy->at(14).value.isRegExp()); |
| |
| // String constructor entry |
| QCOMPARE(spy->at(15).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(15).scriptId, qint64(-1)); |
| |
| // String constructor exit |
| QCOMPARE(spy->at(16).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(16).scriptId, qint64(-1)); |
| QVERIFY(spy->at(16).value.isString()); |
| |
| // evaluate() exit |
| QCOMPARE(spy->at(17).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(17).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(17).value.isString()); |
| QCOMPARE(spy->at(17).value.toString(), QString()); |
| } |
| delete spy; |
| } |
| |
| /** check behaiviour of slots*/ |
| void tst_QScriptEngineAgent::functionEntryAndExit_slots() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry |
| | ScriptEngineSpy::IgnoreFunctionExit)); |
| // slots |
| { |
| eng.globalObject().setProperty("qobj", eng.newQObject(this)); |
| spy->clear(); |
| eng.evaluate("qobj.testSlot(123)"); |
| QCOMPARE(spy->count(), 4); |
| // evaluate() entry |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); |
| // testSlot() entry |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(1).scriptId, qint64(-1)); |
| // testSlot() exit |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(2).scriptId, qint64(-1)); |
| QVERIFY(spy->at(2).value.isNumber()); |
| QCOMPARE(spy->at(2).value.toNumber(), qsreal(123)); |
| // evaluate() exit |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit); |
| } |
| delete spy; |
| } |
| |
| /** check behaiviour of property accessors*/ |
| void tst_QScriptEngineAgent::functionEntryAndExit_property_set() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry |
| | ScriptEngineSpy::IgnoreFunctionExit)); |
| // property accessors |
| { |
| eng.globalObject().setProperty("qobj", eng.newQObject(this)); |
| // set |
| spy->clear(); |
| eng.evaluate("qobj.testProperty = 456"); |
| QCOMPARE(spy->count(), 4); |
| // evaluate() entry |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); |
| // setTestProperty() entry |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(1).scriptId, qint64(-1)); |
| // setTestProperty() exit |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(2).scriptId, qint64(-1)); |
| QVERIFY(spy->at(2).value.isNumber()); |
| QCOMPARE(spy->at(2).value.toNumber(), testProperty()); |
| // evaluate() exit |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit); |
| QVERIFY(spy->at(3).value.strictlyEquals(spy->at(2).value)); |
| } |
| delete spy; |
| } |
| |
| /** check behaiviour of property accessors*/ |
| void tst_QScriptEngineAgent::functionEntryAndExit_property_get() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry |
| | ScriptEngineSpy::IgnoreFunctionExit)); |
| // property accessors |
| { |
| eng.globalObject().setProperty("qobj", eng.newQObject(this)); |
| // set |
| eng.evaluate("qobj.testProperty = 456"); |
| // get |
| spy->clear(); |
| eng.evaluate("qobj.testProperty"); |
| QCOMPARE(spy->count(), 4); |
| // evaluate() entry |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); |
| // testProperty() entry |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(1).scriptId, qint64(-1)); |
| // testProperty() exit |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(2).scriptId, qint64(-1)); |
| QVERIFY(spy->at(2).value.isNumber()); |
| QCOMPARE(spy->at(2).value.toNumber(), testProperty()); |
| // evaluate() exit |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit); |
| QVERIFY(spy->at(3).value.strictlyEquals(spy->at(2).value)); |
| } |
| delete spy; |
| } |
| |
| |
| /** check behaiviour of calling script functions from c++*/ |
| void tst_QScriptEngineAgent::functionEntryAndExit_call() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry |
| | ScriptEngineSpy::IgnoreFunctionExit)); |
| // calling script functions from C++ |
| |
| { |
| QScriptValue fun = eng.evaluate("function foo() { return 123; }; foo"); |
| QVERIFY(fun.isFunction()); |
| |
| spy->clear(); |
| fun.call(); |
| QCOMPARE(spy->count(), 2); |
| |
| // entry |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); |
| QVERIFY(spy->at(0).scriptId != -1); |
| |
| // exit |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(1).value.isNumber()); |
| QCOMPARE(spy->at(1).value.toNumber(), qsreal(123)); |
| } |
| delete spy; |
| } |
| |
| /** check behaiviour of native function returnning arg*/ |
| void tst_QScriptEngineAgent::functionEntryAndExit_functionReturn_call() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry |
| | ScriptEngineSpy::IgnoreFunctionExit)); |
| { |
| QScriptValue fun = eng.newFunction(nativeFunctionReturningArg); |
| |
| spy->clear(); |
| QScriptValueList args; |
| args << QScriptValue(&eng, 123); |
| fun.call(QScriptValue(), args); |
| QCOMPARE(spy->count(), 2); |
| |
| // entry |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); |
| QVERIFY(spy->at(0).scriptId == -1); |
| |
| // exit |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(1).value.strictlyEquals(args.at(0))); |
| } |
| delete spy; |
| } |
| |
| void tst_QScriptEngineAgent::functionEntryAndExit_functionReturn_construct() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry |
| | ScriptEngineSpy::IgnoreFunctionExit)); |
| { |
| QScriptValue fun = eng.newFunction(nativeFunctionReturningArg); |
| |
| spy->clear(); |
| QScriptValueList args; |
| args << QScriptValue(&eng, 123); |
| QScriptValue obj = fun.construct(args); |
| |
| QVERIFY(args.at(0).isValid()); |
| QVERIFY(args.at(0).isNumber()); |
| QVERIFY(args.at(0).toNumber() == 123); |
| |
| QCOMPARE(spy->count(), 2); |
| |
| // entry |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); |
| QVERIFY(spy->at(0).scriptId == -1); |
| |
| // exit |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| |
| QVERIFY(spy->at(1).value.strictlyEquals(args.at(0))); |
| } |
| |
| delete spy; |
| } |
| |
| /** check behaiviour of object creation with args (?)*/ |
| void tst_QScriptEngineAgent::functionEntryAndExit_objectCall() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry |
| | ScriptEngineSpy::IgnoreFunctionExit)); |
| for (int x = 0; x < 2; ++x) { |
| QScriptValue fun = eng.evaluate("Boolean"); |
| |
| QVERIFY(!fun.isError()); |
| |
| spy->clear(); |
| QScriptValueList args; |
| args << QScriptValue(&eng, true); |
| if (x) |
| fun.construct(args); |
| else |
| fun.call(QScriptValue(), args); |
| QCOMPARE(spy->count(), 2); |
| |
| // entry |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); |
| QVERIFY(spy->at(0).scriptId == -1); |
| |
| // exit |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(1).value.equals(args.at(0))); |
| } |
| delete spy; |
| } |
| |
| void tst_QScriptEngineAgent::positionChange_1() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnorePositionChange)); |
| { |
| spy->clear(); |
| eng.evaluate(";"); |
| QEXPECT_FAIL("","QTBUG-6142 JSC do not evaluate ';' to statemant",Continue); |
| QCOMPARE(spy->count(), 1); |
| if (spy->count()) { |
| QEXPECT_FAIL("","QTBUG-6142 JSC do not evaluate ';' to statemant",Continue); |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QEXPECT_FAIL("","QTBUG-6142 JSC do not evaluate ';' to statemant",Continue); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QEXPECT_FAIL("","QTBUG-6142 JSC do not evaluate ';' to statemant",Continue); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| QEXPECT_FAIL("","QTBUG-6142 JSC do not evaluate ';' to statemant",Continue); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| } |
| } |
| |
| { |
| spy->clear(); |
| int lineNumber = 123; |
| eng.evaluate("1 + 2", "foo.qs", lineNumber); |
| QCOMPARE(spy->count(), 1); |
| |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, lineNumber); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| } |
| |
| { |
| spy->clear(); |
| int lineNumber = 123; |
| eng.evaluate("var i = 0", "foo.qs", lineNumber); |
| QCOMPARE(spy->count(), 1); |
| |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, lineNumber); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| } |
| |
| QStringList lineTerminators; |
| lineTerminators << "\n" << "\r" << "\n\r" << "\r\n"; |
| for (int i = 0; i < lineTerminators.size(); ++i) { |
| spy->clear(); |
| int lineNumber = 456; |
| QString code = "1 + 2; 3 + 4;"; |
| code.append(lineTerminators.at(i)); |
| code.append("5 + 6"); |
| eng.evaluate(code, "foo.qs", lineNumber); |
| QCOMPARE(spy->count(), 3); |
| |
| // 1 + 2 |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, lineNumber); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| |
| // 3 + 4 |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(1).lineNumber, lineNumber); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(1).columnNumber, 8); |
| |
| // 5 + 6 |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(2).lineNumber, lineNumber + 1); |
| QCOMPARE(spy->at(2).columnNumber, 1); |
| } |
| delete spy; |
| } |
| |
| void tst_QScriptEngineAgent::positionChange_2() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnorePositionChange)); |
| { |
| spy->clear(); |
| int lineNumber = 789; |
| eng.evaluate("function foo() { return 123; }", "foo.qs", lineNumber); |
| QCOMPARE(spy->count(), 0); |
| |
| eng.evaluate("foo()"); |
| QCOMPARE(spy->count(), 2); |
| |
| // foo() |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| |
| // return 123 |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(1).scriptId != spy->at(0).scriptId); |
| QCOMPARE(spy->at(1).lineNumber, lineNumber); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(1).columnNumber, 18); |
| } |
| |
| { |
| spy->clear(); |
| eng.evaluate("if (true) i = 1; else i = 0;"); |
| QCOMPARE(spy->count(), 2); |
| |
| // if |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| |
| // i = 1 |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(1).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(1).columnNumber, 11); |
| } |
| |
| { |
| spy->clear(); |
| eng.evaluate("for (var i = 0; i < 2; ++i) { }"); |
| QCOMPARE(spy->count(), 1); |
| |
| // for |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| } |
| |
| { |
| spy->clear(); |
| eng.evaluate("for (var i = 0; i < 2; ++i) { void(i); }"); |
| QCOMPARE(spy->count(), 3); |
| |
| // for |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| |
| // void(i) |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(1).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(1).columnNumber, 31); |
| |
| // void(i) |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(2).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(2).columnNumber, 31); |
| } |
| |
| { |
| spy->clear(); |
| eng.evaluate("var i = 0; while (i < 2) { ++i; }"); |
| QCOMPARE(spy->count(), 4); |
| |
| // i = 0 |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| |
| // while |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(1).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(1).columnNumber, 12); |
| |
| // ++i |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(2).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(2).columnNumber, 28); |
| |
| // ++i |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(3).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(3).columnNumber, 28); |
| } |
| |
| { |
| spy->clear(); |
| eng.evaluate("var i = 0; do { ++i; } while (i < 2)"); |
| QCOMPARE(spy->count(), 5); |
| |
| // i = 0 |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| |
| // do |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(1).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(1).columnNumber, 12); |
| |
| // ++i |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(2).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(2).columnNumber, 17); |
| |
| // do |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(3).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(3).columnNumber, 12); |
| |
| // ++i |
| QCOMPARE(spy->at(4).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(4).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(4).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(4).columnNumber, 17); |
| } |
| |
| { |
| spy->clear(); |
| eng.evaluate("for (var i in { }) { void(i); }"); |
| QCOMPARE(spy->count(), 1); |
| |
| // for |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| } |
| |
| { |
| spy->clear(); |
| eng.evaluate("for ( ; ; ) { break; }"); |
| QCOMPARE(spy->count(), 2); |
| |
| // for |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| |
| // break |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(1).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(1).columnNumber, 15); |
| } |
| |
| { |
| spy->clear(); |
| eng.evaluate("for (var i = 0 ; i < 2; ++i) { continue; }"); |
| QCOMPARE(spy->count(), 3); |
| |
| // for |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| |
| // continue |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(1).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(1).columnNumber, 32); |
| |
| // continue |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(2).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(2).columnNumber, 32); |
| } |
| |
| { |
| spy->clear(); |
| eng.evaluate("with (this) { }"); |
| QCOMPARE(spy->count(), 1); |
| |
| // with |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| } |
| |
| { |
| spy->clear(); |
| eng.evaluate("switch (undefined) { }"); |
| QCOMPARE(spy->count(), 1); |
| |
| // switch |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| } |
| |
| { |
| spy->clear(); |
| eng.evaluate("switch (undefined) { default: i = 5; }"); |
| QCOMPARE(spy->count(), 2); |
| |
| // switch |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| |
| // i = 5 |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(1).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(1).columnNumber, 31); |
| } |
| |
| { |
| spy->clear(); |
| eng.evaluate("switch (undefined) { case undefined: i = 5; break; }"); |
| QCOMPARE(spy->count(), 3); |
| |
| // switch |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| |
| // i = 5 |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(1).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(1).columnNumber, 38); |
| |
| // break |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(2).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(2).columnNumber, 45); |
| } |
| |
| { |
| spy->clear(); |
| eng.evaluate("throw 1"); |
| QCOMPARE(spy->count(), 1); |
| |
| // throw |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| } |
| |
| { |
| spy->clear(); |
| eng.evaluate("try { throw 1; } catch(e) { i = e; } finally { i = 2; }"); |
| QCOMPARE(spy->count(), 3); |
| |
| // throw 1 |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(0).columnNumber, 7); |
| |
| // i = e |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(1).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(1).columnNumber, 29); |
| |
| // i = 2 |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(2).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(2).columnNumber, 48); |
| } |
| |
| { |
| spy->clear(); |
| eng.evaluate("try { i = 1; } catch(e) { i = 2; } finally { i = 3; }"); |
| QCOMPARE(spy->count(), 2); |
| |
| // i = 1 |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(0).columnNumber, 7); |
| |
| // i = 3 |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(1).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(1).columnNumber, 46); |
| } |
| |
| { |
| QEXPECT_FAIL("","QTBUG-6142 I believe the test is wrong. Expressions shouldn't call positionChange " |
| "because statement '1+2' will call it at least twice, why debugger have to " |
| "stop here so many times?", Abort); |
| spy->clear(); |
| eng.evaluate("c = {a: 10, b: 20}"); |
| QCOMPARE(spy->count(), 2); |
| |
| // a: 10 |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| QCOMPARE(spy->at(0).columnNumber, 1); |
| |
| // b: 20 |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(1).lineNumber, 1); |
| QEXPECT_FAIL("", "QTBUG-17609: With JSC-based back-end, column number is always reported as 1", Continue); |
| QCOMPARE(spy->at(1).columnNumber, 20); |
| } |
| delete spy; |
| } |
| |
| void tst_QScriptEngineAgent::positionChange_3() |
| { |
| QScriptEngine eng; |
| eng.evaluate("function some_function1(a) {\n a++; \n return a + 12; } \n some_function1(42);", "function1.qs", 12); |
| QScriptValue some_function2 = eng.evaluate("(function (b) {\n b--; \n return b + 11; })", "function2.qs", 21); |
| some_function2.call(QScriptValue(), QScriptValueList() << 2 ); |
| |
| // Test that the agent work, even if installed after the function has been evaluated. |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnorePositionChange)); |
| { |
| spy->clear(); |
| QScriptValue v = eng.evaluate("some_function1(15)"); |
| QCOMPARE(v.toInt32(), (15+1+12)); |
| QCOMPARE(spy->count(), 3); |
| |
| // some_function1() |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 1); |
| |
| // a++ |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(1).scriptId != spy->at(0).scriptId); |
| QCOMPARE(spy->at(1).lineNumber, 13); |
| // return a + 12 |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(2).scriptId == spy->at(1).scriptId); |
| QCOMPARE(spy->at(2).lineNumber, 14); |
| } |
| |
| { |
| spy->clear(); |
| QScriptValue v = some_function2.call(QScriptValue(), QScriptValueList() << 89 ); |
| QCOMPARE(v.toInt32(), (89-1+11)); |
| QCOMPARE(spy->count(), 2); |
| |
| // b-- |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QCOMPARE(spy->at(0).lineNumber, 22); |
| // return b + 11 |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(1).scriptId == spy->at(0).scriptId); |
| QCOMPARE(spy->at(1).lineNumber, 23); |
| } |
| |
| QVERIFY(!eng.hasUncaughtException()); |
| } |
| |
| |
| void tst_QScriptEngineAgent::exceptionThrowAndCatch() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreExceptionThrow |
| | ScriptEngineSpy::IgnoreExceptionCatch)); |
| { |
| spy->clear(); |
| eng.evaluate(";"); |
| QCOMPARE(spy->count(), 0); |
| } |
| |
| { |
| spy->clear(); |
| eng.evaluate("try { i = 5; } catch (e) { }"); |
| QCOMPARE(spy->count(), 0); |
| } |
| |
| { |
| spy->clear(); |
| eng.evaluate("throw new Error('ciao');"); |
| QCOMPARE(spy->count(), 1); |
| |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ExceptionThrow); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QVERIFY(!spy->at(0).hasExceptionHandler); |
| QVERIFY(spy->at(0).value.isError()); |
| QCOMPARE(spy->at(0).value.toString(), QString("Error: ciao")); |
| } |
| |
| { |
| spy->clear(); |
| eng.evaluate("try { throw new Error('ciao'); } catch (e) { }"); |
| QCOMPARE(spy->count(), 2); |
| |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ExceptionThrow); |
| QVERIFY(spy->at(0).scriptId != -1); |
| QVERIFY(spy->at(0).hasExceptionHandler); |
| QVERIFY(spy->at(0).value.isError()); |
| QCOMPARE(spy->at(0).value.toString(), QString("Error: ciao")); |
| QVERIFY(spy->at(0).scriptId != -1); |
| |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::ExceptionCatch); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(1).value.strictlyEquals(spy->at(0).value)); |
| } |
| } |
| |
| void tst_QScriptEngineAgent::eventOrder_assigment() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng); |
| { |
| spy->clear(); |
| eng.evaluate("i = 3; i = 5"); |
| QCOMPARE(spy->count(), 6); |
| // load |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad); |
| // evaluate() entry |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| // i = 3 |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId); |
| // i = 5 |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId); |
| // evaluate() exit |
| QCOMPARE(spy->at(4).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(4).scriptId, spy->at(0).scriptId); |
| // unload |
| QCOMPARE(spy->at(5).type, ScriptEngineEvent::ScriptUnload); |
| QCOMPARE(spy->at(5).scriptId, spy->at(0).scriptId); |
| } |
| delete spy; |
| } |
| |
| void tst_QScriptEngineAgent::eventOrder_functionDefinition() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng); |
| { |
| spy->clear(); |
| eng.evaluate("function foo(arg) { void(arg); }"); |
| QCOMPARE(spy->count(), 3); |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad); |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit); |
| |
| eng.evaluate("foo(123)"); |
| QCOMPARE(spy->count(), 13); |
| // load |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::ScriptLoad); |
| QVERIFY(spy->at(3).scriptId != spy->at(0).scriptId); |
| // evaluate() entry |
| QCOMPARE(spy->at(4).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(4).scriptId, spy->at(3).scriptId); |
| // foo() |
| QCOMPARE(spy->at(5).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(5).scriptId, spy->at(3).scriptId); |
| // new context |
| QCOMPARE(spy->at(6).type, ScriptEngineEvent::ContextPush); |
| // foo() entry |
| QCOMPARE(spy->at(7).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(7).scriptId, spy->at(0).scriptId); |
| // void(arg) |
| QCOMPARE(spy->at(8).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(8).scriptId, spy->at(0).scriptId); |
| // foo() exit |
| QCOMPARE(spy->at(9).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(9).scriptId, spy->at(0).scriptId); |
| // restore context |
| QCOMPARE(spy->at(10).type, ScriptEngineEvent::ContextPop); |
| // evaluate() exit |
| QCOMPARE(spy->at(11).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(11).scriptId, spy->at(3).scriptId); |
| // unload |
| QCOMPARE(spy->at(12).type, ScriptEngineEvent::ScriptUnload); |
| QCOMPARE(spy->at(12).scriptId, spy->at(3).scriptId); |
| |
| eng.evaluate("foo = null"); |
| eng.collectGarbage(); |
| } |
| delete spy; |
| } |
| |
| void tst_QScriptEngineAgent::eventOrder_throwError() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng); |
| { |
| spy->clear(); |
| eng.evaluate("throw new Error('ciao')"); |
| QCOMPARE(spy->count(), 10); |
| // load |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad); |
| // evaluate() entry |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry); |
| // throw |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange); |
| // new context |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::ContextPush); |
| // Error constructor entry |
| QCOMPARE(spy->at(4).type, ScriptEngineEvent::FunctionEntry); |
| // Error constructor exit |
| QCOMPARE(spy->at(5).type, ScriptEngineEvent::FunctionExit); |
| // restore context |
| QCOMPARE(spy->at(6).type, ScriptEngineEvent::ContextPop); |
| // exception |
| QCOMPARE(spy->at(7).type, ScriptEngineEvent::ExceptionThrow); |
| QVERIFY(!spy->at(7).hasExceptionHandler); |
| // evaluate() exit |
| QCOMPARE(spy->at(8).type, ScriptEngineEvent::FunctionExit); |
| // unload |
| QCOMPARE(spy->at(9).type, ScriptEngineEvent::ScriptUnload); |
| } |
| delete spy; |
| } |
| |
| void tst_QScriptEngineAgent::eventOrder_throwAndCatch() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng); |
| { |
| spy->clear(); |
| eng.evaluate("try { throw new Error('ciao') } catch (e) { void(e); }"); |
| QCOMPARE(spy->count(), 12); |
| // load |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad); |
| // evaluate() entry |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry); |
| // throw |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange); |
| // new context |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::ContextPush); |
| // Error constructor entry |
| QCOMPARE(spy->at(4).type, ScriptEngineEvent::FunctionEntry); |
| // Error constructor exit |
| QCOMPARE(spy->at(5).type, ScriptEngineEvent::FunctionExit); |
| // restore context |
| QCOMPARE(spy->at(6).type, ScriptEngineEvent::ContextPop); |
| // exception |
| QCOMPARE(spy->at(7).type, ScriptEngineEvent::ExceptionThrow); |
| QVERIFY(spy->at(7).value.isError()); |
| QVERIFY(spy->at(7).hasExceptionHandler); |
| // catch |
| QCOMPARE(spy->at(8).type, ScriptEngineEvent::ExceptionCatch); |
| QVERIFY(spy->at(8).value.isError()); |
| // void(e) |
| QCOMPARE(spy->at(9).type, ScriptEngineEvent::PositionChange); |
| // evaluate() exit |
| QCOMPARE(spy->at(10).type, ScriptEngineEvent::FunctionExit); |
| QVERIFY(spy->at(10).value.isUndefined()); |
| // unload |
| QCOMPARE(spy->at(11).type, ScriptEngineEvent::ScriptUnload); |
| } |
| delete spy; |
| } |
| |
| void tst_QScriptEngineAgent::eventOrder_functions() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng); |
| { |
| spy->clear(); |
| eng.evaluate("function foo(arg) { return bar(arg); }"); |
| eng.evaluate("function bar(arg) { return arg; }"); |
| QCOMPARE(spy->count(), 6); |
| |
| eng.evaluate("foo(123)"); |
| QCOMPARE(spy->count(), 21); |
| |
| // load |
| QCOMPARE(spy->at(6).type, ScriptEngineEvent::ScriptLoad); |
| // evaluate() entry |
| QCOMPARE(spy->at(7).type, ScriptEngineEvent::FunctionEntry); |
| // foo(123) |
| QCOMPARE(spy->at(8).type, ScriptEngineEvent::PositionChange); |
| // new context |
| QCOMPARE(spy->at(9).type, ScriptEngineEvent::ContextPush); |
| // foo() entry |
| QCOMPARE(spy->at(10).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(10).scriptId, spy->at(0).scriptId); |
| // return bar(arg) |
| QCOMPARE(spy->at(11).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(11).scriptId, spy->at(0).scriptId); |
| // new context |
| QCOMPARE(spy->at(12).type, ScriptEngineEvent::ContextPush); |
| // bar() entry |
| QCOMPARE(spy->at(13).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(13).scriptId, spy->at(3).scriptId); |
| // return arg |
| QCOMPARE(spy->at(14).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(14).scriptId, spy->at(3).scriptId); |
| // bar() exit |
| QCOMPARE(spy->at(15).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(15).scriptId, spy->at(3).scriptId); |
| QVERIFY(spy->at(15).value.isNumber()); |
| // restore context |
| QCOMPARE(spy->at(16).type, ScriptEngineEvent::ContextPop); |
| // foo() exit |
| QCOMPARE(spy->at(17).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(17).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(17).value.isNumber()); |
| // restore context |
| QCOMPARE(spy->at(18).type, ScriptEngineEvent::ContextPop); |
| // evaluate() exit |
| QCOMPARE(spy->at(19).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(19).scriptId, spy->at(6).scriptId); |
| QVERIFY(spy->at(19).value.isNumber()); |
| // unload |
| QCOMPARE(spy->at(20).type, ScriptEngineEvent::ScriptUnload); |
| QCOMPARE(spy->at(20).scriptId, spy->at(6).scriptId); |
| |
| // redefine bar() |
| eng.evaluate("function bar(arg) { throw new Error(arg); }"); |
| eng.collectGarbage(); |
| QCOMPARE(spy->count(), 25); |
| QCOMPARE(spy->at(21).type, ScriptEngineEvent::ScriptLoad); |
| QCOMPARE(spy->at(22).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(23).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(24).type, ScriptEngineEvent::ScriptUnload); |
| QCOMPARE(spy->at(24).scriptId, spy->at(3).scriptId); |
| |
| eng.evaluate("foo('ciao')"); |
| |
| QCOMPARE(spy->count(), 45); |
| |
| // load |
| QCOMPARE(spy->at(25).type, ScriptEngineEvent::ScriptLoad); |
| // evaluate() entry |
| QCOMPARE(spy->at(26).type, ScriptEngineEvent::FunctionEntry); |
| // foo('ciao') |
| QCOMPARE(spy->at(27).type, ScriptEngineEvent::PositionChange); |
| // new context |
| QCOMPARE(spy->at(28).type, ScriptEngineEvent::ContextPush); |
| // foo() entry |
| QCOMPARE(spy->at(29).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(29).scriptId, spy->at(0).scriptId); |
| // return bar(arg) |
| QCOMPARE(spy->at(30).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(30).scriptId, spy->at(0).scriptId); |
| // new context |
| QCOMPARE(spy->at(31).type, ScriptEngineEvent::ContextPush); |
| // bar() entry |
| QCOMPARE(spy->at(32).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(32).scriptId, spy->at(21).scriptId); |
| // throw |
| QCOMPARE(spy->at(33).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(33).scriptId, spy->at(21).scriptId); |
| // new context |
| QCOMPARE(spy->at(34).type, ScriptEngineEvent::ContextPush); |
| // Error constructor entry |
| QCOMPARE(spy->at(35).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(35).scriptId, qint64(-1)); |
| // Error constructor exit |
| QCOMPARE(spy->at(36).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(36).scriptId, qint64(-1)); |
| // restore context |
| QCOMPARE(spy->at(37).type, ScriptEngineEvent::ContextPop); |
| // exception |
| QCOMPARE(spy->at(38).type, ScriptEngineEvent::ExceptionThrow); |
| QCOMPARE(spy->at(38).scriptId, spy->at(21).scriptId); |
| QVERIFY(!spy->at(38).hasExceptionHandler); |
| // bar() exit |
| QCOMPARE(spy->at(39).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(39).scriptId, spy->at(21).scriptId); |
| QVERIFY(spy->at(39).value.isError()); |
| // restore context |
| QCOMPARE(spy->at(40).type, ScriptEngineEvent::ContextPop); |
| // foo() exit |
| QCOMPARE(spy->at(41).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(41).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(41).value.isError()); |
| // restore context |
| QCOMPARE(spy->at(42).type, ScriptEngineEvent::ContextPop); |
| // evaluate() exit |
| QCOMPARE(spy->at(43).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(43).scriptId, spy->at(26).scriptId); |
| QVERIFY(spy->at(43).value.isError()); |
| // unload |
| QCOMPARE(spy->at(44).type, ScriptEngineEvent::ScriptUnload); |
| QCOMPARE(spy->at(44).scriptId, spy->at(25).scriptId); |
| } |
| delete spy; |
| } |
| |
| void tst_QScriptEngineAgent::eventOrder_throwCatchFinally() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng); |
| { |
| spy->clear(); |
| eng.evaluate("try { throw 1; } catch(e) { i = e; } finally { i = 2; }"); |
| QCOMPARE(spy->count(), 9); |
| |
| // load |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad); |
| // evaluate() entry |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry); |
| // throw 1 |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange); |
| // i = e |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::ExceptionThrow); |
| // catch |
| QCOMPARE(spy->at(4).type, ScriptEngineEvent::ExceptionCatch); |
| // i = e |
| QCOMPARE(spy->at(5).type, ScriptEngineEvent::PositionChange); |
| // i = 2 |
| QCOMPARE(spy->at(6).type, ScriptEngineEvent::PositionChange); |
| // evaluate() exit |
| QCOMPARE(spy->at(7).type, ScriptEngineEvent::FunctionExit); |
| // unload |
| QCOMPARE(spy->at(8).type, ScriptEngineEvent::ScriptUnload); |
| } |
| delete spy; |
| } |
| |
| void tst_QScriptEngineAgent::eventOrder_signalsHandling() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng); |
| // signal handling |
| { |
| spy->clear(); |
| QScriptValue fun = eng.evaluate("(function(arg) { throw Error(arg); })"); |
| QVERIFY(fun.isFunction()); |
| QCOMPARE(spy->count(), 4); |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad); |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit); |
| |
| qScriptConnect(this, SIGNAL(testSignal(double)), |
| QScriptValue(), fun); |
| |
| emit testSignal(123); |
| |
| QCOMPARE(spy->count(), 14); |
| // new context |
| QCOMPARE(spy->at(4).type, ScriptEngineEvent::ContextPush); |
| // anonymous function entry |
| QCOMPARE(spy->at(5).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(5).scriptId, spy->at(0).scriptId); |
| // throw statement |
| QCOMPARE(spy->at(6).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(6).scriptId, spy->at(0).scriptId); |
| // new context |
| QCOMPARE(spy->at(7).type, ScriptEngineEvent::ContextPush); |
| // Error constructor entry |
| QCOMPARE(spy->at(8).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(8).scriptId, qint64(-1)); |
| // Error constructor exit |
| QCOMPARE(spy->at(9).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(9).scriptId, qint64(-1)); |
| // restore context |
| QCOMPARE(spy->at(10).type, ScriptEngineEvent::ContextPop); |
| // exception |
| QCOMPARE(spy->at(11).type, ScriptEngineEvent::ExceptionThrow); |
| QCOMPARE(spy->at(11).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(11).value.isError()); |
| QVERIFY(!spy->at(11).hasExceptionHandler); |
| // anonymous function exit |
| QCOMPARE(spy->at(12).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(12).scriptId, spy->at(0).scriptId); |
| QVERIFY(spy->at(12).value.isError()); |
| // restore context |
| QCOMPARE(spy->at(13).type, ScriptEngineEvent::ContextPop); |
| } |
| delete spy; |
| } |
| |
| class DoubleAgent : public ScriptEngineSpy |
| { |
| public: |
| DoubleAgent(QScriptEngine *engine) : ScriptEngineSpy(engine) { } |
| ~DoubleAgent() { } |
| |
| void positionChange(qint64 scriptId, int lineNumber, int columnNumber) |
| { |
| if (lineNumber == 123) |
| engine()->evaluate("1 + 2"); |
| ScriptEngineSpy::positionChange(scriptId, lineNumber, columnNumber); |
| } |
| }; |
| |
| void tst_QScriptEngineAgent::recursiveObserve() |
| { |
| QScriptEngine eng; |
| DoubleAgent *spy = new DoubleAgent(&eng); |
| |
| eng.evaluate("3 + 4", "foo.qs", 123); |
| |
| QCOMPARE(spy->count(), 10); |
| |
| int i = 0; |
| // load "3 + 4" |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::ScriptLoad); |
| i++; |
| // evaluate() entry |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::FunctionEntry); |
| i++; |
| // load "1 + 2" |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::ScriptLoad); |
| i++; |
| // evaluate() entry |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::FunctionEntry); |
| i++; |
| // 1 + 2 |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(i).scriptId, spy->at(2).scriptId); |
| i++; |
| // evaluate() exit |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::FunctionExit); |
| i++; |
| // unload "1 + 2" |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::ScriptUnload); |
| QCOMPARE(spy->at(i).scriptId, spy->at(2).scriptId); |
| i++; |
| // 3 + 4 |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(i).scriptId, spy->at(0).scriptId); |
| i++; |
| // evaluate() exit |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::FunctionExit); |
| i++; |
| // unload "3 + 4" |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::ScriptUnload); |
| QCOMPARE(spy->at(i).scriptId, spy->at(0).scriptId); |
| } |
| |
| |
| /** When second agent is attached to Engine the first one should be deatached */ |
| void tst_QScriptEngineAgent::multipleAgents() |
| { |
| QScriptEngine eng; |
| QCOMPARE(eng.agent(), (QScriptEngineAgent *)0); |
| ScriptEngineSpy *spy1 = new ScriptEngineSpy(&eng); |
| QCOMPARE(eng.agent(), (QScriptEngineAgent*)spy1); |
| ScriptEngineSpy *spy2 = new ScriptEngineSpy(&eng); |
| QCOMPARE(eng.agent(), (QScriptEngineAgent*)spy2); |
| |
| eng.evaluate("1 + 2"); |
| QCOMPARE(spy1->count(), 0); |
| QCOMPARE(spy2->count(), 5); |
| |
| spy2->clear(); |
| eng.setAgent(spy1); |
| eng.evaluate("1 + 2"); |
| QCOMPARE(spy2->count(), 0); |
| QCOMPARE(spy1->count(), 5); |
| } |
| |
| void tst_QScriptEngineAgent::syntaxError() |
| { |
| /* This test was changed. Old backend didn't generate events in exception objects creation time |
| JSC does */ |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng); |
| { |
| int i = 0; |
| spy->clear(); |
| eng.evaluate("{"); |
| |
| QCOMPARE(spy->count(), 9); |
| |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::ScriptLoad); |
| QVERIFY(spy->at(i).scriptId != -1); |
| i = 1; |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::FunctionEntry); |
| QCOMPARE(spy->at(i).scriptId, spy->at(0).scriptId); |
| |
| //create exception |
| |
| i = 2; |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::ContextPush); |
| i = 3; |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::FunctionEntry); |
| QVERIFY(spy->at(i).scriptId == -1); |
| i = 4; |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::FunctionExit); |
| QVERIFY(spy->at(i).scriptId == -1); |
| i = 5; |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::ContextPop); |
| i = 6; |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::ExceptionThrow); |
| QCOMPARE(spy->at(i).scriptId, spy->at(0).scriptId); |
| QVERIFY(!spy->at(i).hasExceptionHandler); |
| QVERIFY(spy->at(i).value.isError()); |
| QVERIFY(spy->at(i).value.toString().contains(QLatin1String("SyntaxError"))); |
| QCOMPARE(spy->at(i).scriptId, spy->at(0).scriptId); |
| i = 7; |
| //exit script |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::FunctionExit); |
| QCOMPARE(spy->at(i).scriptId, spy->at(0).scriptId); |
| i = 8; |
| QCOMPARE(spy->at(i).type, ScriptEngineEvent::ScriptUnload); |
| QCOMPARE(spy->at(i).scriptId, spy->at(0).scriptId); |
| } |
| } |
| |
| void tst_QScriptEngineAgent::extension_invoctaion() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreDebuggerInvocationRequest |
| | ScriptEngineSpy::IgnoreScriptLoad)); |
| // DebuggerInvocationRequest |
| { |
| spy->clear(); |
| |
| QString fileName = "foo.qs"; |
| int lineNumber = 123; |
| QScriptValue ret = eng.evaluate("debugger", fileName, lineNumber); |
| |
| QCOMPARE(spy->count(), 2); |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad); |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::DebuggerInvocationRequest); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(1).lineNumber, lineNumber); |
| QCOMPARE(spy->at(1).columnNumber, 1); |
| |
| QEXPECT_FAIL("","QTBUG-6135 In JSC Eval('debugger') returns undefined",Abort); |
| QVERIFY(ret.isString()); |
| QCOMPARE(ret.toString(), QString::fromLatin1("extension(DebuggerInvocationRequest)")); |
| } |
| delete spy; |
| } |
| |
| void tst_QScriptEngineAgent::extension() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreDebuggerInvocationRequest |
| | ScriptEngineSpy::IgnoreScriptLoad)); |
| |
| { |
| spy->clear(); |
| spy->enableIgnoreFlags(ScriptEngineSpy::IgnoreDebuggerInvocationRequest); |
| |
| QScriptValue ret = eng.evaluate("debugger"); |
| |
| QCOMPARE(spy->count(), 1); |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad); |
| QVERIFY(ret.isUndefined()); |
| } |
| delete spy; |
| } |
| |
| class TestIsEvaluatingAgent : public QScriptEngineAgent |
| { |
| public: |
| TestIsEvaluatingAgent(QScriptEngine *engine) |
| : QScriptEngineAgent(engine), wasEvaluating(false) |
| { engine->setAgent(this); } |
| bool supportsExtension(Extension ext) const |
| { return ext == DebuggerInvocationRequest; } |
| QVariant extension(Extension, const QVariant &) |
| { wasEvaluating = engine()->isEvaluating(); return QVariant(); } |
| |
| bool wasEvaluating; |
| }; |
| |
| void tst_QScriptEngineAgent::isEvaluatingInExtension() |
| { |
| QScriptEngine eng; |
| TestIsEvaluatingAgent *spy = new TestIsEvaluatingAgent(&eng); |
| QVERIFY(!spy->wasEvaluating); |
| eng.evaluate("debugger"); |
| QVERIFY(spy->wasEvaluating); |
| } |
| |
| class NewSpy :public QScriptEngineAgent |
| { |
| bool m_result; |
| public: |
| NewSpy(QScriptEngine* eng) : QScriptEngineAgent(eng), m_result(false) {} |
| void functionExit (qint64, const QScriptValue & /* scriptValue */) |
| { |
| if (engine()->hasUncaughtException()) m_result = true; |
| } |
| |
| bool isPass() { return m_result; } |
| void reset() { m_result = false; } |
| }; |
| |
| void tst_QScriptEngineAgent::hasUncaughtException() |
| { |
| QScriptEngine eng; |
| NewSpy* spy = new NewSpy(&eng); |
| eng.setAgent(spy); |
| QScriptValue scriptValue; |
| |
| // Check unhandled exception. |
| eng.evaluate("function init () {Unknown.doSth ();}"); |
| scriptValue = QScriptValue(eng.globalObject().property("init")).call(); |
| QVERIFY(eng.hasUncaughtException()); |
| QVERIFY2(spy->isPass(), "At least one of a functionExit event should set hasUncaughtException flag."); |
| spy->reset(); |
| |
| // Check caught exception. |
| eng.evaluate("function innerFoo() { throw new Error('ciao') }"); |
| eng.evaluate("function foo() {try { innerFoo() } catch (e) {} }"); |
| scriptValue = QScriptValue(eng.globalObject().property("foo")).call(); |
| QVERIFY(!eng.hasUncaughtException()); |
| QVERIFY2(spy->isPass(), "At least one of a functionExit event should set hasUncaughtException flag."); |
| } |
| |
| void tst_QScriptEngineAgent::evaluateProgram() |
| { |
| QScriptEngine eng; |
| QScriptProgram program("1 + 2", "foo.js", 123); |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng); |
| qint64 scriptId = -1; |
| for (int x = 0; x < 10; ++x) { |
| spy->clear(); |
| (void)eng.evaluate(program); |
| QCOMPARE(spy->count(), (x == 0) ? 4 : 3); |
| |
| if (x == 0) { |
| // script is only loaded on first execution |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad); |
| scriptId = spy->at(0).scriptId; |
| QVERIFY(scriptId != -1); |
| QCOMPARE(spy->at(0).script, program.sourceCode()); |
| QCOMPARE(spy->at(0).fileName, program.fileName()); |
| QCOMPARE(spy->at(0).lineNumber, program.firstLineNumber()); |
| spy->removeFirst(); |
| } |
| |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); // evaluate() |
| QCOMPARE(spy->at(0).scriptId, scriptId); |
| |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange); |
| QCOMPARE(spy->at(1).scriptId, scriptId); |
| QCOMPARE(spy->at(1).lineNumber, program.firstLineNumber()); |
| |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit); // evaluate() |
| QCOMPARE(spy->at(2).scriptId, scriptId); |
| QVERIFY(spy->at(2).value.isNumber()); |
| QCOMPARE(spy->at(2).value.toNumber(), qsreal(3)); |
| } |
| } |
| |
| void tst_QScriptEngineAgent::evaluateProgram_SyntaxError() |
| { |
| QScriptEngine eng; |
| QScriptProgram program("this is not valid syntax", "foo.js", 123); |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng); |
| qint64 scriptId = -1; |
| for (int x = 0; x < 10; ++x) { |
| spy->clear(); |
| (void)eng.evaluate(program); |
| QCOMPARE(spy->count(), (x == 0) ? 8 : 7); |
| |
| if (x == 0) { |
| // script is only loaded on first execution |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad); |
| scriptId = spy->at(0).scriptId; |
| QVERIFY(scriptId != -1); |
| QCOMPARE(spy->at(0).script, program.sourceCode()); |
| QCOMPARE(spy->at(0).fileName, program.fileName()); |
| QCOMPARE(spy->at(0).lineNumber, program.firstLineNumber()); |
| spy->removeFirst(); |
| } |
| |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); // evaluate() |
| QCOMPARE(spy->at(0).scriptId, scriptId); |
| |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::ContextPush); // SyntaxError constructor |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionEntry); // SyntaxError constructor |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit); // SyntaxError constructor |
| QCOMPARE(spy->at(4).type, ScriptEngineEvent::ContextPop); // SyntaxError constructor |
| |
| QCOMPARE(spy->at(5).type, ScriptEngineEvent::ExceptionThrow); |
| QVERIFY(spy->at(5).value.isError()); |
| QCOMPARE(spy->at(5).value.toString(), QString::fromLatin1("SyntaxError: Parse error")); |
| |
| QCOMPARE(spy->at(6).type, ScriptEngineEvent::FunctionExit); // evaluate() |
| QCOMPARE(spy->at(6).scriptId, scriptId); |
| } |
| } |
| |
| void tst_QScriptEngineAgent::evaluateNullProgram() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng); |
| (void)eng.evaluate(QScriptProgram()); |
| QCOMPARE(spy->count(), 0); |
| } |
| |
| void tst_QScriptEngineAgent::QTBUG6108() |
| { |
| QScriptEngine eng; |
| ScriptEngineSpy *spy = new ScriptEngineSpy(&eng); |
| eng.evaluate("eval('a = 1')"); |
| QCOMPARE(spy->count(), 5); |
| |
| QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad); |
| QVERIFY(spy->at(0).scriptId != -1); |
| |
| QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry); |
| QVERIFY(spy->at(1).scriptId != -1); |
| QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId); |
| |
| QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange); |
| QVERIFY(spy->at(2).scriptId != -1); |
| QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId); |
| QCOMPARE(spy->at(2).lineNumber, 1); |
| |
| QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit); |
| QVERIFY(spy->at(3).scriptId != -1); |
| QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId); |
| |
| QCOMPARE(spy->at(4).type, ScriptEngineEvent::ScriptUnload); |
| QCOMPARE(spy->at(4).scriptId, spy->at(0).scriptId); |
| } |
| |
| class BacktraceSpy : public QScriptEngineAgent |
| { |
| public: |
| BacktraceSpy(QScriptEngine *engine, const QStringList &expectedbacktrace, int breakpoint) |
| : QScriptEngineAgent(engine), expectedbacktrace(expectedbacktrace), breakpoint(breakpoint), ok(false) {} |
| |
| QStringList expectedbacktrace; |
| int breakpoint; |
| bool ok; |
| |
| protected: |
| |
| void exceptionThrow(qint64 , const QScriptValue &, bool) |
| { check(); } |
| |
| void positionChange(qint64 , int lineNumber, int ) |
| { |
| if (lineNumber == breakpoint) |
| check(); |
| } |
| |
| private: |
| void check() |
| { |
| QCOMPARE(engine()->currentContext()->backtrace(), expectedbacktrace); |
| ok = true; |
| } |
| }; |
| |
| |
| void tst_QScriptEngineAgent::backtraces_data() |
| { |
| QTest::addColumn<QString>("code"); |
| QTest::addColumn<int>("breakpoint"); |
| QTest::addColumn<QStringList>("expectedbacktrace"); |
| |
| { |
| QString source( |
| "function foo() {\n" |
| " var a = 5\n" |
| "}\n" |
| "foo('hello', { })\n" |
| "var r = 0;"); |
| |
| QStringList expected; |
| expected |
| << "foo('hello', [object Object]) at filename.js:2" |
| << "<global>() at filename.js:4"; |
| QTest::newRow("simple breakpoint") << source << 2 << expected; |
| } |
| |
| { |
| QString source( |
| "function foo() {\n" |
| " error = err\n" //this must throw |
| "}\n" |
| "foo('hello', { })\n" |
| "var r = 0;"); |
| |
| QStringList expected; |
| expected |
| << "foo('hello', [object Object]) at filename.js:2" |
| << "<global>() at filename.js:4"; |
| QTest::newRow("throw because of error") << source << -100 << expected; |
| } |
| } |
| |
| void tst_QScriptEngineAgent::backtraces() |
| { |
| QFETCH(QString, code); |
| QFETCH(int, breakpoint); |
| QFETCH(QStringList, expectedbacktrace); |
| |
| QScriptEngine eng; |
| BacktraceSpy *spy = new BacktraceSpy(&eng, expectedbacktrace, breakpoint); |
| eng.setAgent(spy); |
| QLatin1String filename("filename.js"); |
| eng.evaluate(code, filename); |
| QVERIFY(spy->ok); |
| } |
| |
| QTEST_MAIN(tst_QScriptEngineAgent) |
| #include "tst_qscriptengineagent.moc" |