From c5bd18d46e636b3d8eae603cd0bfe6aad63c17be Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sun, 29 May 2011 18:41:30 +0000 Subject: Fixing an interpreter output regression and upd... Fixing an interpreter output regression and updated repl debugging and tracing code. No review. --- .../scala/tools/nsc/interpreter/IMain.scala | 61 ++++++++++++++-------- .../tools/nsc/interpreter/JLineCompletion.scala | 8 +-- .../scala/tools/nsc/interpreter/ReplConfig.scala | 30 ++++++----- 3 files changed, 59 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index e14f6debab..0ef82351b1 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -168,6 +168,12 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo import global._ + private implicit def privateTreeOps(t: Tree): List[Tree] = { + (new Traversable[Tree] { + def foreach[U](f: Tree => U): Unit = t foreach { x => f(x) ; () } + }).toList + } + object naming extends { val global: imain.global.type = imain.global } with Naming { @@ -333,7 +339,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo /** Stubs for work in progress. */ def handleTypeRedefinition(name: TypeName, old: Request, req: Request) = { for (t1 <- old.simpleNameOfType(name) ; t2 <- req.simpleNameOfType(name)) { - DBG("Redefining type '%s'\n %s -> %s".format(name, t1, t2)) + repldbg("Redefining type '%s'\n %s -> %s".format(name, t1, t2)) } } @@ -343,7 +349,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo // assertion failed: fatal: has owner value x, but a class owner is required // so DBG is by-name now to keep it in the family. (It also traps the assertion error, // but we don't want to unnecessarily risk hosing the compiler's internal state.) - DBG("Redefining term '%s'\n %s -> %s".format(name, t1, t2)) + repldbg("Redefining term '%s'\n %s -> %s".format(name, t1, t2)) } } def recordRequest(req: Request) { @@ -428,17 +434,26 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo // rewriting "5 // foo" to "val x = { 5 // foo }" creates broken code because // the close brace is commented out. Strip single-line comments. + // ... but for error message output reasons this is not used, and rather than + // enclosing in braces it is constructed like "val x =\n5 // foo". private def removeComments(line: String): String = { + showCodeIfDebugging(line) // as we're about to lose our // show line.lines map (s => s indexOf "//" match { case -1 => s case idx => s take idx }) mkString "\n" } + private def safePos(t: Tree, alt: Int): Int = + try t.pos.startOrPoint + catch { case _: UnsupportedOperationException => alt } + // Given an expression like 10 * 10 * 10 we receive the parent tree positioned // at a '*'. So look at each subtree and find the earliest of all positions. private def earliestPosition(tree: Tree): Int = { var pos = Int.MaxValue - tree foreach (t => pos = math.min(pos, t.pos.startOrPoint)) + tree foreach { t => + pos = math.min(pos, safePos(t, Int.MaxValue)) + } pos } @@ -449,6 +464,11 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo case Some(Nil) => return Left(IR.Error) // parse error or empty input case Some(trees) => trees } + repltrace( + trees map { t => + t map { t0 => t0.getClass + " at " + safePos(t0, -1) + "\n" } + } mkString + ) // If the last tree is a bare expression, pinpoint where it begins using the // AST node position and snap the line off there. Rewrite the code embodied // by the last tree as a ValDef instead, so we can access the value. @@ -458,23 +478,24 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo // The position of the last tree, and the source code split there. val lastpos = earliestPosition(trees.last) val (l1, l2) = content splitAt lastpos - val l2body = removeComments(l2).trim + val prefix = if (l1.trim == "") "" else l1 + ";\n" val varName = if (synthetic) freshInternalVarName() else freshUserVarName() - val valDef = "val " + varName + " = { " + l2body + " }" - - DBG(List( - " line" -> line, - "content" -> content, - " was" -> l2, - " l2body" -> l2body, - " now" -> valDef) map { + // Note to self: val source needs to have this precise structure so that + // error messages print the user-submitted part without the "val res0 = " part. + val combined = prefix + "val " + varName + " =\n" + l2 + + repldbg(List( + " line" -> line, + " content" -> content, + " was" -> l2, + "combined" -> combined) map { case (label, s) => label + ": '" + s + "'" } mkString "\n" ) // Rewriting "foo ; bar ; 123" // to "foo ; bar ; val resXX = 123" - requestFromLine(l1 + " ;\n" + valDef + " ;\n", synthetic) match { + requestFromLine(combined, synthetic) match { case Right(req) => return Right(req withOriginalLine line) case x => return x } @@ -549,7 +570,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo ) bindRep.callOpt("set", value) match { case Some(_) => interpret("val %s = %s.value".format(name, bindRep.evalPath)) - case _ => DBG("Set failed in bind(%s, %s, %s)".format(name, boundType, value)) ; IR.Error + case _ => repldbg("Set failed in bind(%s, %s, %s)".format(name, boundType, value)) ; IR.Error } } def rebind(p: NamedParam): IR.Result = { @@ -952,9 +973,9 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo // 3) Try interpreting it as an expression. private var typeOfExpressionDepth = 0 def typeOfExpression(expr: String): Option[Type] = { - DBG("typeOfExpression(" + expr + ")") + repldbg("typeOfExpression(" + expr + ")") if (typeOfExpressionDepth > 2) { - DBG("Terminating typeOfExpression recursion for expression: " + expr) + repldbg("Terminating typeOfExpression recursion for expression: " + expr) return None } @@ -1031,18 +1052,14 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo */ if (code.lines exists (_.trim endsWith "// show")) { echo(code) - parse(code) foreach (ts => ts foreach (t => withoutUnwrapping(DBG(asCompactString(t))))) + parse(code) foreach (ts => ts foreach (t => withoutUnwrapping(repldbg(asCompactString(t))))) } } // debugging def debugging[T](msg: String)(res: T) = { - DBG(msg + " " + res) + repldbg(msg + " " + res) res } - def DBG(s: => String) = if (isReplDebug) { - try repldbg(s) - catch { case x: AssertionError => repldbg("Assertion error printing debug string:\n " + x) } - } } /** Utility methods for the Interpreter. */ diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala index b7baea57fc..8b387823ff 100644 --- a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala +++ b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala @@ -18,7 +18,7 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput import global._ import definitions.{ PredefModule, RootClass, AnyClass, AnyRefClass, ScalaPackage, JavaLangPackage } type ExecResult = Any - import intp.{ DBG, debugging, afterTyper } + import intp.{ debugging, afterTyper } // verbosity goes up with consecutive tabs private var verbosity: Int = 0 @@ -325,7 +325,7 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput // This is jline's entry point for completion. override def complete(buf: String, cursor: Int): Candidates = { verbosity = if (isConsecutiveTabs(buf, cursor)) verbosity + 1 else 0 - DBG("\ncomplete(%s, %d) last = (%s, %d), verbosity: %s".format(buf, cursor, lastBuf, lastCursor, verbosity)) + repldbg("\ncomplete(%s, %d) last = (%s, %d), verbosity: %s".format(buf, cursor, lastBuf, lastCursor, verbosity)) // we don't try lower priority completions unless higher ones return no results. def tryCompletion(p: Parsed, completionFunction: Parsed => List[String]): Option[Candidates] = { @@ -338,7 +338,7 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput val advance = commonPrefix(winners) lastCursor = p.position + advance.length lastBuf = (buf take p.position) + advance - DBG("tryCompletion(%s, _) lastBuf = %s, lastCursor = %s, p.position = %s".format( + repldbg("tryCompletion(%s, _) lastBuf = %s, lastCursor = %s, p.position = %s".format( p, lastBuf, lastCursor, p.position)) p.position } @@ -369,7 +369,7 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput } catch { case ex: Exception => - DBG("Error: complete(%s, %s) provoked %s".format(buf, cursor, ex)) + repldbg("Error: complete(%s, %s) provoked %s".format(buf, cursor, ex)) Candidates(cursor, List(" ", "")) } } diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala b/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala index a8287e3a9a..5c24e492cf 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala @@ -9,7 +9,6 @@ package interpreter import sys.{ Prop, BooleanProp } trait ReplConfig { - class ReplProps { private def bool(name: String) = BooleanProp.keyExists(name) @@ -20,23 +19,26 @@ trait ReplConfig { val trace = bool("scala.repl.trace") val power = bool("scala.repl.power") - val replInitCode = Prop[JFile]("scala.repl.initcode") + val replInitCode = Prop[JFile]("scala.repl.initcode") val powerInitCode = Prop[JFile]("scala.repl.power.initcode") - val powerBanner = Prop[JFile]("scala.repl.power.banner") + val powerBanner = Prop[JFile]("scala.repl.power.banner") } lazy val replProps = new ReplProps /** Debug output */ - private[nsc] def repldbg(msg: String) = if (isReplDebug) Console println msg - - /** Tracing */ - private[nsc] def tracing[T](msg: String)(x: T): T = { - if (isReplDebug) - println("(" + msg + ") " + x) - - x - } - - def isReplDebug: Boolean = replProps.debug + private[nsc] def repldbg(msg: => String) = + if (isReplDebug) { + try Console println msg + catch { case x: AssertionError => Console.println("Assertion error in repldbg:\n " + x) } + } + + private[nsc] def repltrace(msg: => String) = + if (isReplTrace) { + try Console println msg + catch { case x: AssertionError => Console.println("Assertion error in repltrace:\n " + x) } + } + + def isReplTrace: Boolean = replProps.trace + def isReplDebug: Boolean = replProps.debug || isReplTrace def isReplPower: Boolean = replProps.power } -- cgit v1.2.3