diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index 9d130b5166fc..6d12549d79ad 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -1,18 +1,20 @@ package dotty.tools package repl -import java.io.{ StringWriter, PrintWriter } -import java.lang.{ ClassLoader, ExceptionInInitializerError } +import java.io.{PrintWriter, StringWriter} +import java.lang.{ClassLoader, ExceptionInInitializerError} import java.lang.reflect.InvocationTargetException +import scala.util.Try import scala.runtime.ScalaRunTime - import dotc.core.Contexts.Context import dotc.core.Denotations.Denotation import dotc.core.Flags import dotc.core.Symbols.Symbol import dotc.core.StdNames.str +import scala.util.Try + /** This rendering object uses `ClassLoader`s to accomplish crossing the 4th * wall (i.e. fetching back values from the compiled class files put into a * specific class loader capable of loading from memory) and rendering them. @@ -72,21 +74,20 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None) { d.symbol.showUser /** Render value definition result */ - def renderVal(d: Denotation)(implicit ctx: Context): Option[String] = { + def renderVal(d: Denotation)(implicit ctx: Context): Try[Option[String]] = { val dcl = d.symbol.showUser - try { + Try { val resultValue = if (d.symbol.is(Flags.Lazy)) Some("") else valueOf(d.symbol) resultValue.map(value => s"$dcl = $value") } - catch { case ex: InvocationTargetException => Some(renderError(ex)) } } /** Render the stack trace of the underlying exception */ - private def renderError(ex: InvocationTargetException): String = { + def renderError(ex: InvocationTargetException): String = { val cause = ex.getCause match { case ex: ExceptionInInitializerError => ex.getCause case ex => ex diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 4717d38deef5..c5531519188e 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -1,6 +1,7 @@ package dotty.tools.repl import java.io.PrintStream +import java.lang.reflect.InvocationTargetException import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.ast.{tpd, untpd} @@ -22,9 +23,11 @@ import dotty.tools.dotc.util.{SourceFile, SourcePosition} import dotty.tools.dotc.{CompilationUnit, Driver} import dotty.tools.io._ import org.jline.reader._ +import java.lang.reflect.InvocationTargetException import scala.annotation.tailrec import scala.collection.JavaConverters._ +import scala.util.{Failure, Success} /** The state of the REPL contains necessary bindings instead of having to have * mutation @@ -224,13 +227,14 @@ class ReplDriver(settings: Array[String], val warnings = newState.context.reporter.removeBufferedMessages(newState.context) displayErrors(warnings)(newState) // display warnings - displayDefinitions(unit.tpdTree, newestWrapper)(newStateWithImports) + + displayDefinitions(unit.tpdTree, newestWrapper)(newStateWithImports).getOrElse(state) } ) } /** Display definitions from `tree` */ - private def displayDefinitions(tree: tpd.Tree, newestWrapper: Name)(implicit state: State): State = { + private def displayDefinitions(tree: tpd.Tree, newestWrapper: Name)(implicit state: State): Option[State] = { implicit val ctx = state.context def resAndUnit(denot: Denotation) = { @@ -263,15 +267,40 @@ class ReplDriver(settings: Array[String], val typeAliases = info.bounds.hi.typeMembers.filter(_.symbol.info.isTypeAlias) - ( - typeAliases.map("// defined alias " + _.symbol.showUser) ++ - defs.map(rendering.renderMethod) ++ - vals.map(rendering.renderVal).flatten - ).foreach(str => out.println(SyntaxHighlighting.highlight(str))) + var renderedVals: List[Option[String]] = Nil + var error: InvocationTargetException = null + import scala.util.control.Breaks.{break, breakable} + breakable { + for (v <- vals) { + val render = rendering.renderVal(v) + render match { + case Success(rv) => + renderedVals = renderedVals :+ rv + case Failure(th) => th match { + case ex: InvocationTargetException => + error = ex + break() + case other => + throw other + } + } + } + } - state.copy(valIndex = state.valIndex - vals.count(resAndUnit)) + if (error != null) { + out.println(SyntaxHighlighting.highlight(rendering.renderError(error))) + rendering = new Rendering(classLoader) + None + } else { + ( + typeAliases.map("// defined alias " + _.symbol.showUser) ++ + defs.map(rendering.renderMethod) ++ + renderedVals.flatten + ).foreach(str => out.println(SyntaxHighlighting.highlight(str))) + Some(state.copy(valIndex = state.valIndex - vals.count(resAndUnit))) + } } - else state + else Some(state) def isSyntheticCompanion(sym: Symbol) = sym.is(Module) && sym.is(Synthetic) @@ -294,18 +323,13 @@ class ReplDriver(settings: Array[String], ctx.atPhase(ctx.typerPhase.next) { implicit ctx => - // Display members of wrapped module: tree.symbol.info.memberClasses .find(_.symbol.name == newestWrapper.moduleClassName) - .map { wrapperModule => + .flatMap { wrapperModule => displayTypeDefs(wrapperModule.symbol) displayMembers(wrapperModule.symbol) } - .getOrElse { - // user defined a trait/class/object, so no module needed - state - } } }