diff --git a/compiler/src/dotty/tools/dotc/core/MacroClassLoader.scala b/compiler/src/dotty/tools/dotc/core/MacroClassLoader.scala index 896da4044b42..e56835d59c54 100644 --- a/compiler/src/dotty/tools/dotc/core/MacroClassLoader.scala +++ b/compiler/src/dotty/tools/dotc/core/MacroClassLoader.scala @@ -23,7 +23,7 @@ object MacroClassLoader { private def makeMacroClassLoader(using Context): ClassLoader = trace("new macro class loader") { val entries = ClassPath.expandPath(ctx.settings.classpath.value, expandStar=true) val urls = entries.map(cp => java.nio.file.Paths.get(cp).toUri.toURL).toArray - val out = ctx.settings.outputDir.value.jpath.toUri.toURL // to find classes in case of suspended compilation - new java.net.URLClassLoader(urls :+ out, getClass.getClassLoader) + val out = Option(ctx.settings.outputDir.value.toURL) // to find classes in case of suspended compilation + new java.net.URLClassLoader(urls ++ out.toList, getClass.getClassLoader) } } diff --git a/compiler/src/dotty/tools/repl/AbstractFileClassLoader.scala b/compiler/src/dotty/tools/repl/AbstractFileClassLoader.scala index a3f4047fb646..7263758b60c5 100644 --- a/compiler/src/dotty/tools/repl/AbstractFileClassLoader.scala +++ b/compiler/src/dotty/tools/repl/AbstractFileClassLoader.scala @@ -18,7 +18,7 @@ import io.AbstractFile import java.net.{URL, URLConnection, URLStreamHandler} import java.util.Collections -class AbstractFileClassLoader(root: AbstractFile, parent: ClassLoader) extends ClassLoader(parent): +class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) extends ClassLoader(parent): private def findAbstractFile(name: String) = root.lookupPath(name.split('/').toIndexedSeq, directory = false) override protected def findResource(name: String) = diff --git a/compiler/src/dotty/tools/repl/ParseResult.scala b/compiler/src/dotty/tools/repl/ParseResult.scala index c8d36e5c889f..bc0305c17944 100644 --- a/compiler/src/dotty/tools/repl/ParseResult.scala +++ b/compiler/src/dotty/tools/repl/ParseResult.scala @@ -80,6 +80,11 @@ case object Imports extends Command { val command: String = ":imports" } +case class Settings(arg: String) extends Command +object Settings { + val command: String = ":settings" +} + /** Reset the session to the initial state from when the repl program was * started */ @@ -106,6 +111,7 @@ case object Help extends Command { |:doc print the documentation for the given expression |:imports show import history |:reset reset the repl to its initial state, forgetting all session entries + |:settings update compiler options, if possible """.stripMargin } @@ -128,7 +134,8 @@ object ParseResult { Imports.command -> (_ => Imports), Load.command -> (arg => Load(arg)), TypeOf.command -> (arg => TypeOf(arg)), - DocOf.command -> (arg => DocOf(arg)) + DocOf.command -> (arg => DocOf(arg)), + Settings.command -> (arg => Settings(arg)), ) def apply(source: SourceFile)(implicit state: State): ParseResult = { diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index 085df909d71e..435d1266e3a5 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -31,16 +31,16 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None) { private val MaxStringElements: Int = 1000 // no need to mkString billions of elements - private var myClassLoader: ClassLoader = _ + private var myClassLoader: AbstractFileClassLoader = _ private var myReplStringOf: Object => String = _ /** Class loader used to load compiled code */ private[repl] def classLoader()(using Context) = - if (myClassLoader != null) myClassLoader + if (myClassLoader != null && myClassLoader.root == ctx.settings.outputDir.value) myClassLoader else { - val parent = parentClassLoader.getOrElse { + val parent = Option(myClassLoader).orElse(parentClassLoader).getOrElse { val compilerClasspath = ctx.platform.classPath(using ctx).asURLs // We can't use the system classloader as a parent because it would // pollute the user classpath with everything passed to the JVM diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 40876b260394..ceeceb12f669 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -5,8 +5,10 @@ import java.nio.charset.StandardCharsets import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.ast.{tpd, untpd} +import dotty.tools.dotc.config.CommandLineParser.tokenize import dotty.tools.dotc.config.Properties.{javaVersion, javaVmName, simpleVersionString} import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.Phases.{unfusedPhases, typerPhase} import dotty.tools.dotc.core.Denotations.Denotation import dotty.tools.dotc.core.Flags._ @@ -414,6 +416,23 @@ class ReplDriver(settings: Array[String], } state + case Settings(arg) => arg match + case "" => + given ctx: Context = state.context + for (s <- ctx.settings.userSetSettings(ctx.settingsState).sortBy(_.name)) + out.println(s"${s.name} = ${if s.value == "" then "\"\"" else s.value}") + state + case _ => + setup(tokenize(arg).toArray, rootCtx) match + case Some((files, ictx)) => + inContext(ictx) { + if files.nonEmpty then out.println(i"Ignoring spurious arguments: $files%, %") + ictx.base.initialize()(using ictx) + rootCtx = ictx + } + case _ => + state.copy(context = rootCtx) + case Quit => // end of the world! state diff --git a/compiler/test-resources/repl/settings-command b/compiler/test-resources/repl/settings-command new file mode 100644 index 000000000000..db4b1af7343f --- /dev/null +++ b/compiler/test-resources/repl/settings-command @@ -0,0 +1,16 @@ +scala> def f(thread: Thread) = thread.stop() +there were 1 deprecation warning(s); re-run with -deprecation for details +def f(thread: Thread): Unit + +scala>:settings -deprecation foo.scala +Ignoring spurious arguments: foo.scala + +scala> def f(thread: Thread) = thread.stop() +1 warning found +-- Deprecation Warning: -------------------------------------------------------- +1 | def f(thread: Thread) = thread.stop() + | ^^^^^^^^^^^ + |method stop in class Thread is deprecated since : see corresponding Javadoc for more information. +def f(thread: Thread): Unit + +scala> diff --git a/compiler/test-resources/repl/settings-outputDir b/compiler/test-resources/repl/settings-outputDir new file mode 100644 index 000000000000..8f0460a30cfe --- /dev/null +++ b/compiler/test-resources/repl/settings-outputDir @@ -0,0 +1,18 @@ +scala> java.nio.file.Files.exists(java.nio.file.Files.createDirectories(java.nio.file.Paths.get("target", "test-repl-settings-outDir"))) +val res0: Boolean = true + +scala> val x = 1 +val x: Int = 1 + +scala>:settings -d target/test-repl-settings-outDir + +scala> val y = 2 +val y: Int = 2 + +scala> x +val res1: Int = 1 + +scala> y +val res2: Int = 2 + +scala> java.nio.file.Files.walk(java.nio.file.Paths.get("target", "test-repl-settings-outDir")).sorted(java.util.Comparator.reverseOrder).forEach(java.nio.file.Files.delete)