summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2017-03-31 15:52:47 +0200
committerUlf Hermann <ulf.hermann@qt.io>2017-03-31 14:28:36 +0000
commitf261a534cc96d41cee15462795844116887bd99a (patch)
tree8ec2d6890694de267386758ed3be7c2861168dbe
parent0043fab778f2497f644c434a194b5c5eec851819 (diff)
V4: Set correct source locations for jumps out of conditionalsv5.9.0-beta1
We always want to place the jump on the last line of the conditionally executed statement, unless we might never execute the last line. In the latter case, that is if the inner statement is again a conditional, we use some token of the outer condition. This works fine with loops, as the loop condition is actually checked after each iteration, and it's plausible to the user that we jump there. With "if" statements, it's not so great. We cannot really explain why we jump back to the "if" token after executing the conditional statement. However, we have to add some source location to the jump instruction as otherwise it uses the source location of the last statement that had one, which is rather random. Task-number: QTBUG-59204 Task-number: QTBUG-59774 Change-Id: I48e331ce1c1830f236e16b75c9201a2f490d2092 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
-rw-r--r--src/qml/compiler/qv4codegen.cpp37
-rw-r--r--tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp68
2 files changed, 77 insertions, 28 deletions
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index a10c0730bf..fcfbdfa74b 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -92,6 +92,27 @@ static bool cjumpCanHandle(IR::AluOp op)
}
}
+static inline void setJumpOutLocation(IR::Stmt *s, const Statement *body,
+ const SourceLocation &fallback)
+{
+ switch (body->kind) {
+ // Statements where we might never execute the last line.
+ // Use the fallback.
+ case Statement::Kind_ConditionalExpression:
+ case Statement::Kind_ForEachStatement:
+ case Statement::Kind_ForStatement:
+ case Statement::Kind_IfStatement:
+ case Statement::Kind_LocalForEachStatement:
+ case Statement::Kind_LocalForStatement:
+ case Statement::Kind_WhileStatement:
+ setLocation(s, fallback);
+ break;
+ default:
+ setLocation(s, body->lastSourceLocation());
+ break;
+ }
+}
+
Codegen::ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode)
: _cg(cg)
, _sourceCode(sourceCode)
@@ -2256,7 +2277,7 @@ bool Codegen::visit(DoWhileStatement *ast)
_block = loopbody;
statement(ast->statement);
- setLocation(_block->JUMP(loopcond), ast->statement->lastSourceLocation());
+ setJumpOutLocation(_block->JUMP(loopcond), ast->statement, ast->semicolonToken);
_block = loopcond;
condition(ast->expression, loopbody, loopend);
@@ -2321,7 +2342,7 @@ bool Codegen::visit(ForEachStatement *ast)
return false;
move(*init, _block->TEMP(temp));
statement(ast->statement);
- setLocation(_block->JUMP(foreachin), ast->lastSourceLocation());
+ setJumpOutLocation(_block->JUMP(foreachin), ast->statement, ast->forToken);
_block = foreachin;
@@ -2360,7 +2381,7 @@ bool Codegen::visit(ForStatement *ast)
_block = forbody;
statement(ast->statement);
- setLocation(_block->JUMP(forstep), ast->lastSourceLocation());
+ setJumpOutLocation(_block->JUMP(forstep), ast->statement, ast->forToken);
_block = forstep;
statement(ast->expression);
@@ -2386,12 +2407,12 @@ bool Codegen::visit(IfStatement *ast)
_block = iftrue;
statement(ast->ok);
- _block->JUMP(endif);
+ setJumpOutLocation(_block->JUMP(endif), ast->ok, ast->ifToken);
if (ast->ko) {
_block = iffalse;
statement(ast->ko);
- _block->JUMP(endif);
+ setJumpOutLocation(_block->JUMP(endif), ast->ko, ast->elseToken);
}
_block = endif;
@@ -2460,7 +2481,7 @@ bool Codegen::visit(LocalForEachStatement *ast)
int temp = _block->newTemp();
move(identifier(ast->declaration->name.toString()), _block->TEMP(temp));
statement(ast->statement);
- setLocation(_block->JUMP(foreachin), ast->lastSourceLocation());
+ setJumpOutLocation(_block->JUMP(foreachin), ast->statement, ast->forToken);
_block = foreachin;
@@ -2499,7 +2520,7 @@ bool Codegen::visit(LocalForStatement *ast)
_block = forbody;
statement(ast->statement);
- setLocation(_block->JUMP(forstep), ast->lastSourceLocation());
+ setJumpOutLocation(_block->JUMP(forstep), ast->statement, ast->forToken);
_block = forstep;
statement(ast->expression);
@@ -2800,7 +2821,7 @@ bool Codegen::visit(WhileStatement *ast)
_block = whilebody;
statement(ast->statement);
- setLocation(_block->JUMP(whilecond), ast->lastSourceLocation());
+ setJumpOutLocation(_block->JUMP(whilecond), ast->statement, ast->whileToken);
_block = whileend;
leaveLoop();
diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
index 6d0d884ed7..584bd10151 100644
--- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
+++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
@@ -315,8 +315,8 @@ private slots:
void stepToEndOfScript_data() { redundancy_data(); }
void stepToEndOfScript();
- void lastLineOfLoop_data();
- void lastLineOfLoop();
+ void lastLineOfConditional_data();
+ void lastLineOfConditional();
private:
QV4Debugger *debugger() const
{
@@ -801,32 +801,60 @@ void tst_qv4debugger::stepToEndOfScript()
QCOMPARE(state.lineNumber, -4); // A return instruction without proper line number.
}
-void tst_qv4debugger::lastLineOfLoop_data()
+void tst_qv4debugger::lastLineOfConditional_data()
{
- QTest::addColumn<QString>("loopHead");
- QTest::addColumn<QString>("loopTail");
-
- QTest::newRow("for") << "for (var i = 0; i < 10; ++i) {\n" << "}\n";
- QTest::newRow("for..in") << "for (var i in [0, 1, 2, 3, 4]) {\n" << "}\n";
- QTest::newRow("while") << "while (ret < 10) {\n" << "}\n";
- QTest::newRow("do..while") << "do {\n" << "} while (ret < 10);\n";
+ QTest::addColumn<QString>("head");
+ QTest::addColumn<QString>("tail");
+ QTest::addColumn<int>("breakPoint");
+ QTest::addColumn<int>("lastLine");
+
+ QTest::newRow("for {block}") << "for (var i = 0; i < 10; ++i) {\n" << "}" << 4 << 7;
+ QTest::newRow("for..in {block}") << "for (var i in [0, 1, 2, 3, 4]) {\n" << "}" << 4 << 7;
+ QTest::newRow("while {block}") << "while (ret < 10) {\n" << "}" << 4 << 7;
+ QTest::newRow("do..while {block}") << "do {\n" << "} while (ret < 10);" << 4 << 7;
+
+ QTest::newRow("if true {block}") << "if (true) {\n" << "}"
+ << 4 << 7;
+ QTest::newRow("if false {block}") << "if (false) {\n" << "}"
+ << 2 << 8;
+ QTest::newRow("if true else {block}") << "if (true) {\n" << "} else {\n ret += 8;\n}"
+ << 4 << 7;
+ QTest::newRow("if false else {block}") << "if (false) {\n" << "} else {\n ret += 8;\n}"
+ << 8 << 9;
+
+ QTest::newRow("for statement") << "for (var i = 0; i < 10; ++i)\n" << "" << 4 << 2;
+ QTest::newRow("for..in statement") << "for (var i in [0, 1, 2, 3, 4])\n" << "" << 4 << 2;
+ QTest::newRow("while statement") << "while (ret < 10)\n" << "" << 4 << 2;
+ QTest::newRow("do..while statement") << "do\n" << "while (ret < 10);" << 4 << 7;
+
+ // For two nested if statements without blocks, we need to map the jump from the inner to the
+ // outer one on the outer "if". There is just no better place.
+ QTest::newRow("if true statement") << "if (true)\n" << "" << 4 << 2;
+ QTest::newRow("if false statement") << "if (false)\n" << "" << 2 << 8;
+
+ // Also two nested ifs without blocks.
+ QTest::newRow("if true else statement") << "if (true)\n" << "else\n ret += 8;" << 4 << 2;
+ QTest::newRow("if false else statement") << "if (false)\n" << "else\n ret += 8;" << 8 << 9;
}
-void tst_qv4debugger::lastLineOfLoop()
+void tst_qv4debugger::lastLineOfConditional()
{
- QFETCH(QString, loopHead);
- QFETCH(QString, loopTail);
+ QFETCH(QString, head);
+ QFETCH(QString, tail);
+ QFETCH(int, breakPoint);
+ QFETCH(int, lastLine);
QString script =
- "var ret = 0;\n"
- + loopHead +
+ "var ret = 2;\n"
+ + head +
" if (ret == 2)\n"
" ret += 4;\n" // breakpoint, then step over
- " else \n"
+ " else\n"
" ret += 1;\n"
- + loopTail;
+ + tail + "\n" +
+ "ret -= 5;";
- debugger()->addBreakPoint("trueBranch", 4);
+ debugger()->addBreakPoint("trueBranch", breakPoint);
m_debuggerAgent->m_resumeSpeed = QV4Debugger::StepOver;
evaluateJavaScript(script, "trueBranch");
QVERIFY(m_debuggerAgent->m_wasPaused);
@@ -834,10 +862,10 @@ void tst_qv4debugger::lastLineOfLoop()
QVERIFY(m_debuggerAgent->m_statesWhenPaused.count() > 1);
QV4Debugger::ExecutionState firstState = m_debuggerAgent->m_statesWhenPaused.first();
QCOMPARE(firstState.fileName, QString("trueBranch"));
- QCOMPARE(firstState.lineNumber, 4);
+ QCOMPARE(firstState.lineNumber, breakPoint);
QV4Debugger::ExecutionState secondState = m_debuggerAgent->m_statesWhenPaused.at(1);
QCOMPARE(secondState.fileName, QString("trueBranch"));
- QCOMPARE(secondState.lineNumber, 7);
+ QCOMPARE(secondState.lineNumber, lastLine);
}
void tst_qv4debugger::redundancy_data()