aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErik Verbruggen <erik.verbruggen@qt.io>2019-01-14 14:21:21 +0100
committerUlf Hermann <ulf.hermann@qt.io>2019-01-15 17:35:46 +0000
commit8fd3cfe7d0f39a731c585334299f5160ad952df9 (patch)
treeff57f39464d88c610103f6fd4aa1b4e1ec53be01
parent8396dc86f28e89a920d85f59518fac79da8480eb (diff)
Annotate stack traces when frames are elided through tail calls
Task-number: QTBUG-72407 Change-Id: I98b96852309fc783a945797185f666196513d24b Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r--src/qml/jsruntime/qv4engine.cpp5
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp1
-rw-r--r--src/qml/jsruntime/qv4stackframe_p.h2
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp16
4 files changed, 24 insertions, 0 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 57a364b205..f00578aa70 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -1011,6 +1011,11 @@ StackTrace ExecutionEngine::stackTrace(int frameLimit) const
frame.line = qAbs(f->lineNumber());
frame.column = -1;
stack.append(frame);
+ if (f->isTailCalling) {
+ QV4::StackFrame frame;
+ frame.function = QStringLiteral("[elided tail calls]");
+ stack.append(frame);
+ }
--frameLimit;
f = f->parent;
}
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index dfe9d35194..41a21ba379 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -518,6 +518,7 @@ ReturnedValue ArrowFunction::virtualCall(const FunctionObject *fo, const Value *
do {
frame.pendingTailCall = false;
result = Moth::VME::exec(&frame, engine);
+ frame.isTailCalling = true;
} while (frame.pendingTailCall);
frame.pop();
diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h
index a97ae0e7c9..44cfef9173 100644
--- a/src/qml/jsruntime/qv4stackframe_p.h
+++ b/src/qml/jsruntime/qv4stackframe_p.h
@@ -125,6 +125,7 @@ struct Q_QML_EXPORT CppStackFrame {
bool yieldIsIterator;
bool callerCanHandleTailCall;
bool pendingTailCall;
+ bool isTailCalling;
void init(EngineBase *engine, Function *v4Function, const Value *argv, int argc, bool callerCanHandleTailCall = false) {
this->engine = engine;
@@ -140,6 +141,7 @@ struct Q_QML_EXPORT CppStackFrame {
yieldIsIterator = false;
this->callerCanHandleTailCall = callerCanHandleTailCall;
pendingTailCall = false;
+ isTailCalling = false;
}
void push() {
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index 00c631141b..cf7b9c7224 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -1759,6 +1759,22 @@ void tst_QJSEngine::stacktrace()
QJSValue result2 = eng.evaluate(script2, fileName);
QVERIFY(!result2.isError());
QVERIFY(result2.isString());
+
+ {
+ QString script3 = QString::fromLatin1(
+ "'use strict'\n"
+ "function throwUp() { throw new Error('up') }\n"
+ "function indirectlyThrow() { return throwUp() }\n"
+ "indirectlyThrow()\n"
+ );
+ QJSValue result3 = eng.evaluate(script3);
+ QVERIFY(result3.isError());
+ QJSValue stack = result3.property("stack");
+ QVERIFY(stack.isString());
+ QString stackTrace = stack.toString();
+ QVERIFY(!stackTrace.contains(QStringLiteral("indirectlyThrow")));
+ QVERIFY(stackTrace.contains(QStringLiteral("elide")));
+ }
}
void tst_QJSEngine::numberParsing_data()