Skip to content

Commit c07aa08

Browse files
committed
fix #1484: position of while incorrect in debug
1 parent da7d723 commit c07aa08

File tree

6 files changed

+220
-3
lines changed

6 files changed

+220
-3
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,5 @@ build/
5252

5353
# Put local stuff here
5454
local/
55+
compiler/test/debug/Gen.jar
5556

bin/dotr

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ fi
88
DOTTY_ROOT="$(dirname "$DOTTY_ROOT")"
99
DOTTY_ROOT="$( cd "$DOTTY_ROOT" >& /dev/null && pwd )/.." # absolute
1010

11+
# debug
12+
DEBUG_STR=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005
13+
DEBUG=
14+
1115
# Load common functions and variables
1216
source $DOTTY_ROOT/bin/common
1317

@@ -24,10 +28,21 @@ function runMain {
2428
echo "java bin not detected - please specify with \$JAVA_BIN or install java to a default location"
2529
exit 1
2630
else
27-
eval "$jbin $CLASS_PATH $@"
31+
eval "$jbin $DEBUG $CLASS_PATH $@"
2832
fi
2933
}
3034

35+
# parse command line params -d to enable debugging
36+
while getopts "dx" opt; do
37+
case $opt in
38+
d)
39+
DEBUG=$DEBUG_STR
40+
;;
41+
esac
42+
done
43+
44+
shift $((OPTIND-1))
45+
3146
first_arg=$1
3247

