summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-06-03 03:59:12 +0000
committerPaul Phillips <paulp@improving.org>2011-06-03 03:59:12 +0000
commit33d45626bd1b13adfd36071cf8f4d94123c7d29d (patch)
tree181265875be9495dfd43b0f72db87d8ca4c26d5f
parent92a2fd5397e1c6f67677e96bbf427ab256ec37f9 (diff)
downloadscala-33d45626bd1b13adfd36071cf8f4d94123c7d29d.tar.gz
scala-33d45626bd1b13adfd36071cf8f4d94123c7d29d.tar.bz2
scala-33d45626bd1b13adfd36071cf8f4d94123c7d29d.zip
Misc accumulated repl work.
down into more flexible pieces. No review.
-rw-r--r--src/compiler/scala/reflect/internal/StdNames.scala1
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ILoop.scala12
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/IMain.scala249
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Logger.scala18
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala2
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala21
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Naming.scala60
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala43
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ReplProps.scala25
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala2
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala8
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/package.scala7
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala1
13 files changed, 271 insertions, 178 deletions
diff --git a/src/compiler/scala/reflect/internal/StdNames.scala b/src/compiler/scala/reflect/internal/StdNames.scala
index f8f3c681be..a31dc30f48 100644
--- a/src/compiler/scala/reflect/internal/StdNames.scala
+++ b/src/compiler/scala/reflect/internal/StdNames.scala
@@ -388,7 +388,6 @@ trait StdNames extends /*reflect.generic.StdNames with*/ NameManglers { self: Sy
val EXCEPTION_RESULT_PREFIX = "exceptionResult"
val INTERPRETER_IMPORT_WRAPPER = "$iw"
val INTERPRETER_LINE_PREFIX = "line"
- val INTERPRETER_SYNTHVAR_PREFIX = "synthvar$"
val INTERPRETER_VAR_PREFIX = "res"
val INTERPRETER_WRAPPER_SUFFIX = "$object"
val WHILE_PREFIX = "while$"
diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
index 51f8da6063..75cbf6cdbc 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
@@ -7,7 +7,7 @@ package scala.tools.nsc
package interpreter
import Predef.{ println => _, _ }
-import java.io.{ BufferedReader, FileReader, PrintWriter }
+import java.io.{ BufferedReader, FileReader }
import java.util.concurrent.locks.ReentrantLock
import scala.sys.process.Process
import session._
@@ -31,13 +31,13 @@ import io.{ File, Sources }
* @author Lex Spoon
* @version 1.2
*/
-class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
+class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
extends AnyRef
with LoopCommands
with ILoopInit
{
- def this(in0: BufferedReader, out: PrintWriter) = this(Some(in0), out)
- def this() = this(None, new PrintWriter(Console.out, true))
+ def this(in0: BufferedReader, out: JPrintWriter) = this(Some(in0), out)
+ def this() = this(None, new JPrintWriter(Console.out, true))
var in: InteractiveReader = _ // the input stream from which commands come
var settings: Settings = _
@@ -861,7 +861,7 @@ object ILoop {
stringFromStream { ostream =>
Console.withOut(ostream) {
- val output = new PrintWriter(new OutputStreamWriter(ostream), true) {
+ val output = new JPrintWriter(new OutputStreamWriter(ostream), true) {
override def write(str: String) = {
// completely skip continuation lines
if (str forall (ch => ch.isWhitespace || ch == '|')) ()
@@ -897,7 +897,7 @@ object ILoop {
stringFromStream { ostream =>
Console.withOut(ostream) {
val input = new BufferedReader(new StringReader(code))
- val output = new PrintWriter(new OutputStreamWriter(ostream), true)
+ val output = new JPrintWriter(new OutputStreamWriter(ostream), true)
val repl = new ILoop(input, output)
if (sets.classpath.isDefault)
diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
index 6a61f107c8..f39cbd0d14 100644
--- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
@@ -7,10 +7,12 @@ package scala.tools.nsc
package interpreter
import Predef.{ println => _, _ }
-import java.io.{ PrintWriter }
-import java.lang.reflect
-import java.net.URL
-import util._
+import util.{ Set => _, _ }
+import scala.collection.{ mutable, immutable }
+import scala.sys.BooleanProp
+import Exceptional.unwrap
+import ScalaClassLoader.URLClassLoader
+import symtab.Flags
import io.VirtualDirectory
import reporters._
import symtab.Flags
@@ -44,7 +46,7 @@ import IMain._
* all variables defined by that code. To extract the result of an
* interpreted line to show the user, a second "result object" is created
* which imports the variables exported by the above object and then
- * exports a single member named "$export". To accomodate user expressions
+ * exports members called "$eval" and "$print". To accomodate user expressions
* that read from variables or methods defined in previous statements, "import"
* statements are used.
*
@@ -57,25 +59,29 @@ import IMain._
* @author Moez A. Abdel-Gawad
* @author Lex Spoon
*/
-class IMain(val settings: Settings, protected val out: PrintWriter) extends Imports {
+class IMain(val settings: Settings, protected val out: JPrintWriter) extends Imports {
imain =>
/** construct an interpreter that reports to Console */
def this(settings: Settings) = this(settings, new NewLinePrintWriter(new ConsoleWriter, true))
def this() = this(new Settings())
- /** whether to print out result lines */
- private[nsc] var printResults: Boolean = true
-
- /** whether to print errors */
- private[nsc] var totalSilence: Boolean = false
-
- private val RESULT_OBJECT_PREFIX = "RequestResult$"
-
+ lazy val repllog: Logger = new Logger {
+ val out: JPrintWriter = imain.out
+ val isInfo: Boolean = BooleanProp keyExists "scala.repl.info"
+ val isDebug: Boolean = BooleanProp keyExists "scala.repl.debug"
+ val isTrace: Boolean = BooleanProp keyExists "scala.repl.trace"
+ }
lazy val formatting: Formatting = new Formatting {
val prompt = Properties.shellPromptString
}
+ lazy val reporter: ReplReporter = new ReplReporter(this)
+
import formatting._
+ import reporter.{ printMessage, withoutTruncating }
+
+ private[nsc] var printResults: Boolean = true // whether to print result lines
+ private[nsc] var totalSilence: Boolean = false // whether to print anything
/** directory to save .class files to */
val virtualDirectory = new VirtualDirectory("(memory)", None) {
@@ -89,15 +95,9 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
def show() = pp(this, 0)
}
- /** reporter */
- lazy val reporter: ReplReporter = new ReplReporter(this)
- import reporter.{ printMessage, withoutTruncating }
-
- // not sure if we have some motivation to print directly to console
+ // This exists mostly because using the reporter too early leads to deadlock.
private def echo(msg: String) { Console println msg }
- // protected def defaultImports: List[String] = List("_root_.scala.sys.exit")
-
/** We're going to go to some trouble to initialize the compiler asynchronously.
* It's critical that nothing call into it until it's been initialized or we will
* run into unrecoverable issues, but the perceived repl startup time goes
@@ -107,8 +107,6 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
*/
private val _compiler: Global = newCompiler(settings, reporter)
private var _initializeComplete = false
- def isInitializeComplete = _initializeComplete
-
private def _initSources = List(new BatchSourceFile("<init>", "class $repl_$init { }"))
private def _initialize() = {
try {
@@ -130,6 +128,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
result
}
}
+ def isInitializeComplete = _initializeComplete
/** the public, go through the future compiler */
lazy val global: Global = {
@@ -146,6 +145,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
lazy val compiler: global.type = global
import global._
+ import definitions.{ ScalaPackage, JavaLangPackage, PredefModule, RootClass }
private implicit def privateTreeOps(t: Tree): List[Tree] = {
(new Traversable[Tree] {
@@ -153,6 +153,9 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
}).toList
}
+ // TODO: If we try to make naming a lazy val, we run into big time
+ // scalac unhappiness with what look like cycles. It has not been easy to
+ // reduce, but name resolution clearly takes different paths.
object naming extends {
val global: imain.global.type = imain.global
} with Naming {
@@ -162,6 +165,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
if (definedNameMap contains name) freshUserVarName()
else name
}
+ def isInternalVarName(name: Name): Boolean = isInternalVarName("" + name)
}
import naming._
@@ -179,14 +183,11 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
def afterTyper[T](op: => T): T = atPhase(currentRun.typerPhase.next)(op)
/** Temporarily be quiet */
- def beQuietDuring[T](operation: => T): T = {
- val wasPrinting = printResults
- ultimately(printResults = wasPrinting) {
- if (isReplDebug) echo(">> beQuietDuring")
- else printResults = false
-
- operation
- }
+ def beQuietDuring[T](body: => T): T = {
+ val saved = printResults
+ printResults = false
+ try body
+ finally printResults = saved
}
def beSilentDuring[T](operation: => T): T = {
val saved = totalSilence
@@ -197,8 +198,21 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
def quietRun[T](code: String) = beQuietDuring(interpret(code))
+ private def logAndDiscard[T](label: String, alt: => T): PartialFunction[Throwable, T] = {
+ case t => repldbg(label + ": " + t) ; alt
+ }
+
/** whether to bind the lastException variable */
- private var bindLastException = true
+ private var bindExceptions = true
+ /** takes AnyRef because it may be binding a Throwable or an Exceptional */
+ private def withLastExceptionLock[T](body: => T): T = {
+ assert(bindExceptions, "withLastExceptionLock called incorrectly.")
+ bindExceptions = false
+
+ try beQuietDuring(body)
+ catch logAndDiscard("bindLastException", null.asInstanceOf[T])
+ finally bindExceptions = true
+ }
/** A string representing code to be wrapped around all lines. */
private var _executionWrapper: String = ""
@@ -206,29 +220,26 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
def setExecutionWrapper(code: String) = _executionWrapper = code
def clearExecutionWrapper() = _executionWrapper = ""
- /** Temporarily stop binding lastException */
- def withoutBindingLastException[T](operation: => T): T = {
- val wasBinding = bindLastException
- ultimately(bindLastException = wasBinding) {
- bindLastException = false
- operation
- }
- }
-
- protected def createLineManager(): Line.Manager = new Line.Manager
lazy val lineManager = createLineManager()
/** interpreter settings */
lazy val isettings = new ISettings(this)
- /** Instantiate a compiler. Subclasses can override this to
- * change the compiler class used by this interpreter. */
+ /** Create a line manager. Overridable. */
+ protected def createLineManager(): Line.Manager = new Line.Manager
+
+ /** Instantiate a compiler. Overridable. */
protected def newCompiler(settings: Settings, reporter: Reporter) = {
settings.outputDirs setSingleOutput virtualDirectory
settings.exposeEmptyPackage.value = true
+
new Global(settings, reporter)
}
+ /** Parent classloader. Overridable. */
+ protected def parentClassLoader: ClassLoader =
+ settings.explicitParentLoader.getOrElse( this.getClass.getClassLoader() )
+
/** the compiler's classpath, as URL's */
lazy val compilerClasspath = global.classPath.asURLs
@@ -272,11 +283,6 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
}
}
}
- private def loadByName(s: String): JClass =
- (classLoader tryToInitializeClass s) getOrElse sys.error("Failed to load expected class: '" + s + "'")
-
- protected def parentClassLoader: ClassLoader =
- settings.explicitParentLoader.getOrElse( this.getClass.getClassLoader() )
def getInterpreterClassLoader() = classLoader
@@ -347,7 +353,8 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
newSym <- req.definedSymbols get name
oldSym <- oldReq.definedSymbols get name.companionName
} {
- replwarn("warning: previously defined %s is not a companion to %s.".format(oldSym, newSym))
+ replwarn("warning: previously defined %s is not a companion to %s.".format(
+ stripString("" + oldSym), stripString("" + newSym)))
replwarn("Companions must be defined together; you may wish to use :paste mode for this.")
}
@@ -523,6 +530,9 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
if (succeeded) {
if (printResults)
show()
+ else if (isReplDebug) // show quiet-mode activity
+ printMessage(result.trim.lines map ("[quiet] " + _) mkString "\n")
+
// Book-keeping. Have to record synthetic requests too,
// as they may have been issued for information, e.g. :type
recordRequest(req)
@@ -585,7 +595,8 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
def quietBind(p: NamedParam): IR.Result = beQuietDuring(bind(p))
def bind(p: NamedParam): IR.Result = bind(p.name, p.tpe, p.value)
def bind[T: Manifest](name: String, value: T): IR.Result = bind((name, value))
- def bindValue(x: Any): IR.Result = bind(freshUserVarName(), TypeStrings.fromValue(x), x)
+ def bindValue(x: Any): IR.Result = bindValue(freshUserVarName(), x)
+ def bindValue(name: String, x: Any): IR.Result = bind(name, TypeStrings.fromValue(x), x)
/** Reset this interpreter, forgetting all user-specified requests. */
def reset() {
@@ -613,11 +624,40 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
class ReadEvalPrint(lineId: Int) {
def this() = this(freshLineId())
- val packageName = "$line" + lineId
- val readName = "$read"
- val evalName = "$eval"
- val printName = "$print"
- val valueMethod = "$result" // no-args method giving result
+ val packageName = sessionNames.line + lineId
+ val readName = sessionNames.read
+ val evalName = sessionNames.eval
+ val printName = sessionNames.print
+
+ class LineExceptional(ex: Throwable) extends Exceptional(ex) {
+ private def showReplInternal = isettings.showInternalStackTraces
+
+ override def spanFn(frame: JavaStackFrame) =
+ if (showReplInternal) super.spanFn(frame)
+ else !(frame.className startsWith evalPath)
+
+ override def contextPrelude = super.contextPrelude + (
+ if (showReplInternal) ""
+ else "/* The repl internal portion of the stack trace is elided. */\n"
+ )
+ }
+ def bindError(t: Throwable) = {
+ if (!bindExceptions) // avoid looping if already binding
+ throw t
+
+ val unwrapped = unwrap(t)
+ withLastExceptionLock {
+ if (opt.richExes) {
+ val ex = new LineExceptional(unwrapped)
+ bind[Exceptional]("lastException", ex)
+ ex.contextHead + "\n(access lastException for the full trace)"
+ }
+ else {
+ bind[Throwable]("lastException", unwrapped)
+ util.stackTraceString(unwrapped)
+ }
+ }
+ }
// TODO: split it out into a package object and a regular
// object and we can do that much less wrapping.
@@ -635,13 +675,13 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
def callOpt(name: String, args: Any*): Option[AnyRef] =
try Some(call(name, args: _*))
- catch { case ex: Exception =>
- quietBind("lastException", ex)
- None
- }
+ catch { case ex: Exception => bindError(ex) ; None }
+
+ private def load(s: String): Class[_] =
+ (classLoader tryToInitializeClass s) getOrElse sys.error("Failed to load expected class: '" + s + "'")
- lazy val evalClass = loadByName(evalPath)
- lazy val evalValue = callOpt(valueMethod)
+ lazy val evalClass = load(evalPath)
+ lazy val evalValue = callOpt(evalName)
def compile(source: String): Boolean = compileAndSaveRun("<console>", source)
def lineAfterTyper[T](op: => T): T = {
@@ -660,10 +700,9 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
}
}
private var lastRun: Run = _
- private def evalMethod(name: String) = {
- val methods = evalClass.getMethods filter (_.getName == name)
- assert(methods.size == 1, "Internal error - eval object method " + name + " is overloaded: " + methods)
- methods.head
+ private def evalMethod(name: String) = evalClass.getMethods filter (_.getName == name) match {
+ case Array(method) => method
+ case xs => sys.error("Internal error: eval object " + evalClass + ", " + xs.mkString("\n", "\n", ""))
}
private def compileAndSaveRun(label: String, code: String) = {
showCodeIfDebugging(code)
@@ -696,6 +735,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
/** def and val names */
def termNames = handlers flatMap (_.definesTerm)
def typeNames = handlers flatMap (_.definesType)
+ def definedOrImported = handlers flatMap (_.definedOrImported)
/** Code to import bound names from previous lines - accessPath is code to
* append to objectName to access anything bound by request.
@@ -714,6 +754,9 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
def fullFlatName(name: String) =
lineRep.readPath + accessPath.replace('.', '$') + "$" + name
+ /** The unmangled symbol name, but supplemented with line info. */
+ def disambiguated(name: Name): String = name + " (in " + lineRep + ")"
+
/** Code to access a variable with the specified name */
def fullPath(vname: Name): String = fullPath(vname.toString)
@@ -724,7 +767,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
private object ObjectSourceCode extends CodeAssembler[MemberHandler] {
val preamble = """
|object %s {
- | %s%s
+ |%s%s
""".stripMargin.format(lineRep.readName, importsPreamble, indentCode(toCompute))
val postamble = importsTrailer + "\n}"
val generate = (m: MemberHandler) => m extraCodeToEvaluate Request.this
@@ -740,9 +783,9 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
case Some(vname) if typeOf contains vname =>
"""
|lazy val $result = {
- | $export
| %s
- |}""".stripMargin.format(fullPath(vname))
+ | %s
+ |}""".stripMargin.format(lineRep.printName, fullPath(vname))
case _ => ""
}
// first line evaluates object to make sure constructor is run
@@ -750,11 +793,12 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
val preamble = """
|object %s {
| %s
- | val $export: String = %s {
+ | val %s: String = %s {
| %s
| (""
""".stripMargin.format(
- lineRep.evalName, evalResult, executionWrapper, lineRep.readName + accessPath
+ lineRep.evalName, evalResult, lineRep.printName,
+ executionWrapper, lineRep.readName + accessPath
)
val postamble = """
@@ -819,50 +863,17 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
lazy val typesOfDefinedTerms: Map[Name, Type] =
termNames map (x => x -> applyToResultMember(x, _.tpe)) toMap
- private def bindExceptionally(t: Throwable) = {
- val ex: Exceptional =
- if (isettings.showInternalStackTraces) Exceptional(t)
- else new Exceptional(t) {
- override def spanFn(frame: JavaStackFrame) = !(frame.className startsWith lineRep.evalPath)
- override def contextPrelude = super.contextPrelude + "/* The repl internal portion of the stack trace is elided. */\n"
- }
-
- quietBind("lastException", ex)
- ex.contextHead + "\n(access lastException for the full trace)"
- }
- private def bindUnexceptionally(t: Throwable) = {
- quietBind("lastException", t)
- stackTraceString(t)
- }
-
/** load and run the code using reflection */
def loadAndRun: (String, Boolean) = {
import interpreter.Line._
- def handleException(t: Throwable) = {
- /** We turn off the binding to accomodate ticket #2817 */
- withoutBindingLastException {
- val message =
- if (opt.richExes) bindExceptionally(unwrap(t))
- else bindUnexceptionally(unwrap(t))
-
- (message, false)
- }
- }
-
try {
- val execution = lineManager.set(originalLine)(lineRep call "$export")
+ val execution = lineManager.set(originalLine)(lineRep call sessionNames.print)
execution.await()
execution.state match {
case Done => ("" + execution.get(), true)
- case Threw =>
- val ex = execution.caught()
- if (isReplDebug)
- ex.printStackTrace()
-
- if (bindLastException) handleException(ex)
- else throw ex
+ case Threw => (lineRep.bindError(execution.caught()), false)
case Cancelled => ("Execution interrupted by signal.\n", false)
case Running => ("Execution still running! Seems impossible.", false)
}
@@ -886,14 +897,17 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
case _ => naming.mostRecentVar
})
- private def requestForName(name: Name): Option[Request] = {
+ def requestForName(name: Name): Option[Request] = {
assert(definedNameMap != null, "definedNameMap is null")
definedNameMap get name
}
- private def requestForIdent(line: String): Option[Request] =
+ def requestForIdent(line: String): Option[Request] =
requestForName(newTermName(line)) orElse requestForName(newTypeName(line))
+ def requestHistoryForName(name: Name): List[Request] =
+ prevRequests.toList.reverse filter (_.definedNames contains name)
+
def safeClass(name: String): Option[Symbol] = {
try Some(definitions.getClass(newTypeName(name)))
catch { case _: MissingRequirementError => None }
@@ -994,7 +1008,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
/** Secret bookcase entrance for repl debuggers: end the line
* with "// show" and see what's going on.
*/
- if (code.lines exists (_.trim endsWith "// show")) {
+ if (repllog.isTrace || (code.lines exists (_.trim endsWith "// show"))) {
echo(code)
parse(code) foreach (ts => ts foreach (t => withoutUnwrapping(repldbg(asCompactString(t)))))
}
@@ -1042,8 +1056,8 @@ object IMain {
else str
}
}
- abstract class StrippingTruncatingWriter(out: PrintWriter)
- extends PrintWriter(out)
+ abstract class StrippingTruncatingWriter(out: JPrintWriter)
+ extends JPrintWriter(out)
with StrippingWriter
with TruncatingWriter {
self =>
@@ -1057,17 +1071,6 @@ object IMain {
def isStripping = isettings.unwrapStrings
def isTruncating = reporter.truncationOK
- def stripImpl(str: String): String = {
- val cleaned = stripString(str)
- var ctrlChars = 0
- cleaned map { ch =>
- if (ch.isControl && !ch.isWhitespace) {
- ctrlChars += 1
- if (ctrlChars > 5) return "[line elided for control chars: possibly a scala signature]"
- else '?'
- }
- else ch
- }
- }
+ def stripImpl(str: String): String = naming.unmangle(str)
}
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/Logger.scala b/src/compiler/scala/tools/nsc/interpreter/Logger.scala
new file mode 100644
index 0000000000..d6cba5da6a
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/interpreter/Logger.scala
@@ -0,0 +1,18 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package interpreter
+
+trait Logger {
+ def isInfo: Boolean
+ def isDebug: Boolean
+ def isTrace: Boolean
+ def out: JPrintWriter
+
+ def info(msg: => Any): Unit = if (isInfo) out println msg
+ def debug(msg: => Any): Unit = if (isDebug) out println msg
+ def trace(msg: => Any): Unit = if (isTrace) out println msg
+}
diff --git a/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala b/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala
index 196d8b882f..188f891054 100644
--- a/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala
@@ -27,7 +27,7 @@ object ProcessResult {
}
trait LoopCommands {
- protected def out: java.io.PrintWriter
+ protected def out: JPrintWriter
// a single interpreter command
abstract class LoopCommand(val name: String, val help: String) extends (String => Result) {
diff --git a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala
index 33f21165b4..83482de449 100644
--- a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala
@@ -80,10 +80,7 @@ trait MemberHandlers {
sealed abstract class MemberHandler(val member: Tree) {
def definesImplicit = false
def definesValue = false
- def isLegalTopLevel = member match {
- case _: ModuleDef | _: ClassDef | _: Import => true
- case _ => false
- }
+ def isLegalTopLevel = false
def definesTerm = Option.empty[TermName]
def definesType = Option.empty[TypeName]
@@ -140,7 +137,7 @@ trait MemberHandlers {
/** Print out lhs instead of the generated varName */
override def resultExtractionCode(req: Request) = {
val lhsType = string2code(req lookupTypeOf name)
- val res = string2code(req fullPath name)
+ val res = string2code(req fullPath name)
""" + "%s: %s = " + %s + "\n" """.format(lhs, lhsType, res) + "\n"
}
@@ -149,6 +146,7 @@ trait MemberHandlers {
class ModuleHandler(module: ModuleDef) extends MemberDefHandler(module) {
override def definesTerm = Some(name)
override def definesValue = true
+ override def isLegalTopLevel = true
override def resultExtractionCode(req: Request) = codegenln("defined module ", name)
}
@@ -156,6 +154,7 @@ trait MemberHandlers {
class ClassHandler(member: ClassDef) extends MemberDefHandler(member) {
override def definesType = Some(name.toTypeName)
override def definesTerm = Some(name.toTermName) filter (_ => mods.isCase)
+ override def isLegalTopLevel = true
override def resultExtractionCode(req: Request) =
codegenln("defined %s %s".format(keyword, name))
@@ -172,6 +171,15 @@ trait MemberHandlers {
class ImportHandler(imp: Import) extends MemberHandler(imp) {
val Import(expr, selectors) = imp
def targetType = intp.typeOfExpression("" + expr)
+ override def isLegalTopLevel = true
+
+ def createImportForName(name: Name): String = {
+ selectors foreach {
+ case sel @ ImportSelector(old, _, `name`, _) => return "import %s.{ %s }".format(expr, sel)
+ case _ => ()
+ }
+ "import %s.%s".format(expr, name)
+ }
// TODO: Need to track these specially to honor Predef masking attempts,
// because they must be the leading imports in the code generated for each
// line. We can use the same machinery as Contexts now, anyway.
@@ -185,6 +193,9 @@ trait MemberHandlers {
/** Whether this import includes a wildcard import */
val importsWildcard = selectorWild.nonEmpty
+ /** Whether anything imported is implicit .*/
+ def importsImplicit = implicitSymbols.nonEmpty
+
def implicitSymbols = importedSymbols filter (_.isImplicit)
def importedSymbols = individualSymbols ++ wildcardSymbols
diff --git a/src/compiler/scala/tools/nsc/interpreter/Naming.scala b/src/compiler/scala/tools/nsc/interpreter/Naming.scala
index 2b9c8a954a..14322620b3 100644
--- a/src/compiler/scala/tools/nsc/interpreter/Naming.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/Naming.scala
@@ -6,13 +6,56 @@
package scala.tools.nsc
package interpreter
+/** This is for name logic which is independent of the compiler (notice there's no Global.)
+ * That includes at least generating, metaquoting, mangling, and unmangling.
+ */
trait Naming {
- val global: Global
+ def unmangle(str: String): String = {
+ val cleaned = removeIWPackages(removeLineWrapper(str))
+ var ctrlChars = 0
+ cleaned map { ch =>
+ if (ch.isControl && !ch.isWhitespace) {
+ ctrlChars += 1
+ if (ctrlChars > 5) return "[line elided for control chars: possibly a scala signature]"
+ else '?'
+ }
+ else ch
+ }
+ }
+
+ // The two name forms this is catching are the two sides of this assignment:
+ //
+ // $line3.$read.$iw.$iw.Bippy =
+ // $line3.$read$$iw$$iw$Bippy@4a6a00ca
+
+ private def noMeta(s: String) = "\\Q" + s + "\\E"
+ private lazy val lineRegex = {
+ val sn = sessionNames
+ val members = List(sn.read, sn.eval, sn.print) map noMeta mkString ("(?:", "|", ")")
+ debugging("lineRegex")(noMeta(sn.line) + """\d+[./]""" + members + """[$.]""")
+ }
+
+ private def removeLineWrapper(s: String) = s.replaceAll(lineRegex, "")
+ private def removeIWPackages(s: String) = s.replaceAll("""\$iw[$.]""", "")
+
+ trait SessionNames {
+ // All values are configurable by passing e.g. -Dscala.repl.naming.read=XXX
+ final def propOr(name: String): String = propOr(name, "$" + name)
+ final def propOr(name: String, default: String): String =
+ sys.props.getOrElse("scala.repl.naming." + name, default)
+
+ // Prefixes used in repl machinery. Default to $line, $read, etc.
+ def line = propOr("line")
+ def read = propOr("read")
+ def eval = propOr("eval")
+ def print = propOr("print")
- import global.{ Name, nme }
- import nme.{
- INTERPRETER_VAR_PREFIX, INTERPRETER_SYNTHVAR_PREFIX, INTERPRETER_LINE_PREFIX
+ // The prefix for unnamed results: by default res0, res1, etc.
+ def res = propOr("res", "res") // INTERPRETER_VAR_PREFIX
+ // Internal ones
+ def ires = propOr("ires")
}
+ lazy val sessionNames: SessionNames = new SessionNames { }
/** Generates names pre0, pre1, etc. via calls to apply method */
class NameCreator(pre: String) {
@@ -29,24 +72,21 @@ trait Naming {
(name startsWith pre) && ((name drop pre.length) forall (_.isDigit))
}
- private lazy val line = new NameCreator(INTERPRETER_LINE_PREFIX) // line name, like line$0
- private lazy val userVar = new NameCreator(INTERPRETER_VAR_PREFIX) // var name, like res0
- private lazy val internalVar = new NameCreator(INTERPRETER_SYNTHVAR_PREFIX) // internal var name, like $synthvar0
+ private lazy val userVar = new NameCreator(sessionNames.res) // var name, like res0
+ private lazy val internalVar = new NameCreator(sessionNames.ires) // internal var name, like $ires0
+ def isLineName(name: String) = (name startsWith sessionNames.line) && (name stripPrefix sessionNames.line forall (_.isDigit))
def isUserVarName(name: String) = userVar didGenerate name
def isInternalVarName(name: String): Boolean = internalVar didGenerate name
- def isInternalVarName(name: Name): Boolean = internalVar didGenerate name.toString
val freshLineId = {
var x = 0
() => { x += 1 ; x }
}
- def freshLineName() = line()
def freshUserVarName() = userVar()
def freshInternalVarName() = internalVar()
def resetAllCreators() {
- line.reset()
userVar.reset()
internalVar.reset()
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala b/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala
index 5c24e492cf..c4a77e9630 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala
@@ -6,39 +6,30 @@
package scala.tools.nsc
package interpreter
-import sys.{ Prop, BooleanProp }
-
trait ReplConfig {
- class ReplProps {
- private def bool(name: String) = BooleanProp.keyExists(name)
-
- val jlineDebug = bool("scala.tools.jline.internal.Log.debug")
- val jlineTrace = bool("scala.tools.jline.internal.Log.trace")
-
- val debug = bool("scala.repl.debug")
- val trace = bool("scala.repl.trace")
- val power = bool("scala.repl.power")
-
- val replInitCode = Prop[JFile]("scala.repl.initcode")
- val powerInitCode = Prop[JFile]("scala.repl.power.initcode")
- val powerBanner = Prop[JFile]("scala.repl.power.banner")
- }
lazy val replProps = new ReplProps
- /** Debug output */
- private[nsc] def repldbg(msg: => String) =
- if (isReplDebug) {
- try Console println msg
- catch { case x: AssertionError => Console.println("Assertion error in repldbg:\n " + x) }
+ class TapMaker[T](x: T) {
+ def tapInfo(msg: => String): T = tap(x => replinfo(parens(x)))
+ def tapDebug(msg: => String): T = tap(x => repldbg(parens(x)))
+ def tapTrace(msg: => String): T = tap(x => repltrace(parens(x)))
+ def tap[U](f: T => U): T = {
+ f(x)
+ 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) }
- }
+ private def parens(x: Any) = "(" + x + ")"
+ private def echo(msg: => String) =
+ try Console println msg
+ catch { case x: AssertionError => Console.println("Assertion error printing debugging output: " + x) }
+
+ private[nsc] def repldbg(msg: => String) = if (isReplDebug) echo(msg)
+ private[nsc] def repltrace(msg: => String) = if (isReplTrace) echo(msg)
+ private[nsc] def replinfo(msg: => String) = if (isReplInfo) echo(msg)
def isReplTrace: Boolean = replProps.trace
def isReplDebug: Boolean = replProps.debug || isReplTrace
+ def isReplInfo: Boolean = replProps.info || isReplDebug
def isReplPower: Boolean = replProps.power
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplProps.scala b/src/compiler/scala/tools/nsc/interpreter/ReplProps.scala
new file mode 100644
index 0000000000..5eb1e0ae18
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/interpreter/ReplProps.scala
@@ -0,0 +1,25 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package interpreter
+
+import scala.sys._
+
+class ReplProps {
+ private def bool(name: String) = BooleanProp.keyExists(name)
+
+ val jlineDebug = bool("scala.tools.jline.internal.Log.debug")
+ val jlineTrace = bool("scala.tools.jline.internal.Log.trace")
+
+ val info = bool("scala.repl.info")
+ val debug = bool("scala.repl.debug")
+ val trace = bool("scala.repl.trace")
+ val power = bool("scala.repl.power")
+
+ val replInitCode = Prop[JFile]("scala.repl.initcode")
+ val powerInitCode = Prop[JFile]("scala.repl.power.initcode")
+ val powerBanner = Prop[JFile]("scala.repl.power.banner")
+}
diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala b/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala
index ab0911d374..e181f98018 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala
@@ -17,6 +17,6 @@ class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, null, new
if (intp.totalSilence) ()
else super.printMessage(msg)
}
- else Console.println(msg)
+ else Console.println("[init] " + msg)
}
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala b/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala
index 5249f89dfb..992bef8056 100644
--- a/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala
@@ -6,13 +6,13 @@
package scala.tools.nsc
package interpreter
-import java.io.{ BufferedReader, PrintWriter }
+import java.io.{ BufferedReader }
import session.NoHistory
/** Reads using standard JDK API */
class SimpleReader(
in: BufferedReader,
- out: PrintWriter,
+ out: JPrintWriter,
val interactive: Boolean)
extends InteractiveReader
{
@@ -37,8 +37,8 @@ extends InteractiveReader
object SimpleReader {
def defaultIn = Console.in
- def defaultOut = new PrintWriter(Console.out)
+ def defaultOut = new JPrintWriter(Console.out)
- def apply(in: BufferedReader = defaultIn, out: PrintWriter = defaultOut, interactive: Boolean = true): SimpleReader =
+ def apply(in: BufferedReader = defaultIn, out: JPrintWriter = defaultOut, interactive: Boolean = true): SimpleReader =
new SimpleReader(in, out, interactive)
} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/interpreter/package.scala b/src/compiler/scala/tools/nsc/interpreter/package.scala
index 4309ceaa4a..e78e92c8f8 100644
--- a/src/compiler/scala/tools/nsc/interpreter/package.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/package.scala
@@ -27,14 +27,19 @@ package object interpreter extends ReplConfig with ReplStrings {
type JClass = java.lang.Class[_]
type JList[T] = java.util.List[T]
type JCollection[T] = java.util.Collection[T]
+ type JPrintWriter = java.io.PrintWriter
type InputStream = java.io.InputStream
type OutputStream = java.io.OutputStream
val IR = Results
- private[nsc] implicit def enrichClass[T](clazz: Class[T]) = new RichClass[T](clazz)
private[interpreter] implicit def javaCharSeqCollectionToScala(xs: JCollection[_ <: CharSequence]): List[String] = {
import collection.JavaConverters._
xs.asScala.toList map ("" + _)
}
+
+ private[nsc] implicit def enrichClass[T](clazz: Class[T]) = new RichClass[T](clazz)
+ private[nsc] implicit def enrichAnyRefWithTap[T](x: T) = new TapMaker(x)
+ private[nsc] def tracing[T](msg: String)(x: T): T = x.tapTrace(msg)
+ private[nsc] def debugging[T](msg: String)(x: T) = x.tapDebug(msg)
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala b/src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala
index d33658b6cd..e238bdf654 100644
--- a/src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala
@@ -69,6 +69,7 @@ trait FileBackedHistory extends JLineHistory with JPersistentHistory {
}
moveToEnd()
}
+
def flush(): Unit = ()
def purge(): Unit = historyFile.truncate()
}