Skip to content

Commit

Permalink
fix #1484: position of while incorrect in debug
Browse files Browse the repository at this point in the history
  • Loading branch information
liufengyun committed Feb 10, 2017
1 parent b5a9c8c commit 6823e15
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,5 @@ build/

# Put local stuff here
local/
compiler/test/debug/Gen.jar

17 changes: 16 additions & 1 deletion bin/dotr
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ fi
DOTTY_ROOT="$(dirname "$DOTTY_ROOT")"
DOTTY_ROOT="$( cd "$DOTTY_ROOT" >& /dev/null && pwd )/.." # absolute

# debug
DEBUG_STR=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005
DEBUG=

# Load common functions and variables
source "$DOTTY_ROOT"/bin/common

Expand All @@ -24,10 +28,21 @@ function runMain {
echo "java bin not detected - please specify with \$JAVA_BIN or install java to a default location"
exit 1
else
eval "$jbin $CLASS_PATH $@"
eval "$jbin $DEBUG $CLASS_PATH $@"
fi
}

# parse command line params -d to enable debugging
while getopts "dx" opt; do
case "$opt" in
d)
DEBUG="$DEBUG_STR"
;;
esac
done

shift $((OPTIND-1))

first_arg="$1"

if [ -z "$1" ]; then
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -991,12 +991,12 @@ object desugar {
else Apply(ref(tupleTypeRef.classSymbol.companionModule.valRef), ts)
case WhileDo(cond, body) =>
// { <label> def while$(): Unit = if (cond) { body; while$() } ; while$() }
val call = Apply(Ident(nme.WHILE_PREFIX), Nil)
val call = Apply(Ident(nme.WHILE_PREFIX), Nil).withPos(tree.pos)
val rhs = If(cond, Block(body, call), unitLiteral)
labelDefAndCall(nme.WHILE_PREFIX, rhs, call)
case DoWhile(body, cond) =>
// { label def doWhile$(): Unit = { body; if (cond) doWhile$() } ; doWhile$() }
val call = Apply(Ident(nme.DO_WHILE_PREFIX), Nil)
val call = Apply(Ident(nme.DO_WHILE_PREFIX), Nil).withPos(tree.pos)
val rhs = Block(body, If(cond, call, unitLiteral))
labelDefAndCall(nme.DO_WHILE_PREFIX, rhs, call)
case ForDo(enums, body) =>
Expand Down
175 changes: 175 additions & 0 deletions compiler/test/debug/Gen
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#!/bin/sh
exec scala -savecompiled "$0" "$@"
!#

import scala.io.Source
import scala.collection.mutable.Buffer
import scala.collection.mutable.ListBuffer
import scala.collection.mutable.StringBuilder

/** Automate testing debuggability of generated code using JDB and expect
*
* The debugging information is annotated as comments to the code in brackets:
*
* val x = f(3) // [break] [next: line=5]
* val y = 5
*
* 1. A jdb command must be wrapped in brackets, like `[step]`. All jdb commands can be used.
* 2. To check output of jdb for a command, use `[cmd: expect]`.
* 3. If `expect` is wrapped in double quotes, regex is supported.
* 4. Break commands are collected and set globally.
* 5. Other commands will be send to jdb in the order they appear in the source file
*
* Note: jdb uses line number starts from 1
*/

object Gen {
val MainObject = "Test"
val CommandWait = 0.5

sealed trait Tree

case class Break(line: Int) extends Tree

case class Command(val name: String, val expect: Expect = EmptyExpect) extends Tree

sealed trait Expect

case object EmptyExpect extends Expect

case class LitExpect(lit: String) extends Expect

case class PatExpect(pat: String) extends Expect

case class Program(breaks: Seq[Break], commands: Seq[Command])

def error(msg: String): Nothing = {
throw new Exception(msg)
}

def parseCommand(command: String, lineNo: Int): Tree = {
val index = command.indexOf(':')
if (index == -1) {
// simple command
if (command == "break") Break(lineNo)
else Command(command)
} else {
val Seq(cmd, rhs) = command.split(":", 2).toSeq.map(_.trim)
if (rhs.startsWith("\"")) {
// regex match
val content = "\"(.+)\"".r
rhs match {
case content(expect) => Command(cmd, PatExpect(expect))
case _ => error(s"""incorrect specification: `$rhs` for `$cmd` at line $lineNo. Ending " expected.""")
}
} else {
// literal match
Command(cmd, LitExpect(rhs))
}
}
}

def parse(file: String): Program = {
val lines = Source.fromFile(file).getLines.toBuffer

val breaks = new ListBuffer[Break]()
val cmds = new ListBuffer[Command]()
lines.zipWithIndex.map { case (code, line) =>
val comment = if (code.indexOf("//") != -1) code.split("//").last else ""
val regex = """(?<=\[).*?(?=\])""".r
for (p <- regex findAllIn comment) parseCommand(p.trim, line + 1) match { // jdb index from 0
case b: Break => breaks += b
case c: Command => cmds += c
}
}

Program(breaks, cmds)
}

def generate(program: Program, source: String = "tests/debug/"): String = {
val Program(breaks, cmds) = program
val breakpoints = (breaks.map {
case Break(point) =>
s"""|send "stop at $MainObject$$:$point\\r"
|sleep $CommandWait
|expect "breakpoint $MainObject$$:$point"
|expect -re $$
""".stripMargin
}).mkString("\n\n")

val commands = (cmds.map {
case Command(cmd, EmptyExpect) =>
s"""|# send_user "send command `$cmd`\\n"
|send "$cmd\\r"
|sleep $CommandWait
|expect -re $$
""".stripMargin
case Command(cmd, LitExpect(lit)) =>
s"""|# send_user "send command `$cmd`\\n"
|send "$cmd\\r"
|sleep $CommandWait
|expect {
| "*$lit*" { send_user "success - $cmd : $lit \\n" }
| timeout {
| send_user "timeout while waiting for response: $cmd : $lit\\n"
| exit 1
| }
|}
|expect -re $$
|""".stripMargin
case Command(cmd, PatExpect(pat)) =>
s"""|# send_user "send command `$cmd`\\n"
|send "$cmd\\r"
|sleep $CommandWait
|expect {
| -re {$pat} { send_user "success - $cmd : $pat \\n" }
| timeout {
| send_user "timeout while waiting for response: $cmd : $pat\\n"
| exit 1
| }
|}
|expect -re $$
|""".stripMargin
}).mkString("\n\n")

s"""|#!/usr/bin/expect
|
|# log_user 1
|# exp_internal 1
|# set timeout 5
|
|send_user "spawning job...\\n"
|
|spawn jdb -attach 5005 -sourcepath $source
|
|send_user "interacting...\\n"
|
|expect {
| "*VM Started*" { send_user "success - connected to server \\n" }
| timeout {
| send_user "timeout while waiting for: *VM Started*\\n"
| exit 1
| }
|}
|
|send_user "setting breakpoints...\\n"
|
|# breakpoints
|$breakpoints
|
|# run
|send_user "run program...\\n"
|send "run\\r"
|expect "Breakpoint hit"
|
|# interactions
|$commands""".stripMargin
}
}

val prog = Gen.parse(args(0))
// println("--------------------------------")
// println("prog:" + prog)
// println("\n\n\n scrip:")
// println("--------------------------------")
println(Gen.generate(prog))
17 changes: 17 additions & 0 deletions compiler/test/debug/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash

set -x

sbt compile package || exit 1

for file in tests/debug/*.scala; do
./bin/dotc $file || exit 1
./bin/dotr -d Test&
./compiler/test/debug/Gen $file > robot
expect robot

if [[ $? != 0 ]]; then
echo "debug test failed for file $file"
exit 1
fi
done
14 changes: 14 additions & 0 deletions tests/debug/while.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
object Test {

def main(args: Array[String]): Unit = {
var a = 1 + 2
a = a + 3
a = 4 + 5 // [break] [step: while]

while (a * 8 < 100) { // [step: a += 1]
a += 1 // [step: while] [cont: print]
}

print(a) // [break] [cont]
}
}

0 comments on commit 6823e15

Please sign in to comment.