From 9429a85a2160cf8b860b1292e4e0bc0855841755 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Mon, 20 Aug 2012 12:40:36 +0200 Subject: Capture full backtrace when a native function calls back into JS Commit df0ec196031d33850324dc5eeed2d71f61413885 assumed that JSC's Interpreter::throwException function is called exactly once when an exception occurs. That's wrong. If there is a native call inbetween two JS calls on the stack, the call stack will be unwound to the point of the native call frame, and throwException will return. After the native (C) call frame has been unwound, throwException will be called again to unwind remaining JS call frames, and so on. This was causing QtScript to discard the backtrace belonging to the inner-most JS frames; the backtrace would be regenerated from a partially unwound state. Fix this by ignoring subsequent calls to the uncaughtException() callback once a backtrace has been captured; the backtrace is already cleared before each evaluation is started. Task-number: QTBUG-26889 Change-Id: I03e1d60fbac5e592cff1dd5ef70f397cf94454ae Reviewed-by: Simon Hausmann --- tests/auto/qscriptengine/tst_qscriptengine.cpp | 41 ++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'tests') 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("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("() at -1") + << QStringLiteral("() at test.js:4"); + QCOMPARE(eng.uncaughtExceptionBacktrace(), expectedBacktrace); +} + void tst_QScriptEngine::numberParsing_data() { QTest::addColumn("string"); -- cgit v1.2.1