3348
if [ -z "$1" ]; then

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -989,12 +989,12 @@ object desugar {
989989
else Apply(ref(tupleTypeRef.classSymbol.companionModule.valRef), ts)
990990
case WhileDo(cond, body) =>
991991
// { <label> def while$(): Unit = if (cond) { body; while$() } ; while$() }
992-
val call = Apply(Ident(nme.WHILE_PREFIX), Nil)
992+
val call = Apply(Ident(nme.WHILE_PREFIX), Nil).withPos(tree.pos)
993993
val rhs = If(cond, Block(body, call), unitLiteral)
994994
labelDefAndCall(nme.WHILE_PREFIX, rhs, call)
995995
case DoWhile(body, cond) =>
996996
// { label def doWhile$(): Unit = { body; if (cond) doWhile$() } ; doWhile$() }
997-
val call = Apply(Ident(nme.DO_WHILE_PREFIX), Nil)
997+
val call = Apply(Ident(nme.DO_WHILE_PREFIX), Nil).withPos(tree.pos)
998998
val rhs = Block(body, If(cond, call, unitLiteral))
999999
labelDefAndCall(nme.DO_WHILE_PREFIX, rhs, call)
10001000
case ForDo(enums, body) =>

compiler/test/debug/Gen

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
#!/bin/sh
2+
exec scala -savecompiled "$0" "$@"
3+
!#
4+
5+
import scala.io.Source
6+
import scala.collection.mutable.Buffer
7+
import scala.collection.mutable.ListBuffer
8+
import scala.collection.mutable.StringBuilder
9+
10+
/** Automate testing debuggability of generated code using JDB and expect
11+
*
12+
* The debugging information is annotated as comments to the code in brackets:
13+
*
14+
* val x = f(3) // [break] [next: line=5]
15+
* val y = 5
16+
*
17+
* 1. A jdb command must be wrapped in brackets, like `[step]`. All jdb commands can be used.
18+
* 2. To check output of jdb for a command, use `[cmd: expect]`.
19+
* 3. If `expect` is wrapped in double quotes, regex is supported.
20+
* 4. Break commands are collected and set globally.
21+
* 5. Other commands will be send to jdb in the order they appear in the source file
22+
*
23+
* Note: jdb uses line number starts from 1
24+
*/
25+
26+
object Gen {
27+
val MainObject = "Test"
28+
29+
sealed trait Tree
30+
31+
case class Break(line: Int) extends Tree
32+
33+
case class Command(val name: String, val expect: Expect = EmptyExpect) extends Tree
34+
35+
sealed trait Expect
36+
37+
case object EmptyExpect extends Expect
38+
39+
case class LitExpect(lit: String) extends Expect
40+
41+
case class PatExpect(pat: String) extends Expect
42+
43+
case class Program(breaks: Seq[Break], commands: Seq[Command])
44+
45+
def error(msg: String): Nothing = {
46+
throw new Exception(msg)
47+
}
48+
49+
def parseCommand(command: String, lineNo: Int): Tree = {
50+
val index = command.indexOf(':')
51+
if (index == -1) {
52+
// simple command
53+
if (command == "break") Break(lineNo)
54+
else Command(command)
55+
} else {
56+
val Seq(cmd, rhs) = command.split(":", 2).toSeq.map(_.trim)
57+
if (rhs.startsWith("\"")) {
58+
// regex match
59+
val content = "\"(.+)\"".r
60+
rhs match {
61+
case content(expect) => Command(cmd, PatExpect(expect))
62+
case _ => error(s"""incorrect specification: `$rhs` for `$cmd` at line $lineNo. Ending " expected.""")
63+
}
64+
} else {
65+
// literal match
66+
Command(cmd, LitExpect(rhs))
67+
}
68+
}
69+
}
70+
71+
def parse(file: String): Program = {
72+
val lines = Source.fromFile(file).getLines.toBuffer
73+
74+
val breaks = new ListBuffer[Break]()
75+
val cmds = new ListBuffer[Command]()
76+
lines.zipWithIndex.map { case (code, line) =>
77+
val comment = if (code.indexOf("//") != -1) code.split("//").last else ""
78+
val regex = """(?<=\[).*?(?=\])""".r
79+
for (p <- regex findAllIn comment) parseCommand(p.trim, line + 1) match { // jdb index from 0
80+
case b: Break => breaks += b
81+
case c: Command => cmds += c
82+
}
83+
}
84+
85+
Program(breaks, cmds)
86+
}
87+
88+
def generate(program: Program, source: String = "tests/debug/"): String = {
89+
val Program(breaks, cmds) = program
90+
val breakpoints = (breaks.map {
91+
case Break(point) =>
92+
s"""|send "stop at $MainObject$$:$point\\r"
93+
|expect "breakpoint $MainObject$$:$point"
94+
""".stripMargin
95+
}).mkString("\n\n")
96+
97+
val commands = (cmds.map {
98+
case Command(cmd, EmptyExpect) =>
99+
s"""|# send_user "send command `$cmd`\\n"
100+
|send "$cmd\\r"
101+
""".stripMargin
102+
case Command(cmd, LitExpect(lit)) =>
103+
s"""|# send_user "send command `$cmd`\\n"
104+
|send "$cmd\\r"
105+
|expect {
106+
| "*$lit*" { send_user "success - $cmd : $lit \\n" }
107+
| timeout {
108+
| send_user "timeout while waiting for response: $cmd : $lit\\n"
109+
| exit 1
110+
| }
111+
|}
112+
|""".stripMargin
113+
case Command(cmd, PatExpect(pat)) =>
114+
s"""|# send_user "send command `$cmd`\\n"
115+
|send "$cmd\\r"
116+
|expect {
117+
| -re {$pat} { send_user "success - $cmd : $pat \\n" }
118+
| timeout {
119+
| send_user "timeout while waiting for response: $cmd : $pat\\n"
120+
| exit 1
121+
| }
122+
|}
123+
|""".stripMargin
124+
}).mkString("\n\n")
125+
126+
s"""|#!/usr/bin/expect
127+
|
128+
|# log_user 1
129+
|# exp_internal 1
130+
|# set timeout 5
131+
|
132+
|send_user "spawning job...\\n"
133+
|
134+
|spawn jdb -attach 5005 -sourcepath $source
135+
|
136+
|send_user "interacting...\\n"
137+
|
138+
|expect {
139+
| "*VM Started*" { send_user "success - connected to server \\n" }
140+
| timeout {
141+
| send_user "timeout while waiting for: *VM Started*\\n"
142+
| exit 1
143+
| }
144+
|}
145+
|
146+
|send_user "setting breakpoints...\\n"
147+
|
148+
|# breakpoints
149+
|$breakpoints
150+
|
151+
|# run
152+
|send_user "run program...\\n"
153+
|send "run\\r"
154+
|expect "Breakpoint hit"
155+
|
156+
|# interactions
157+
|$commands""".stripMargin
158+
}
159+
}
160+
161+
val prog = Gen.parse(args(0))
162+
// println("--------------------------------")
163+
// println("prog:" + prog)
164+
// println("\n\n\n scrip:")
165+
// println("--------------------------------")
166+
println(Gen.generate(prog))
167+
168+

compiler/test/debug/test

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env bash
2+
3+
set -x
4+
5+
sbt compile package || exit 1
6+
7+
for file in tests/debug/*.scala; do
8+
./bin/dotc $file || exit 1
9+
./bin/dotr -d Test&
10+
./compiler/test/debug/Gen $file > robot
11+
expect robot
12+
13+
if [[ $? != 0 ]]; then
14+
echo "debug test failed for file $file"
15+
exit 1
16+
fi
17+
done
18+
19+

tests/debug/while.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
object Test {
2+
3+
def main(args: Array[String]): Unit = {
4+
var a = 1 + 2
5+
a = a + 3
6+
a = 4 + 5 // [break] [step: while]
7+
8+
while (a * 8 < 100) { // [step: a += 1]
9+
a += 1 // [step: while] [cont: print]
10+
}
11+
12+
print(a) // [break] [cont]
13+
}
14+
}

0 commit comments

Comments
 (0)