diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/interpreter/ILoop.scala | 27 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/interpreter/IMain.scala | 138 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/DirectTest.scala | 12 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/ReplTest.scala | 6 | ||||
-rw-r--r-- | test/files/run/repl-exceptions.scala | 2 |
5 files changed, 101 insertions, 84 deletions
diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala index a355e19ff9..ce74b2a8c3 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala @@ -107,23 +107,24 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) class ILoopInterpreter extends IMain(settings, out) { outer => + private class ThreadStoppingLineManager extends Line.Manager(parentClassLoader) { + override def onRunaway(line: Line[_]): Unit = { + val template = """ + |// She's gone rogue, captain! Have to take her out! + |// Calling Thread.stop on runaway %s with offending code: + |// scala> %s""".stripMargin + + echo(template.format(line.thread, line.code)) + // XXX no way to suppress the deprecation warning + line.thread.stop() + in.redrawLine() + } + } override lazy val formatting = new Formatting { def prompt = ILoop.this.prompt } override protected def createLineManager(): Line.Manager = - if (ReplPropsKludge.noThreadCreation(settings)) null else new Line.Manager(parentClassLoader) { - override def onRunaway(line: Line[_]): Unit = { - val template = """ - |// She's gone rogue, captain! Have to take her out! - |// Calling Thread.stop on runaway %s with offending code: - |// scala> %s""".stripMargin - - echo(template.format(line.thread, line.code)) - // XXX no way to suppress the deprecation warning - line.thread.stop() - in.redrawLine() - } - } + new ThreadStoppingLineManager override protected def parentClassLoader = settings.explicitParentLoader.getOrElse( classOf[ILoop].getClassLoader ) diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index 43d8e4d24a..861f617ed6 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -8,6 +8,7 @@ package interpreter import Predef.{ println => _, _ } import util.{ Set => _, _ } +import java.net.URL import scala.sys.BooleanProp import io.VirtualDirectory import scala.tools.nsc.io.AbstractFile @@ -21,6 +22,19 @@ import Exceptional.unwrap import scala.collection.{ mutable, immutable } import scala.util.control.Exception.{ ultimately } import IMain._ +import java.util.concurrent.Future + +/** directory to save .class files to */ +private class ReplVirtualDirectory(out: JPrintWriter) extends VirtualDirectory("(memory)", None) { + private def pp(root: AbstractFile, indentLevel: Int) { + val spaces = " " * indentLevel + out.println(spaces + root.name) + if (root.isDirectory) + root.toList sortBy (_.name) foreach (x => pp(x, indentLevel + 1)) + } + // print the contents hierarchically + def show() = pp(this, 0) +} /** An interpreter for Scala code. * @@ -57,16 +71,32 @@ import IMain._ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends Imports { imain => + /** Leading with the eagerly evaluated. + */ + val virtualDirectory: VirtualDirectory = new ReplVirtualDirectory(out) // "directory" for classfiles private var currentSettings: Settings = initialSettings private[nsc] var printResults = true // whether to print result lines private[nsc] var totalSilence = false // whether to print anything private var _initializeComplete = false // compiler is initialized - private var _classLoader: AbstractFileClassLoader = null // active classloader - private var _isInitialized: () => Boolean = null // set up initialization future + private var _isInitialized: Future[Boolean] = null // set up initialization future private var bindExceptions = true // whether to bind the lastException variable private var _executionWrapper = "" // code to be wrapped around all lines - private var _lineManager: Line.Manager = createLineManager() + /** 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 + * through the roof if we wait for it. So we initialize it with a future and + * use a lazy val to ensure that any attempt to use the compiler object waits + * on the future. + */ + private var _classLoader: AbstractFileClassLoader = null // active classloader + private var _lineManager: Line.Manager = null // logic for individual lines + private val _compiler: Global = newCompiler(settings, reporter) // our private compiler + + def compilerClasspath: Seq[URL] = ( + if (isInitializeComplete) global.classPath.asURLs + else new PathResolver(settings).result.asURLs // the compiler's classpath + ) def settings = currentSettings def savingSettings[T](fn: Settings => Unit)(body: => T): T = { val saved = currentSettings @@ -108,29 +138,8 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends import formatting._ import reporter.{ printMessage, withoutTruncating } - /** directory to save .class files to */ - val virtualDirectory = new VirtualDirectory("(memory)", None) { - private def pp(root: io.AbstractFile, indentLevel: Int) { - val spaces = " " * indentLevel - out.println(spaces + root.name) - if (root.isDirectory) - root.toList sortBy (_.name) foreach (x => pp(x, indentLevel + 1)) - } - // print the contents hierarchically - def show() = pp(this, 0) - } - // This exists mostly because using the reporter too early leads to deadlock. private def echo(msg: String) { Console println msg } - - /** 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 - * through the roof if we wait for it. So we initialize it with a future and - * use a lazy val to ensure that any attempt to use the compiler object waits - * on the future. - */ - private val _compiler: Global = newCompiler(settings, reporter) private def _initSources = List(new BatchSourceFile("<init>", "class $repl_$init { }")) private def _initialize() = { try { @@ -141,13 +150,15 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends catch AbstractOrMissingHandler() } // argument is a thunk to execute after init is done - def initialize(postInitSignal: => Unit): Unit = synchronized { - if (_isInitialized == null) - _isInitialized = scala.concurrent.ops future { - val result = _initialize() - postInitSignal - result + def initialize(postInitSignal: => Unit) { + synchronized { + if (_isInitialized == null) { + _isInitialized = io.spawn { + try _initialize() + finally postInitSignal + } } + } } def initializeSynchronous(): Unit = { if (!isInitializeComplete) { @@ -167,7 +178,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends initialize(()) } // blocks until it is ; false means catastrophic failure - if (_isInitialized()) _compiler + if (_isInitialized.get()) _compiler else null } } @@ -241,17 +252,14 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends def executionWrapper = _executionWrapper def setExecutionWrapper(code: String) = _executionWrapper = code def clearExecutionWrapper() = _executionWrapper = "" - def lineManager = _lineManager /** interpreter settings */ lazy val isettings = new ISettings(this) /** Create a line manager. Overridable. */ - protected def createLineManager(): Line.Manager = - createLineManager(classLoader) - protected def createLineManager(loader: ClassLoader): Line.Manager = - if (ReplPropsKludge.noThreadCreation(settings)) null else new Line.Manager(loader) + protected def noLineManager = ReplPropsKludge.noThreadCreation(settings) + protected def createLineManager(): Line.Manager = new Line.Manager(_classLoader) /** Instantiate a compiler. Overridable. */ protected def newCompiler(settings: Settings, reporter: Reporter) = { @@ -265,9 +273,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends protected def parentClassLoader: ClassLoader = settings.explicitParentLoader.getOrElse( this.getClass.getClassLoader() ) - /** the compiler's classpath, as URL's */ - lazy val compilerClasspath = global.classPath.asURLs - /* A single class loader is used for all commands interpreted by this Interpreter. It would also be possible to create a new class loader for each command to interpret. The advantages of the current approach are: @@ -283,39 +288,42 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends */ def resetClassLoader() = { repldbg("Setting new classloader: was " + _classLoader) - _classLoader = makeClassLoader() - _lineManager = createLineManager(_classLoader) + _classLoader = null + ensureClassLoader() + } + final def ensureClassLoader() { + if (_classLoader == null) { + _classLoader = makeClassLoader() + _lineManager = if (noLineManager) null else createLineManager() + } } def classLoader: AbstractFileClassLoader = { - if (_classLoader == null) - resetClassLoader() - + ensureClassLoader() _classLoader } - private def makeClassLoader(): AbstractFileClassLoader = { - val parent = - if (parentClassLoader == null) ScalaClassLoader fromURLs compilerClasspath - else new URLClassLoader(compilerClasspath, parentClassLoader) - - new AbstractFileClassLoader(virtualDirectory, parent) { - private[IMain] var traceClassLoading = isReplTrace - override protected def trace = traceClassLoading - - /** Overridden here to try translating a simple name to the generated - * class name if the original attempt fails. This method is used by - * getResourceAsStream as well as findClass. - */ - override protected def findAbstractFile(name: String): AbstractFile = { - super.findAbstractFile(name) match { - // deadlocks on startup if we try to translate names too early - case null if isInitializeComplete => - generatedName(name) map (x => super.findAbstractFile(x)) orNull - case file => - file - } + private class TranslatingClassLoader(parent: ClassLoader) extends AbstractFileClassLoader(virtualDirectory, parent) { + private[IMain] var traceClassLoading = isReplTrace + override protected def trace = super.trace || traceClassLoading + + /** Overridden here to try translating a simple name to the generated + * class name if the original attempt fails. This method is used by + * getResourceAsStream as well as findClass. + */ + override protected def findAbstractFile(name: String): AbstractFile = { + super.findAbstractFile(name) match { + // deadlocks on startup if we try to translate names too early + case null if isInitializeComplete => + generatedName(name) map (x => super.findAbstractFile(x)) orNull + case file => + file } } } + private def makeClassLoader(): AbstractFileClassLoader = + new TranslatingClassLoader(parentClassLoader match { + case null => ScalaClassLoader fromURLs compilerClasspath + case p => new URLClassLoader(compilerClasspath, p) + }) def getInterpreterClassLoader() = classLoader diff --git a/src/partest/scala/tools/partest/DirectTest.scala b/src/partest/scala/tools/partest/DirectTest.scala index e6e817f96d..6c210f1ac1 100644 --- a/src/partest/scala/tools/partest/DirectTest.scala +++ b/src/partest/scala/tools/partest/DirectTest.scala @@ -24,13 +24,15 @@ abstract class DirectTest extends App { def testOutput = io.Directory(sys.props("partest.output")) // override to add additional settings with strings - def extraSettings: String = "" + def extraSettings = "" // a default Settings object def settings: Settings = newSettings(extraSettings) // a custom Settings object def newSettings(argString: String) = { val s = new Settings - s processArgumentString (argString + " " + debugSettings) + val args = argString + " " + debugSettings + log("newSettings: args = '" + args + "'") + s processArgumentString args s } // compile the code, optionally first adding to the settings @@ -46,8 +48,10 @@ abstract class DirectTest extends App { catch { case t => println(t) ; sys.exit(1) } /** Debugger interest only below this line **/ - protected val isDebug = (sys.props contains "partest.debug") || (sys.env contains "PARTEST_DEBUG") + protected def isDebug = (sys.props contains "partest.debug") || (sys.env contains "PARTEST_DEBUG") protected def debugSettings = sys.props.getOrElse("partest.debug.settings", "") - final def log(msg: => Any) { if (isDebug) Console println msg } + final def log(msg: => Any) { + if (isDebug) Console.err println msg + } } diff --git a/src/partest/scala/tools/partest/ReplTest.scala b/src/partest/scala/tools/partest/ReplTest.scala index 02cf61902a..9c45df9ec5 100644 --- a/src/partest/scala/tools/partest/ReplTest.scala +++ b/src/partest/scala/tools/partest/ReplTest.scala @@ -22,6 +22,10 @@ abstract class ReplTest extends DirectTest { s.Xnojline.value = true transformSettings(s) } - def eval() = ILoop.runForTranscript(code, settings).lines drop 1 + def eval() = { + val s = settings + log("eval(): settings = " + s) + ILoop.runForTranscript(code, s).lines drop 1 + } def show() = eval() foreach println } diff --git a/test/files/run/repl-exceptions.scala b/test/files/run/repl-exceptions.scala index aaf37c8ae7..301f3595d0 100644 --- a/test/files/run/repl-exceptions.scala +++ b/test/files/run/repl-exceptions.scala @@ -2,7 +2,7 @@ import scala.tools.partest.ReplTest import scala.tools.util.Javap object Test extends ReplTest { - override def extraSettings = "-Yrich-exceptions" + override def extraSettings = "-Yrich-exceptions -Yrepl-sync" def code = """ |sys.SystemProperties.traceSourcePath setValue "" |def f = sys.error("hi mom") |