diff options
-rw-r--r-- | src/script/api/qscriptengine.cpp | 12 | ||||
-rw-r--r-- | tests/auto/qscriptengine/tst_qscriptengine.cpp | 41 |
2 files changed, 51 insertions, 2 deletions
diff --git a/src/script/api/qscriptengine.cpp b/src/script/api/qscriptengine.cpp index d96d11c..8186dec 100644 --- a/src/script/api/qscriptengine.cpp +++ b/src/script/api/qscriptengine.cpp @@ -1434,12 +1434,20 @@ bool QScriptEnginePrivate::isLikelyStackOverflowError(JSC::ExecState *exec, JSC: /*! \internal - Called by the VM when an uncaught exception is detected. - At the time of this call, the VM stack has not yet been unwound. + Called by the VM when an uncaught exception is being processed. + If the VM call stack contains a native call inbetween two JS calls at the + time the exception is thrown, this function will get called multiple times + for a single exception (once per "interval" of JS call frames). In other + words, at the time of this call, the VM stack can be in a partially unwound + state. */ void QScriptEnginePrivate::uncaughtException(JSC::ExecState *exec, unsigned bytecodeOffset, JSC::JSValue value) { + // Don't capture exception information if we already have. + if (uncaughtExceptionLineNumber != -1) + return; + QScript::SaveFrameHelper saveFrame(this, exec); uncaughtExceptionLineNumber = exec->codeBlock()->lineNumberForBytecodeOffset(exec, bytecodeOffset); diff --git a/tests/auto/qscriptengine/tst_qscriptengine.cpp b/tests/auto/qscriptengine/tst_qscriptengine.cpp index 6d7540f..6d77b32 100644 --- a/tests/auto/qscriptengine/tst_qscriptengine.cpp +++ b/tests/auto/qscriptengine/tst_qscriptengine.cpp @@ -146,6 +146,8 @@ private slots: void throwErrorFromProcessEvents(); void disableProcessEventsInterval(); void stacktrace(); + void stacktrace_callJSFromCpp_data(); + void stacktrace_callJSFromCpp(); void numberParsing_data(); void numberParsing(); void automaticSemicolonInsertion(); @@ -3107,6 +3109,45 @@ void tst_QScriptEngine::stacktrace() QVERIFY(eng.uncaughtExceptionBacktrace().isEmpty()); } +void tst_QScriptEngine::stacktrace_callJSFromCpp_data() +{ + QTest::addColumn<QString>("callbackExpression"); + + QTest::newRow("explicit throw") << QString::fromLatin1("throw new Error('callback threw')"); + QTest::newRow("reference error") << QString::fromLatin1("noSuchFunction()"); +} + +// QTBUG-26889 +void tst_QScriptEngine::stacktrace_callJSFromCpp() +{ + struct CallbackCaller { + static QScriptValue call(QScriptContext *, QScriptEngine *eng) + { return eng->globalObject().property(QStringLiteral("callback")).call(); } + + }; + + QFETCH(QString, callbackExpression); + QString script = QString::fromLatin1( + "function callback() {\n" + " %0\n" + "}\n" + "callCallbackFromCpp()").arg(callbackExpression); + + QScriptEngine eng; + eng.globalObject().setProperty(QStringLiteral("callCallbackFromCpp"), + eng.newFunction(&CallbackCaller::call)); + eng.evaluate(script, QStringLiteral("test.js")); + + QVERIFY(eng.hasUncaughtException()); + QCOMPARE(eng.uncaughtExceptionLineNumber(), 2); + + QStringList expectedBacktrace; + expectedBacktrace << QStringLiteral("callback() at test.js:2") + << QStringLiteral("<native>() at -1") + << QStringLiteral("<global>() at test.js:4"); + QCOMPARE(eng.uncaughtExceptionBacktrace(), expectedBacktrace); +} + void tst_QScriptEngine::numberParsing_data() { QTest::addColumn<QString>("string"); |