From f8cd3ba2dd9f03c061c94b370df3623eca16c2e5 Mon Sep 17 00:00:00 2001 From: aherlihy Date: Thu, 9 Jan 2025 15:12:03 -0500 Subject: [PATCH 01/18] Skeleton :require --- compiler/src/dotty/tools/repl/ParseResult.scala | 8 ++++++++ compiler/src/dotty/tools/repl/ReplDriver.scala | 12 ++++++++++++ .../test/dotty/tools/repl/TabcompleteTests.scala | 1 + 3 files changed, 21 insertions(+) diff --git a/compiler/src/dotty/tools/repl/ParseResult.scala b/compiler/src/dotty/tools/repl/ParseResult.scala index 2674a385a10c..5a43eaa5c8ab 100644 --- a/compiler/src/dotty/tools/repl/ParseResult.scala +++ b/compiler/src/dotty/tools/repl/ParseResult.scala @@ -52,6 +52,13 @@ object Load { val command: String = ":load" } +/** `:require ` adds a jar to the classpath + */ +case class Require(path: String) extends Command +object Require { + val command: String = ":require" +} + /** `:kind ` display the kind of a type. see also :help kind */ case class KindOf(expr: String) extends Command @@ -147,6 +154,7 @@ object ParseResult { Imports.command -> (_ => Imports), KindOf.command -> (arg => KindOf(arg)), Load.command -> (arg => Load(arg)), + Require.command -> (arg => Require(arg)), TypeOf.command -> (arg => TypeOf(arg)), DocOf.command -> (arg => DocOf(arg)), Settings.command -> (arg => Settings(arg)), diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 0f2921fd736c..0055a59ae67d 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -510,6 +510,18 @@ class ReplDriver(settings: Array[String], state } + case Require(path) => + val file = new JFile(path) + if (file.exists) { +// val contents = Using(scala.io.Source.fromFile(file, StandardCharsets.UTF_8.name))(_.mkString).get +// run(contents) + ??? + } + else { + out.println(s"""Couldn't find file "${file.getCanonicalPath}"""") + state + } + case KindOf(expr) => out.println(s"""The :kind command is not currently supported.""") state diff --git a/compiler/test/dotty/tools/repl/TabcompleteTests.scala b/compiler/test/dotty/tools/repl/TabcompleteTests.scala index 66c0fd8a9ce7..efd13db1cc80 100644 --- a/compiler/test/dotty/tools/repl/TabcompleteTests.scala +++ b/compiler/test/dotty/tools/repl/TabcompleteTests.scala @@ -215,6 +215,7 @@ class TabcompleteTests extends ReplTest { ":imports", ":kind", ":load", + ":require", ":quit", ":reset", ":settings", From d528014801d4c1a31f1b21393b143545bfdf7da0 Mon Sep 17 00:00:00 2001 From: aherlihy Date: Thu, 9 Jan 2025 17:11:09 -0500 Subject: [PATCH 02/18] find class names, need to add URL to loader --- .../src/dotty/tools/repl/ParseResult.scala | 2 +- .../src/dotty/tools/repl/ReplDriver.scala | 43 +++++++++++++++---- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/repl/ParseResult.scala b/compiler/src/dotty/tools/repl/ParseResult.scala index 5a43eaa5c8ab..f61d2be5274d 100644 --- a/compiler/src/dotty/tools/repl/ParseResult.scala +++ b/compiler/src/dotty/tools/repl/ParseResult.scala @@ -52,7 +52,7 @@ object Load { val command: String = ":load" } -/** `:require ` adds a jar to the classpath +/** `:require ` adds a jar to the classpath */ case class Require(path: String) extends Command object Require { diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 0055a59ae67d..80647c0245e1 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -39,6 +39,7 @@ import scala.annotation.tailrec import scala.collection.mutable import scala.compiletime.uninitialized import scala.jdk.CollectionConverters.* +import scala.tools.asm.ClassReader import scala.util.control.NonFatal import scala.util.Using @@ -511,16 +512,40 @@ class ReplDriver(settings: Array[String], } case Require(path) => - val file = new JFile(path) - if (file.exists) { -// val contents = Using(scala.io.Source.fromFile(file, StandardCharsets.UTF_8.name))(_.mkString).get -// run(contents) - ??? - } - else { - out.println(s"""Couldn't find file "${file.getCanonicalPath}"""") + val f = new JFile(path) + val jarFile = AbstractFile.getDirectory(path) + if (!f.exists || jarFile == null) + out.println(s"""Cannot add "$path" to classpath.""") + state + else + def flatten(f: AbstractFile): Iterator[AbstractFile] = + if (f.isClassContainer) f.iterator.flatMap(flatten) + else Iterator(f) + + val entries = flatten(jarFile) + + def classNameOf(classFile: AbstractFile): String = { + val input = classFile.input + try { + val reader = new ClassReader(input) + reader.getClassName.replace('/', '.') + } finally { + input.close() + } + } + + val clsl = rendering.classLoader()(using state.context) + +// def alreadyDefined(clsName: String) = classLoader.tryToLoadClass(clsName).isDefined +// val existingClass = entries.filter(_.ext.isClass).map(classNameOf).find(alreadyDefined) + +// if (existingClass.nonEmpty) out.println(s"The path '$f' cannot be loaded, it contains a classfile that already exists on the classpath: ${existingClass.get}") +// else { +// intp.addUrlsToClassPath(f.toURI.toURL) +// out.println(s"Added '$path' to classpath.") +// out.println("Added '%s'. Your new classpath is:\n\"%s\"".format(f.path, intp.classPathString)) +// } state - } case KindOf(expr) => out.println(s"""The :kind command is not currently supported.""") From f4968e795b31970a0fc7f553a8f96ceebaaa482b Mon Sep 17 00:00:00 2001 From: aherlihy Date: Thu, 9 Jan 2025 17:42:09 -0500 Subject: [PATCH 03/18] Attempt to update class loader, not successful --- compiler/src/dotty/tools/repl/ReplDriver.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 80647c0245e1..f74b73fd480f 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -534,8 +534,15 @@ class ReplDriver(settings: Array[String], } } + // TODO: no idea how to access and reload the class paths + val newClassPath = state.context.platform.classPath(using state.context).asURLs :+ f.toURI.toURL + println(s"new class path=${newClassPath.mkString(", ")}") val clsl = rendering.classLoader()(using state.context) + val newClsl = fromURLsParallelCapable(newClassPath, clsl) + println(s"newClsl getResource=${newClsl.getURLs.toList}") + newClsl.asContext(state.context) +// Scala 2: // def alreadyDefined(clsName: String) = classLoader.tryToLoadClass(clsName).isDefined // val existingClass = entries.filter(_.ext.isClass).map(classNameOf).find(alreadyDefined) From b8034c231de2e95fa4e0c9dfb08d991af05d30d4 Mon Sep 17 00:00:00 2001 From: aherlihy Date: Fri, 10 Jan 2025 17:41:03 -0500 Subject: [PATCH 04/18] Add updateClassPath + reset rootctx --- .../tools/dotc/config/JavaPlatform.scala | 6 +++ .../dotty/tools/dotc/config/Platform.scala | 1 + .../src/dotty/tools/repl/ReplDriver.scala | 53 +++++++++++-------- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala index f3c2f295ce82..686c49be879f 100644 --- a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala +++ b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala @@ -27,6 +27,12 @@ class JavaPlatform extends Platform { case _ => false }) + def addToClassPath(cPath: ClassPath): Unit = currentClassPath.get match { + case AggregateClassPath(entries) => + currentClassPath = Some(AggregateClassPath(entries :+ cPath)) + case cp: ClassPath => + currentClassPath = Some(AggregateClassPath(cp :: cPath :: Nil)) + } /** Update classpath with a substituted subentry */ def updateClassPath(subst: Map[ClassPath, ClassPath]): Unit = currentClassPath.get match { case AggregateClassPath(entries) => diff --git a/compiler/src/dotty/tools/dotc/config/Platform.scala b/compiler/src/dotty/tools/dotc/config/Platform.scala index 2a0b207e68c1..d12d1187bcb0 100644 --- a/compiler/src/dotty/tools/dotc/config/Platform.scala +++ b/compiler/src/dotty/tools/dotc/config/Platform.scala @@ -20,6 +20,7 @@ abstract class Platform { /** Update classpath with a substitution that maps entries to entries */ def updateClassPath(subst: Map[ClassPath, ClassPath]): Unit + def addToClassPath(cPath: ClassPath): Unit /** Any platform-specific phases. */ //def platformPhases: List[SubComponent] diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index f74b73fd480f..b9d2ed4c4b81 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -1,17 +1,16 @@ package dotty.tools.repl import scala.language.unsafeNulls - -import java.io.{File => JFile, PrintStream} +import java.io.{PrintStream, File as JFile} import java.nio.charset.StandardCharsets - import dotty.tools.dotc.ast.Trees.* import dotty.tools.dotc.ast.{tpd, untpd} +import dotty.tools.dotc.classpath.{AggregateClassPath, ClassPathFactory, ZipAndJarClassPathFactory} 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.Phases.{typerPhase, unfusedPhases} import dotty.tools.dotc.core.Denotations.Denotation import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.Mode @@ -534,24 +533,36 @@ class ReplDriver(settings: Array[String], } } - // TODO: no idea how to access and reload the class paths - val newClassPath = state.context.platform.classPath(using state.context).asURLs :+ f.toURI.toURL - println(s"new class path=${newClassPath.mkString(", ")}") - val clsl = rendering.classLoader()(using state.context) - val newClsl = fromURLsParallelCapable(newClassPath, clsl) - println(s"newClsl getResource=${newClsl.getURLs.toList}") - newClsl.asContext(state.context) - -// Scala 2: -// def alreadyDefined(clsName: String) = classLoader.tryToLoadClass(clsName).isDefined -// val existingClass = entries.filter(_.ext.isClass).map(classNameOf).find(alreadyDefined) - -// if (existingClass.nonEmpty) out.println(s"The path '$f' cannot be loaded, it contains a classfile that already exists on the classpath: ${existingClass.get}") -// else { -// intp.addUrlsToClassPath(f.toURI.toURL) + def alreadyDefined(clsName: String) = state.context.platform.classPath(using state.context).findClassFile(clsName).isDefined + val existingClass = entries.filter(_.ext.isClass).map(classNameOf).find(alreadyDefined) + if (existingClass.nonEmpty) + out.println(s"The path '$f' cannot be loaded, it contains a classfile that already exists on the classpath: ${existingClass.get}") + else +// val cp = state.context.platform.classPath(using state.context).asClassPathString +// println(s"CURRENT CP STRING: $cp") +// val newCP = s"$cp${JFile.pathSeparator}$path" +// println(s"UPDATED CP: $newCP") + + // add to compiler class path + println(s"INIT state classPath = ${state.context.platform.classPath(using state.context).asClassPathString}") + val cpCP = ClassPathFactory.newClassPath(jarFile)(using state.context) + state.context.platform.addToClassPath(cpCP) + println(s"classPath after add = ${state.context.platform.classPath(using state.context).asClassPathString}") + + // create initial context + rootCtx = setupRootCtx(Array(), rootCtx) + state.copy(context = rootCtx) + + + // new class loader +// val newClassPath = state.context.platform.classPath(using state.context).asURLs :+ f.toURI.toURL +// val oldCL = rendering.classLoader()(using state.context) +// val newCL = fromURLsParallelCapable(newClassPath, oldCL) +// println(s"new CL class path = ${newCL.getURLs.toList}") +// println(s"\nclass name = ${cpCP.className}") +// rendering.myClassLoader = new AbstractFileClassLoader(state.context.settings.outputDir.default, newCL) // out.println(s"Added '$path' to classpath.") -// out.println("Added '%s'. Your new classpath is:\n\"%s\"".format(f.path, intp.classPathString)) -// } + println(s"after setupRootCtx classPath = ${state.context.platform.classPath(using state.context).asClassPathString}") state case KindOf(expr) => From 800356babcdeaf99ff599a1f1c021417cfdfb517 Mon Sep 17 00:00:00 2001 From: aherlihy Date: Fri, 10 Jan 2025 17:58:38 -0500 Subject: [PATCH 05/18] updated state with classpath --- .../src/dotty/tools/repl/ReplDriver.scala | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index b9d2ed4c4b81..51502fd7c0ac 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -537,21 +537,23 @@ class ReplDriver(settings: Array[String], val existingClass = entries.filter(_.ext.isClass).map(classNameOf).find(alreadyDefined) if (existingClass.nonEmpty) out.println(s"The path '$f' cannot be loaded, it contains a classfile that already exists on the classpath: ${existingClass.get}") + state else -// val cp = state.context.platform.classPath(using state.context).asClassPathString + val cp = state.context.platform.classPath(using state.context).asClassPathString // println(s"CURRENT CP STRING: $cp") -// val newCP = s"$cp${JFile.pathSeparator}$path" -// println(s"UPDATED CP: $newCP") + val newCP = s"$cp${JFile.pathSeparator}$path" + println(s"UPDATED CP: $newCP") // add to compiler class path - println(s"INIT state classPath = ${state.context.platform.classPath(using state.context).asClassPathString}") - val cpCP = ClassPathFactory.newClassPath(jarFile)(using state.context) - state.context.platform.addToClassPath(cpCP) - println(s"classPath after add = ${state.context.platform.classPath(using state.context).asClassPathString}") +// println(s"INIT state classPath = ${state.context.platform.classPath(using state.context).asClassPathString}") +// val cpCP = ClassPathFactory.newClassPath(jarFile)(using state.context) +// state.context.platform.addToClassPath(cpCP) +// println(s"classPath after add = ${state.context.platform.classPath(using state.context).asClassPathString}") - // create initial context - rootCtx = setupRootCtx(Array(), rootCtx) - state.copy(context = rootCtx) + // recreate initial context + rootCtx = setupRootCtx(Array(), rootCtx.fresh.setSetting(rootCtx.settings.classpath, newCP)) + val s = state.copy(context = rootCtx) + println(s"after setupRootCtx classPath = ${s.context.platform.classPath(using s.context).asClassPathString}") // new class loader @@ -562,13 +564,11 @@ class ReplDriver(settings: Array[String], // println(s"\nclass name = ${cpCP.className}") // rendering.myClassLoader = new AbstractFileClassLoader(state.context.settings.outputDir.default, newCL) // out.println(s"Added '$path' to classpath.") - println(s"after setupRootCtx classPath = ${state.context.platform.classPath(using state.context).asClassPathString}") - state + s case KindOf(expr) => out.println(s"""The :kind command is not currently supported.""") state - case TypeOf(expr) => expr match { case "" => out.println(s":type ") From f79588b5e55a499ab5c9b50d6137605c5fa4d914 Mon Sep 17 00:00:00 2001 From: aherlihy Date: Mon, 13 Jan 2025 10:37:36 -0500 Subject: [PATCH 06/18] fix test order --- compiler/test/dotty/tools/repl/TabcompleteTests.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/test/dotty/tools/repl/TabcompleteTests.scala b/compiler/test/dotty/tools/repl/TabcompleteTests.scala index efd13db1cc80..328725cf411a 100644 --- a/compiler/test/dotty/tools/repl/TabcompleteTests.scala +++ b/compiler/test/dotty/tools/repl/TabcompleteTests.scala @@ -215,8 +215,8 @@ class TabcompleteTests extends ReplTest { ":imports", ":kind", ":load", - ":require", ":quit", + ":require", ":reset", ":settings", ":silent", From 0fad93965cce76c2c54367430a37ca631ad94432 Mon Sep 17 00:00:00 2001 From: aherlihy Date: Mon, 13 Jan 2025 11:35:40 -0500 Subject: [PATCH 07/18] working but state lost --- compiler/src/dotty/tools/repl/ReplDriver.scala | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 51502fd7c0ac..771da255003f 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -551,18 +551,14 @@ class ReplDriver(settings: Array[String], // println(s"classPath after add = ${state.context.platform.classPath(using state.context).asClassPathString}") // recreate initial context - rootCtx = setupRootCtx(Array(), rootCtx.fresh.setSetting(rootCtx.settings.classpath, newCP)) + resetToInitial(List("-classpath", newCP)) +// rootCtx = setupRootCtx(Array(), rootCtx.fresh.setSetting(rootCtx.settings.classpath, newCP)) val s = state.copy(context = rootCtx) - println(s"after setupRootCtx classPath = ${s.context.platform.classPath(using s.context).asClassPathString}") - // new class loader -// val newClassPath = state.context.platform.classPath(using state.context).asURLs :+ f.toURI.toURL -// val oldCL = rendering.classLoader()(using state.context) -// val newCL = fromURLsParallelCapable(newClassPath, oldCL) -// println(s"new CL class path = ${newCL.getURLs.toList}") -// println(s"\nclass name = ${cpCP.className}") -// rendering.myClassLoader = new AbstractFileClassLoader(state.context.settings.outputDir.default, newCL) + val oldCL = rendering.classLoader()(using state.context) + val newCL = fromURLsParallelCapable(s.context.platform.classPath(using s.context).asURLs, oldCL) + rendering.myClassLoader = new AbstractFileClassLoader(state.context.settings.outputDir.default, newCL) // out.println(s"Added '$path' to classpath.") s From 7b0ba9f4df0ff9c84deeb854876a8b15cb4dbb5f Mon Sep 17 00:00:00 2001 From: aherlihy Date: Wed, 15 Jan 2025 15:36:31 -0500 Subject: [PATCH 08/18] Clean up impl --- .../dotc/classpath/AggregateClassPath.scala | 6 ++- .../tools/dotc/config/JavaPlatform.scala | 4 +- .../dotty/tools/dotc/config/Platform.scala | 6 ++- .../src/dotty/tools/dotc/core/Contexts.scala | 6 ++- .../dotty/tools/dotc/core/Definitions.scala | 2 +- .../src/dotty/tools/repl/ReplDriver.scala | 54 ++++++++++--------- 6 files changed, 46 insertions(+), 32 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala b/compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala index cd44ba27df96..c7508e14a004 100644 --- a/compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala +++ b/compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala @@ -33,7 +33,11 @@ case class AggregateClassPath(aggregates: Seq[ClassPath]) extends ClassPath { packageIndex.getOrElseUpdate(pkg.dottedString, aggregates.filter(_.hasPackage(pkg))) } - override def asURLs: Seq[URL] = aggregates.flatMap(_.asURLs) + override def asURLs: Seq[URL] = + aggregates.flatMap { + case v: VirtualDirectoryClassPath => Seq() + case a => a.asURLs + } override def asClassPathStrings: Seq[String] = aggregates.map(_.asClassPathString).distinct diff --git a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala index 686c49be879f..e923ee1cc053 100644 --- a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala +++ b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala @@ -27,14 +27,14 @@ class JavaPlatform extends Platform { case _ => false }) - def addToClassPath(cPath: ClassPath): Unit = currentClassPath.get match { + def addToClassPath(cPath: ClassPath)(using Context): Unit = classPath match { case AggregateClassPath(entries) => currentClassPath = Some(AggregateClassPath(entries :+ cPath)) case cp: ClassPath => currentClassPath = Some(AggregateClassPath(cp :: cPath :: Nil)) } /** Update classpath with a substituted subentry */ - def updateClassPath(subst: Map[ClassPath, ClassPath]): Unit = currentClassPath.get match { + def updateClassPath(subst: Map[ClassPath, ClassPath])(using Context): Unit = classPath match { case AggregateClassPath(entries) => currentClassPath = Some(AggregateClassPath(entries map (e => subst.getOrElse(e, e)))) case cp: ClassPath => diff --git a/compiler/src/dotty/tools/dotc/config/Platform.scala b/compiler/src/dotty/tools/dotc/config/Platform.scala index d12d1187bcb0..750e2fe5b203 100644 --- a/compiler/src/dotty/tools/dotc/config/Platform.scala +++ b/compiler/src/dotty/tools/dotc/config/Platform.scala @@ -19,8 +19,10 @@ abstract class Platform { def classPath(using Context): ClassPath /** Update classpath with a substitution that maps entries to entries */ - def updateClassPath(subst: Map[ClassPath, ClassPath]): Unit - def addToClassPath(cPath: ClassPath): Unit + def updateClassPath(subst: Map[ClassPath, ClassPath])(using Context): Unit + + /** Add new entry to classpath */ + def addToClassPath(cPath: ClassPath)(using Context): Unit /** Any platform-specific phases. */ //def platformPhases: List[SubComponent] diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index e12ab1cc2da2..5e95b4f8717a 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -36,6 +36,7 @@ import scala.annotation.internal.sharable import DenotTransformers.DenotTransformer import dotty.tools.dotc.profile.Profiler import dotty.tools.dotc.sbt.interfaces.{IncrementalCallback, ProgressCallback} +import dotty.tools.dotc.classpath.ClassPathFactory import util.Property.Key import util.Store import plugins.* @@ -915,8 +916,11 @@ object Contexts { /** Initializes the `ContextBase` with a starting context. * This initializes the `platform` and the `definitions`. */ - def initialize()(using Context): Unit = { + def initialize(previousOutputDir: Option[AbstractFile] = None)(using Context): Unit = { _platform = newPlatform + previousOutputDir.foreach(cp => + _platform.nn.addToClassPath(ClassPathFactory.newClassPath(cp)) + ) definitions.init() } diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 6e2e924edf65..63f4cd743e8f 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -2233,7 +2233,7 @@ class Definitions { private var isInitialized = false - def init()(using Context): Unit = { + def init(require: Boolean = false)(using Context): Unit = { this.initCtx = ctx if (!isInitialized) { // force initialization of every symbol that is synthesized or hijacked by the compiler diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 771da255003f..a1c64fcbc8c9 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -1,16 +1,16 @@ package dotty.tools.repl import scala.language.unsafeNulls -import java.io.{PrintStream, File as JFile} +import java.io.{File => JFile, PrintStream} import java.nio.charset.StandardCharsets import dotty.tools.dotc.ast.Trees.* import dotty.tools.dotc.ast.{tpd, untpd} -import dotty.tools.dotc.classpath.{AggregateClassPath, ClassPathFactory, ZipAndJarClassPathFactory} +import dotty.tools.dotc.classpath.ClassPathFactory 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.{typerPhase, unfusedPhases} +import dotty.tools.dotc.core.Phases.{unfusedPhases, typerPhase} import dotty.tools.dotc.core.Denotations.Denotation import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.Mode @@ -94,7 +94,7 @@ class ReplDriver(settings: Array[String], initCtx.settings.YwithBestEffortTasty.name ) - private def setupRootCtx(settings: Array[String], rootCtx: Context) = { + private def setupRootCtx(settings: Array[String], rootCtx: Context, previousOutputDir: Option[AbstractFile] = None) = { val incompatible = settings.intersect(incompatibleOptions) val filteredSettings = if !incompatible.isEmpty then @@ -107,7 +107,7 @@ class ReplDriver(settings: Array[String], case Some((files, ictx)) => inContext(ictx) { shouldStart = true if files.nonEmpty then out.println(i"Ignoring spurious arguments: $files%, %") - ictx.base.initialize() + ictx.base.initialize(previousOutputDir) ictx } case None => @@ -523,43 +523,47 @@ class ReplDriver(settings: Array[String], val entries = flatten(jarFile) - def classNameOf(classFile: AbstractFile): String = { + def tryClassLoad(classFile: AbstractFile): Option[String] = { val input = classFile.input try { val reader = new ClassReader(input) - reader.getClassName.replace('/', '.') - } finally { + val clsName = reader.getClassName.replace('/', '.') + rendering.myClassLoader.loadClass(clsName) + Some(clsName) + } catch + case _: ClassNotFoundException => None + finally { input.close() } } - def alreadyDefined(clsName: String) = state.context.platform.classPath(using state.context).findClassFile(clsName).isDefined - val existingClass = entries.filter(_.ext.isClass).map(classNameOf).find(alreadyDefined) + val existingClass = entries.filter(_.ext.isClass).find(tryClassLoad(_).isDefined) if (existingClass.nonEmpty) out.println(s"The path '$f' cannot be loaded, it contains a classfile that already exists on the classpath: ${existingClass.get}") state else val cp = state.context.platform.classPath(using state.context).asClassPathString -// println(s"CURRENT CP STRING: $cp") val newCP = s"$cp${JFile.pathSeparator}$path" - println(s"UPDATED CP: $newCP") // add to compiler class path -// println(s"INIT state classPath = ${state.context.platform.classPath(using state.context).asClassPathString}") -// val cpCP = ClassPathFactory.newClassPath(jarFile)(using state.context) -// state.context.platform.addToClassPath(cpCP) -// println(s"classPath after add = ${state.context.platform.classPath(using state.context).asClassPathString}") - - // recreate initial context - resetToInitial(List("-classpath", newCP)) -// rootCtx = setupRootCtx(Array(), rootCtx.fresh.setSetting(rootCtx.settings.classpath, newCP)) + val prevOutputDir = rootCtx.settings.outputDir.valueIn(rootCtx.settingsState) + val ctxToUse = initCtx.fresh.setSetting(rootCtx.settings.classpath, newCP) + rootCtx = setupRootCtx( + Array(), + ctxToUse, + previousOutputDir = Some(prevOutputDir) + ) val s = state.copy(context = rootCtx) - // new class loader - val oldCL = rendering.classLoader()(using state.context) - val newCL = fromURLsParallelCapable(s.context.platform.classPath(using s.context).asURLs, oldCL) - rendering.myClassLoader = new AbstractFileClassLoader(state.context.settings.outputDir.default, newCL) -// out.println(s"Added '$path' to classpath.") + // new class loader with previous output dir and specified jar + val prevClassLoader = rendering.classLoader()(using state.context) + val jarClassLoader = fromURLsParallelCapable( + ClassPathFactory.newClassPath(jarFile)(using rootCtx).asURLs, prevClassLoader) + val replOutputClassLoader = new AbstractFileClassLoader( + prevOutputDir, jarClassLoader) + rendering.myClassLoader = new AbstractFileClassLoader( + rootCtx.settings.outputDir.valueIn(rootCtx.settingsState), replOutputClassLoader) + out.println(s"Added '$path' to classpath.") s case KindOf(expr) => From 1ac09e6276fe8fd003b5fa55dd3f754da44b6457 Mon Sep 17 00:00:00 2001 From: aherlihy Date: Wed, 15 Jan 2025 16:49:36 -0500 Subject: [PATCH 09/18] further cleanup, reuse dir (bug w double import) --- .../src/dotty/tools/repl/ReplDriver.scala | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index a1c64fcbc8c9..acec370c6b37 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -511,9 +511,8 @@ class ReplDriver(settings: Array[String], } case Require(path) => - val f = new JFile(path) val jarFile = AbstractFile.getDirectory(path) - if (!f.exists || jarFile == null) + if (jarFile == null) out.println(s"""Cannot add "$path" to classpath.""") state else @@ -539,15 +538,17 @@ class ReplDriver(settings: Array[String], val existingClass = entries.filter(_.ext.isClass).find(tryClassLoad(_).isDefined) if (existingClass.nonEmpty) - out.println(s"The path '$f' cannot be loaded, it contains a classfile that already exists on the classpath: ${existingClass.get}") + out.println(s"The path '$path' cannot be loaded, it contains a classfile that already exists on the classpath: ${existingClass.get}") state else - val cp = state.context.platform.classPath(using state.context).asClassPathString - val newCP = s"$cp${JFile.pathSeparator}$path" + val prevClassPath = state.context.platform.classPath(using state.context).asClassPathString + val newClassPath = s"$prevClassPath${JFile.pathSeparator}$path" // add to compiler class path val prevOutputDir = rootCtx.settings.outputDir.valueIn(rootCtx.settingsState) - val ctxToUse = initCtx.fresh.setSetting(rootCtx.settings.classpath, newCP) + val ctxToUse = initCtx.fresh + .setSetting(rootCtx.settings.classpath, newClassPath) + .setSetting(rootCtx.settings.outputDir, prevOutputDir) // reuse virtual output directory rootCtx = setupRootCtx( Array(), ctxToUse, @@ -559,10 +560,11 @@ class ReplDriver(settings: Array[String], val prevClassLoader = rendering.classLoader()(using state.context) val jarClassLoader = fromURLsParallelCapable( ClassPathFactory.newClassPath(jarFile)(using rootCtx).asURLs, prevClassLoader) - val replOutputClassLoader = new AbstractFileClassLoader( - prevOutputDir, jarClassLoader) +// val replOutputClassLoader = new AbstractFileClassLoader( +// prevOutputDir, jarClassLoader) rendering.myClassLoader = new AbstractFileClassLoader( - rootCtx.settings.outputDir.valueIn(rootCtx.settingsState), replOutputClassLoader) + rootCtx.settings.outputDir.valueIn(rootCtx.settingsState), jarClassLoader) //replOutputClassLoader) + println(s"new classpath: ${s.context.platform.classPath(using s.context)}") out.println(s"Added '$path' to classpath.") s From 2babf5ca588ce12bb203d8a51a8bb1fc6b104874 Mon Sep 17 00:00:00 2001 From: aherlihy Date: Wed, 15 Jan 2025 17:36:52 -0500 Subject: [PATCH 10/18] Add simple tests --- compiler/src/dotty/tools/repl/ReplDriver.scala | 5 +---- compiler/test-resources/repl/require-command | 13 +++++++++++++ compiler/test-resources/repl/require-errors | 8 ++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 compiler/test-resources/repl/require-command create mode 100644 compiler/test-resources/repl/require-errors diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index acec370c6b37..02d3d28639f5 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -560,11 +560,8 @@ class ReplDriver(settings: Array[String], val prevClassLoader = rendering.classLoader()(using state.context) val jarClassLoader = fromURLsParallelCapable( ClassPathFactory.newClassPath(jarFile)(using rootCtx).asURLs, prevClassLoader) -// val replOutputClassLoader = new AbstractFileClassLoader( -// prevOutputDir, jarClassLoader) rendering.myClassLoader = new AbstractFileClassLoader( - rootCtx.settings.outputDir.valueIn(rootCtx.settingsState), jarClassLoader) //replOutputClassLoader) - println(s"new classpath: ${s.context.platform.classPath(using s.context)}") + rootCtx.settings.outputDir.valueIn(rootCtx.settingsState), jarClassLoader) out.println(s"Added '$path' to classpath.") s diff --git a/compiler/test-resources/repl/require-command b/compiler/test-resources/repl/require-command new file mode 100644 index 000000000000..1af49ccff6fb --- /dev/null +++ b/compiler/test-resources/repl/require-command @@ -0,0 +1,13 @@ +scala> val z = 1 +val z: Int = 1 + +scala>:require sbt-test/source-dependencies/canon/actual/a.jar +Added 'sbt-test/source-dependencies/canon/actual/a.jar' to classpath. + +scala> import A.x + +scala> x +val res0: Int = 3 + +scala> z +val res1: Int = 1 diff --git a/compiler/test-resources/repl/require-errors b/compiler/test-resources/repl/require-errors new file mode 100644 index 000000000000..8e26c673f9b3 --- /dev/null +++ b/compiler/test-resources/repl/require-errors @@ -0,0 +1,8 @@ +scala>:require path/does/not/exist +Cannot add "path/does/not/exist" to classpath. + +scala>:require sbt-test/source-dependencies/canon/actual/a.jar +Added 'sbt-test/source-dependencies/canon/actual/a.jar' to classpath. + +scala>:require sbt-test/source-dependencies/canon/actual/a.jar +The path 'sbt-test/source-dependencies/canon/actual/a.jar' cannot be loaded, it contains a classfile that already exists on the classpath: sbt-test/source-dependencies/canon/actual/a.jar(A.class) From 4d1e2b02d0b66fc397b1dd2ddafa55c39f0e0295 Mon Sep 17 00:00:00 2001 From: aherlihy Date: Wed, 15 Jan 2025 22:13:47 -0500 Subject: [PATCH 11/18] Handle out of sync imports after require + tests --- .../src/dotty/tools/repl/ReplCompiler.scala | 6 +++- compiler/test-resources/jars/MyLibrary.scala | 10 ++++++ compiler/test-resources/jars/MyLibrary2.scala | 10 ++++++ compiler/test-resources/jars/mylibrary.jar | Bin 0 -> 3399 bytes compiler/test-resources/jars/mylibrary2.jar | Bin 0 -> 3435 bytes compiler/test-resources/repl/require-multiple | 32 ++++++++++++++++++ 6 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 compiler/test-resources/jars/MyLibrary.scala create mode 100644 compiler/test-resources/jars/MyLibrary2.scala create mode 100644 compiler/test-resources/jars/mylibrary.jar create mode 100644 compiler/test-resources/jars/mylibrary2.jar create mode 100644 compiler/test-resources/repl/require-multiple diff --git a/compiler/src/dotty/tools/repl/ReplCompiler.scala b/compiler/src/dotty/tools/repl/ReplCompiler.scala index 087eb836dfcb..989d6bb52a41 100644 --- a/compiler/src/dotty/tools/repl/ReplCompiler.scala +++ b/compiler/src/dotty/tools/repl/ReplCompiler.scala @@ -47,7 +47,11 @@ class ReplCompiler extends Compiler: /** Import previous runs and user defined imports */ override protected def rootContext(using Context): Context = { def importContext(imp: tpd.Import)(using Context) = - ctx.importContext(imp, imp.symbol) + // TODO: only when context has changed? + val typer = ctx.typer + typer.index(imp) + val imp2 = typer.typed(imp).asInstanceOf[tpd.Import] + ctx.importContext(imp2, imp2.symbol) def importPreviousRun(id: Int)(using Context) = { // we first import the wrapper object id diff --git a/compiler/test-resources/jars/MyLibrary.scala b/compiler/test-resources/jars/MyLibrary.scala new file mode 100644 index 000000000000..8dda1938608a --- /dev/null +++ b/compiler/test-resources/jars/MyLibrary.scala @@ -0,0 +1,10 @@ +// MyLibrary.scala +package mylibrary + +object Utils: + def greet(name: String): String = s"Hello, $name!" + +class Calculator: + def add(x: Int, y: Int): Int = x + y + def subtract(x: Int, y: Int): Int = x - y + diff --git a/compiler/test-resources/jars/MyLibrary2.scala b/compiler/test-resources/jars/MyLibrary2.scala new file mode 100644 index 000000000000..2fdfa6c09b5d --- /dev/null +++ b/compiler/test-resources/jars/MyLibrary2.scala @@ -0,0 +1,10 @@ +// MyLibrary2.scala +package mylibrary2 + +object Utils2: + def greet(name: String): String = s"Hello, $name!" + +class Calculator2: + def add(x: Int, y: Int): Int = x + y + def subtract(x: Int, y: Int): Int = x - y + diff --git a/compiler/test-resources/jars/mylibrary.jar b/compiler/test-resources/jars/mylibrary.jar new file mode 100644 index 0000000000000000000000000000000000000000..5378fdcc0f7209b1d0ad4a34dd71e19120bcb74d GIT binary patch literal 3399 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 UJUf8&-oUhHH*M@{^s5Nqf37t~djJ3c literal 0 HcmV?d00001 diff --git a/compiler/test-resources/repl/require-multiple b/compiler/test-resources/repl/require-multiple new file mode 100644 index 000000000000..34faa564d37d --- /dev/null +++ b/compiler/test-resources/repl/require-multiple @@ -0,0 +1,32 @@ +scala> val z = 1 +val z: Int = 1 + +scala>:require compiler/test-resources/jars/mylibrary.jar +Added 'compiler/test-resources/jars/mylibrary.jar' to classpath. + +scala> import mylibrary.Utils + +scala> Utils.greet("Alice") +val res0: String = Hello, Alice! + +scala>:require compiler/test-resources/jars/mylibrary2.jar +Added 'compiler/test-resources/jars/mylibrary2.jar' to classpath. + +scala> import mylibrary2.Utils2 + +scala> Utils2.greet("Alice") +val res1: String = Hello, Alice! + +scala> Utils.greet("Alice") +val res2: String = Hello, Alice! + +scala> import mylibrary.Utils.greet + +scala> greet("Tom") +val res3: String = Hello, Tom! + +scala> Utils.greet("Alice") +val res4: String = Hello, Alice! + +scala> z +val res5: Int = 1 From 78bf30b8a4449f31552f0d49109608242973a5f0 Mon Sep 17 00:00:00 2001 From: aherlihy Date: Thu, 16 Jan 2025 13:05:05 -0500 Subject: [PATCH 12/18] slightly cleaner tests --- compiler/test-resources/jars/MyLibrary.scala | 10 ++++------ compiler/test-resources/jars/MyLibrary2.scala | 11 +++++------ compiler/test-resources/jars/mylibrary.jar | Bin 3399 -> 2309 bytes compiler/test-resources/jars/mylibrary2.jar | Bin 3435 -> 2336 bytes compiler/test-resources/repl/require-multiple | 2 +- 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/compiler/test-resources/jars/MyLibrary.scala b/compiler/test-resources/jars/MyLibrary.scala index 8dda1938608a..cc492696c139 100644 --- a/compiler/test-resources/jars/MyLibrary.scala +++ b/compiler/test-resources/jars/MyLibrary.scala @@ -1,10 +1,8 @@ -// MyLibrary.scala +/** + * JAR used for testing repl :require + * Generated using: mkdir out; scalac -d out MyLibrary.scala; jar cf mylibrary.jar -C out . + */ package mylibrary object Utils: def greet(name: String): String = s"Hello, $name!" - -class Calculator: - def add(x: Int, y: Int): Int = x + y - def subtract(x: Int, y: Int): Int = x - y - diff --git a/compiler/test-resources/jars/MyLibrary2.scala b/compiler/test-resources/jars/MyLibrary2.scala index 2fdfa6c09b5d..ca288fcfbf85 100644 --- a/compiler/test-resources/jars/MyLibrary2.scala +++ b/compiler/test-resources/jars/MyLibrary2.scala @@ -1,10 +1,9 @@ -// MyLibrary2.scala +/** + * JAR used for testing repl :require + * Generated using: mkdir out2; scalac -d out MyLibrary2.scala; jar cf mylibrary2.jar -C out2 . + */ package mylibrary2 object Utils2: - def greet(name: String): String = s"Hello, $name!" - -class Calculator2: - def add(x: Int, y: Int): Int = x + y - def subtract(x: Int, y: Int): Int = x - y + def greet(name: String): String = s"Greetings, $name!" diff --git a/compiler/test-resources/jars/mylibrary.jar b/compiler/test-resources/jars/mylibrary.jar index 5378fdcc0f7209b1d0ad4a34dd71e19120bcb74d..4537e10ee4910923d760abced82b45e96367d257 100644 GIT binary patch delta 1741 zcmY+Fc{CgN7RQ5(rHOs5T`cX(vjLFp&(x#xcFcTh7^Nfw2a0IC3_ zrKJJ6VdfNBTZscXpL!^x#s@;J1HuSFo%iPa8(97UB-Jf{y?5%Gj=u2QDOOnK`|Bw( zrJ=8lxs~%RWAvi4N`w^0;kc^?9_a(@q0!MPo5iU_{`B3)J$nGuz=f>9U#hPlV;nXL zAPth=fXJ0~e@lE;mG4e|gojR0GL5)c?c$1A1^;Mc$4#1&+8B6zqTbefT#L}rH4pY_ z+MTc8YUj*(FJAaj!*q4T3BlJW>| zYvkNRcD6^=&AX{hlgBBgUe>&)TBnp_+VXT<#^IY= z0d7(u*|x(zIQ<#9#FG`XOp#XXIZwTqXxiv~OtVQPwKe7m{EGrX#zO+HDMCHqeH*^^ zT-H*@zM5F;lr;9A2!wlW^>GA2!d3U#P@P%TF9|=BM=pg^^_8&V7?EPrno7#IFo*ON zoA~r+3x0Hcfwtji*)j@7-6$e0kULLCAuRH3mESOfvE;XAd*_y(qAjNSk5rJIOI6&* zie-G5Go`S#giIGpl?O|oI>T(ZGY_kEkPCGkjOy{}TqYqR;d1N~#d7TnC+%T5vnZsL zbOQEf%L#D+fF}U}{NHd<)ul`bxcpwvJ|~5&y;NbQe+{I-DJP*7CLrA*&o^Yah~`qc z=+pQi|H7KWj~;R0zTUI)39oKK+&^K`V(5g>jUCHPR{S0yil|ocAyC7iZA?vc{8gx_ zQw9dAZeeACF;vX@HMB6*-6PBP^^x}_0&ncY!Mgq?*{v~#UWqIm|- z8MgA(rdNVXF9!Up;Jg4EP>goPI4R`Hm_)e0P$)6UV&8f{JS8+@<6(HH3H~tWNxkfIbQL53|rBB8)&f-^YV8dgbgdh?cDqI);dZu zn>dJ*k#Fk>c`NfyF`6kX`wp~RfF#}OBLbNj5z49yHbnAvd^D@P=i{%6PwP3GXsdD| zr`y?==J@K!vR{+F8bz;RU1WB2 zvew+SF}=#O$A+wynGcOifsXe|1@j6 z>Z_%kIP*FxuXtU}GQF1yL*iqE+br?%p3|)%u>Intig@_XC9#6ITKJ&p4pJhbp65Q#Ip;plxnD;#aGeAI3 z)|dcb9ROp!+X7+hfYMrS2LONoM>Ic}>%se%o8!L*OK?1X24gH7Y*E%OyEQQ=Hm0+~ z9*xlKT8~D+>79&w&e}bCOjekt9Y(_z(-l#?PB3i}I-b7e=oe`f)v{A%e_Z+-(se_iB5x6fm2SEUUlUMU^6X5g$f+7R`e8R9{ky@*{{_gk3FMddD z;K7JMYXWjRXKH}LhyBkpx^>e>4M_7_;LEUo#!lb5ck1sK0E4h%+Pio8A`jOTvOc<0Zkuj z+o^3ckr43u1Pl2YSn!wV=%CDf$QL38t-?+CK!?To&4%+P$b#WZwbN-9Wxb1hz7$rj zLI))l7*g-3BVB5o7xTeb*uS*5_yOc4;#->er$3eNTGFP-_SXq-yFH%6s4BlSRrB!!TQ&*=Na*=FA=7=Zug{pKf}yOAo~yz+V(qc*eRI7X1A&HFgX!<@ zy^1Z}u`7bl+DEL9G?riCX@8^FdX8Rm>ciSvdXOp9cY zI^?;OvdGzZ`T!3AV8G$k|FKQsSX_AIp+rW&b8kosdz87tY#Y46m(Dz7yNS=@lk}dD z`V$#^Z8qYq8+2~gMGhH-bT<}E8X9?3SGsz36#HdVNZk6 z=c-GA@+olL-HaV&hkysVF@skLrFqrhG(zV#C9L9fz^p4K!n&R+n5$F1&jgg~lFwtk zWsB$l@MKrG;MtFRB(6&@U@5sh=v+Dk z^BD{`=(mcukuw)-qf1R+516zTcyrtFb$=XPa%INp(~Q%}$tu=(91$4W>#wi>JYrhQ z)Qxw6`1x^Tn{qHBD!KlK2E~aWv2Y#q3#%+(5<9CR`NAlv#%zv z`kB~ja*}wd?Ux-fTD6zA*Lbq;U&$KC{)j;9`QiPvm(t6Qhb|g?_#p2s6ZE7dZGE<2 z&R5dL?SXUWmP;^L)y|B|SoYIs&80SxR|?DV$JMjrL+>E zkCMtt_uSjFgqYkweOSZPkpZLE}6OQAJFvRAEZFKs8 z$=E;?V=(7&-3q|5$t8ZtJI{9~&MY*Ew+N*ol#q9G33yy`ej)fC4qiowQ*Q8qT&?RD z^9WPf&Q27Q>K1;~oG(}O=cOvoP*6!%bm;EWdwtY;#$0+Jnp5aqAO<9b)qV#E6Ebu@ z263j5WORJzs^L~IZJvaZEez4sCBpQb2;e6NVabnz#YLV)3~BU21{PW-7e$Z(KNy~G zw#&RGYcsU1XZa>6ZW1+eww^FTbwU*TwV^f|LAp;Ntaju{6H0fWZyzXGg6653np0k< zzPp{BbmP51&@et_i6L6Pq-s}r_@LyZZ+}4UmB;r|X`uARbI9RY2!3Tt=&k*@V;eG1 zAI*XL7!*r9C?+=DNJ9!&en}Sj%7DGFZzq3D($@Cg8=K!G^2_E9WemeAb~(u7!cys> z+C!o=($Kci>(SuY0>W#=>08w!SM}#7Qqx7`x~*I&<`Z6qn^aw#C8&=HN*l&UyRUT_ z)fzOhZdcff3MkTc$alTB0(c5c>>Z`I!D0;=^}?h%iLW7Q5COb}5{4Y$o*>|=+)!wS zyyInKe`kp$uRdk0(tx=o+ODQ=OH=kr$5whh)N2~FoDgOOofO(vk%c#Tg2Hc$SU6nBp?1i#sFJC)&a!+bGBihshEZ zf#N_RAXuAs7EsCS$D^=oof;jSZ(^u3?H_cIIRx0#(5V1cPKUTdW6x9okOi(I2`{s5 zLaX{S?ML*Dw82D)9~bW#6fIzivl=GXF%MlYjx$RdY>FFZPnegMlUs)(#yq+5Tq z=S!$-MQqGGk#Y#dr=hH9prLaM;%J~91xZXk>4)j%^@kTiRUzUh#E3MX;LN^2{>}`l zxUy>3^^CbOs3X-|cu~oQbdyegq@^teRT7YD0jHhv3_Cx-hbEhlNa7wHk`(AP?n=Z1)PnOy)To4a_dbuC-cW- zdzuw$J>Ji&2cNEySYH@XUM8N((yAn$Ax3S*1rzh&8R5#a87F(WVZ@w!6LE2vg+Z zcLw{fy+alUMHNU+-AV2*K9xgP_NoF#)_qJSfZbJR^eBfa;>|ACoxS-gUqtrY!mGeNg}2{Sv1#t% zyRPuLlbQSSH#ZZV8Z*{+MM8`!I(>sv7pA`}5*}U& zz^@va)BWAJ8u?fIDSEkz*%AF~=vv*%RmiJm#%cZxU89$|2!eor*SD((G){A^+Fc!) zt9RE*EUw;NwFyphHSTxyyLJX#oV6G9uQ+7Rn*1Zq_mlrS&f3Duz0OzdI%h>!an@e$ zzhkT|M%hj4QQ*sOWbA0^FwKg}YJ>nc>Sy}Z+ z`g7GwiZo({XH?%x>1@qNUbc-iBi^DvCV0XSZuIbERP+MR^zBsK>h9;gIkk9=BE2^d zfA=Vq`y7*waxkex!F7{g9Tz-8--^3jF<~k*Ib@mTr^j@7eGYo8S+o7$42C9*1AQ(3 z%)hX;WWa|V)dR&S(OO8!8 zq*Tq{q_{uG{cJWlg6#Cq50MyEYaRIQaeyQ9Or)~gA_xlp=(s?B3xZBH5amZIs@t2%Ncaf8a%1&m0 z#!^G3|FGK?B9kEEF)uJ^T`&m569$3)$7q0uxH7{()Q?UN%xHJ&B%>a-3y(@Rgmi@6 z>n^#Po|JxlJ6OX{9WAfJzjM4fv6%w2$y|ttm=^k%a0zUToIQTj@0|iKns@8n`w}s(b8px<3~vEF-4pNSux@d8n5rKowCX*4R<5YI zhYugXw_5x)buYrxQn0UzWXoy7DhMPEvK0D%Su_{~N2H&8@0uwR8Jvtj%q|>J0W0fh zpDTs3>?>iaDublhV?W)C=9_2YA6@4E7OhRzXejA%$#Jorwq)fdG_gwbGO#vCC{z+y zBqp8~?lT}9ZWjmyuw1u$GLeCJREJhrSokmDtz#pHS4wyp81HoWrSh=I-z8cYlcWI} zBQj+*ZQ5o|%Rq&8bV_U?zPt8Jf%6!5B>-o)RBGh9`3JN;D35kyRu3kY)+|N+D^zz4 zo}QIzbKa@Hpu!4%Rc{(?MGQS_{ke{7&nfQqP&`VDqOV`s_#rFt@d?N5A?kv{WBx{$ zK8a{U(;YDDknW;i23ue%8YUD~618L*wl|AEErWKWC0Ev*x?xDm>rPqRHzsqk1GUUm(pU*V24@*rjuekQ!^PaVI*#c zVlQdCvrE49SmrCN^}AcqOG8R=9|U?Je))oDVPE!BDlPPGpytc(I2EO zB>F@3R&|bJEP8Ax!P|7LAA9%Wp4M&gEb(7YR(ZxM*9MmRod5Ag$%?CUL)6MUQu~K$ zeOK3P6~~UcK+3ZqwGet{=~kBe%9{n7LG;T)_7;uKSYPyPb-Em_xa7n+?aR5+w2a{Q zsN>snL{G7|LcJy$Hzh7|3dsx?2XlXol)I7x6@qN*uF0Rj2=6;ux~h1$`fJX&zb=_1 z1V(}WTPc8pB<@>_T$8l;hP{%;QeRtz`u%T^2M{ntpbe$~-Dke#1DJ-%fN8=&9SjLb jz@-oG6W^ba1ARXLPzyr=&Ts@m3?v3h{+g>K$UgrcNC6eZ literal 3435 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 UJUf8&-oUhHH*M@{^s5Nqf37t~djJ3c diff --git a/compiler/test-resources/repl/require-multiple b/compiler/test-resources/repl/require-multiple index 34faa564d37d..3803234141d6 100644 --- a/compiler/test-resources/repl/require-multiple +++ b/compiler/test-resources/repl/require-multiple @@ -15,7 +15,7 @@ Added 'compiler/test-resources/jars/mylibrary2.jar' to classpath. scala> import mylibrary2.Utils2 scala> Utils2.greet("Alice") -val res1: String = Hello, Alice! +val res1: String = Greetings, Alice! scala> Utils.greet("Alice") val res2: String = Hello, Alice! From 9a6067993748c659b7c76c5361993335ef5a13a9 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 13 Feb 2025 23:53:46 +0000 Subject: [PATCH 13/18] Fix :require adding classes/pkgs --- .../dotc/classpath/AggregateClassPath.scala | 6 +--- .../tools/dotc/config/JavaPlatform.scala | 3 +- .../dotty/tools/dotc/config/Platform.scala | 2 +- .../src/dotty/tools/dotc/core/Contexts.scala | 6 +--- .../dotty/tools/dotc/core/Definitions.scala | 2 +- .../dotty/tools/dotc/core/SymbolLoaders.scala | 29 ++++++++++++++- .../src/dotty/tools/repl/ReplCompiler.scala | 6 +--- .../src/dotty/tools/repl/ReplDriver.scala | 35 +++++++++---------- compiler/test/dotty/tools/repl/ReplTest.scala | 5 +-- 9 files changed, 52 insertions(+), 42 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala b/compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala index c7508e14a004..cd44ba27df96 100644 --- a/compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala +++ b/compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala @@ -33,11 +33,7 @@ case class AggregateClassPath(aggregates: Seq[ClassPath]) extends ClassPath { packageIndex.getOrElseUpdate(pkg.dottedString, aggregates.filter(_.hasPackage(pkg))) } - override def asURLs: Seq[URL] = - aggregates.flatMap { - case v: VirtualDirectoryClassPath => Seq() - case a => a.asURLs - } + override def asURLs: Seq[URL] = aggregates.flatMap(_.asURLs) override def asClassPathStrings: Seq[String] = aggregates.map(_.asClassPathString).distinct diff --git a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala index e923ee1cc053..a6dbf696a575 100644 --- a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala +++ b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala @@ -33,8 +33,9 @@ class JavaPlatform extends Platform { case cp: ClassPath => currentClassPath = Some(AggregateClassPath(cp :: cPath :: Nil)) } + /** Update classpath with a substituted subentry */ - def updateClassPath(subst: Map[ClassPath, ClassPath])(using Context): Unit = classPath match { + def updateClassPath(subst: Map[ClassPath, ClassPath]): Unit = currentClassPath.get match { case AggregateClassPath(entries) => currentClassPath = Some(AggregateClassPath(entries map (e => subst.getOrElse(e, e)))) case cp: ClassPath => diff --git a/compiler/src/dotty/tools/dotc/config/Platform.scala b/compiler/src/dotty/tools/dotc/config/Platform.scala index 750e2fe5b203..c7d319e736b4 100644 --- a/compiler/src/dotty/tools/dotc/config/Platform.scala +++ b/compiler/src/dotty/tools/dotc/config/Platform.scala @@ -19,7 +19,7 @@ abstract class Platform { def classPath(using Context): ClassPath /** Update classpath with a substitution that maps entries to entries */ - def updateClassPath(subst: Map[ClassPath, ClassPath])(using Context): Unit + def updateClassPath(subst: Map[ClassPath, ClassPath]): Unit /** Add new entry to classpath */ def addToClassPath(cPath: ClassPath)(using Context): Unit diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 5e95b4f8717a..e12ab1cc2da2 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -36,7 +36,6 @@ import scala.annotation.internal.sharable import DenotTransformers.DenotTransformer import dotty.tools.dotc.profile.Profiler import dotty.tools.dotc.sbt.interfaces.{IncrementalCallback, ProgressCallback} -import dotty.tools.dotc.classpath.ClassPathFactory import util.Property.Key import util.Store import plugins.* @@ -916,11 +915,8 @@ object Contexts { /** Initializes the `ContextBase` with a starting context. * This initializes the `platform` and the `definitions`. */ - def initialize(previousOutputDir: Option[AbstractFile] = None)(using Context): Unit = { + def initialize()(using Context): Unit = { _platform = newPlatform - previousOutputDir.foreach(cp => - _platform.nn.addToClassPath(ClassPathFactory.newClassPath(cp)) - ) definitions.init() } diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 63f4cd743e8f..6e2e924edf65 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -2233,7 +2233,7 @@ class Definitions { private var isInitialized = false - def init(require: Boolean = false)(using Context): Unit = { + def init()(using Context): Unit = { this.initCtx = ctx if (!isInitialized) { // force initialization of every symbol that is synthesized or hijacked by the compiler diff --git a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index 68f2e350c3e4..39297697f29a 100644 --- a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -7,6 +7,7 @@ import java.nio.channels.ClosedByInterruptException import scala.util.control.NonFatal +import dotty.tools.dotc.classpath.{ ClassPathFactory, PackageNameUtils } import dotty.tools.dotc.classpath.FileUtils.{hasTastyExtension, hasBetastyExtension} import dotty.tools.io.{ ClassPath, ClassRepresentation, AbstractFile, NoAbstractFile } import dotty.tools.backend.jvm.DottyBackendInterface.symExtensions @@ -272,7 +273,7 @@ object SymbolLoaders { def maybeModuleClass(classRep: ClassRepresentation): Boolean = classRep.name.nonEmpty && classRep.name.last == '$' - private def enterClasses(root: SymDenotation, packageName: String, flat: Boolean)(using Context) = { + def enterClasses(root: SymDenotation, packageName: String, flat: Boolean)(using Context) = { def isAbsent(classRep: ClassRepresentation) = !root.unforcedDecls.lookup(classRep.name.toTypeName).exists @@ -316,6 +317,32 @@ object SymbolLoaders { } } } + + def mergeNewEntries( + packageClass: ClassSymbol, fullPackageName: String, + jarClasspath: ClassPath, fullClasspath: ClassPath, + )(using Context): Unit = + if jarClasspath.classes(fullPackageName).nonEmpty then + // if the package contains classes in jarClasspath, the package is invalidated (or removed if there are no more classes in it) + val packageVal = packageClass.sourceModule.asInstanceOf[TermSymbol] + if packageClass.isRoot then + val loader = new PackageLoader(packageVal, fullClasspath) + loader.enterClasses(defn.EmptyPackageClass, fullPackageName, flat = false) + loader.enterClasses(defn.EmptyPackageClass, fullPackageName, flat = true) + else if packageClass.ownersIterator.contains(defn.ScalaPackageClass) then + () // skip + else if fullClasspath.hasPackage(fullPackageName) then + packageClass.info = new PackageLoader(packageVal, fullClasspath) + else + packageClass.owner.info.decls.openForMutations.unlink(packageVal) + else + for p <- jarClasspath.packages(fullPackageName) do + val subPackageName = PackageNameUtils.separatePkgAndClassNames(p.name)._2.toTermName + val subPackage = packageClass.info.decl(subPackageName).orElse: + // package does not exist in symbol table, create a new symbol + enterPackage(packageClass, subPackageName, (module, modcls) => new PackageLoader(module, fullClasspath)) + mergeNewEntries(subPackage.asSymDenotation.moduleClass.asClass, p.name, jarClasspath, fullClasspath) + end mergeNewEntries } /** A lazy type that completes itself by calling parameter doComplete. diff --git a/compiler/src/dotty/tools/repl/ReplCompiler.scala b/compiler/src/dotty/tools/repl/ReplCompiler.scala index 989d6bb52a41..087eb836dfcb 100644 --- a/compiler/src/dotty/tools/repl/ReplCompiler.scala +++ b/compiler/src/dotty/tools/repl/ReplCompiler.scala @@ -47,11 +47,7 @@ class ReplCompiler extends Compiler: /** Import previous runs and user defined imports */ override protected def rootContext(using Context): Context = { def importContext(imp: tpd.Import)(using Context) = - // TODO: only when context has changed? - val typer = ctx.typer - typer.index(imp) - val imp2 = typer.typed(imp).asInstanceOf[tpd.Import] - ctx.importContext(imp2, imp2.symbol) + ctx.importContext(imp, imp.symbol) def importPreviousRun(id: Int)(using Context) = { // we first import the wrapper object id diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 02d3d28639f5..12065be7cdd0 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -1,8 +1,10 @@ package dotty.tools.repl import scala.language.unsafeNulls + import java.io.{File => JFile, PrintStream} import java.nio.charset.StandardCharsets + import dotty.tools.dotc.ast.Trees.* import dotty.tools.dotc.ast.{tpd, untpd} import dotty.tools.dotc.classpath.ClassPathFactory @@ -20,6 +22,7 @@ import dotty.tools.dotc.core.NameOps.* import dotty.tools.dotc.core.Names.Name import dotty.tools.dotc.core.StdNames.* import dotty.tools.dotc.core.Symbols.{Symbol, defn} +import dotty.tools.dotc.core.SymbolLoaders import dotty.tools.dotc.interfaces import dotty.tools.dotc.interactive.Completion import dotty.tools.dotc.printing.SyntaxHighlighting @@ -70,6 +73,7 @@ case class State(objectIndex: Int, quiet: Boolean, context: Context): def validObjectIndexes = (1 to objectIndex).filterNot(invalidObjectIndexes.contains(_)) + //def copy() = this /** Main REPL instance, orchestrating input, compilation and presentation */ class ReplDriver(settings: Array[String], @@ -94,7 +98,7 @@ class ReplDriver(settings: Array[String], initCtx.settings.YwithBestEffortTasty.name ) - private def setupRootCtx(settings: Array[String], rootCtx: Context, previousOutputDir: Option[AbstractFile] = None) = { + private def setupRootCtx(settings: Array[String], rootCtx: Context) = { val incompatible = settings.intersect(incompatibleOptions) val filteredSettings = if !incompatible.isEmpty then @@ -107,7 +111,7 @@ class ReplDriver(settings: Array[String], case Some((files, ictx)) => inContext(ictx) { shouldStart = true if files.nonEmpty then out.println(i"Ignoring spurious arguments: $files%, %") - ictx.base.initialize(previousOutputDir) + ictx.base.initialize() ictx } case None => @@ -540,30 +544,23 @@ class ReplDriver(settings: Array[String], if (existingClass.nonEmpty) out.println(s"The path '$path' cannot be loaded, it contains a classfile that already exists on the classpath: ${existingClass.get}") state - else - val prevClassPath = state.context.platform.classPath(using state.context).asClassPathString - val newClassPath = s"$prevClassPath${JFile.pathSeparator}$path" + else inContext(state.context): + val jarClassPath = ClassPathFactory.newClassPath(jarFile) + val prevOutputDir = ctx.settings.outputDir.value // add to compiler class path - val prevOutputDir = rootCtx.settings.outputDir.valueIn(rootCtx.settingsState) - val ctxToUse = initCtx.fresh - .setSetting(rootCtx.settings.classpath, newClassPath) - .setSetting(rootCtx.settings.outputDir, prevOutputDir) // reuse virtual output directory - rootCtx = setupRootCtx( - Array(), - ctxToUse, - previousOutputDir = Some(prevOutputDir) - ) - val s = state.copy(context = rootCtx) + ctx.platform.addToClassPath(jarClassPath) + SymbolLoaders.mergeNewEntries(defn.RootClass, ClassPath.RootPackage, jarClassPath, ctx.platform.classPath) // new class loader with previous output dir and specified jar - val prevClassLoader = rendering.classLoader()(using state.context) + val prevClassLoader = rendering.classLoader() val jarClassLoader = fromURLsParallelCapable( - ClassPathFactory.newClassPath(jarFile)(using rootCtx).asURLs, prevClassLoader) + jarClassPath.asURLs, prevClassLoader) rendering.myClassLoader = new AbstractFileClassLoader( - rootCtx.settings.outputDir.valueIn(rootCtx.settingsState), jarClassLoader) + prevOutputDir, jarClassLoader) + out.println(s"Added '$path' to classpath.") - s + state case KindOf(expr) => out.println(s"""The :kind command is not currently supported.""") diff --git a/compiler/test/dotty/tools/repl/ReplTest.scala b/compiler/test/dotty/tools/repl/ReplTest.scala index 3e827a0f1e36..a0975d603ae1 100644 --- a/compiler/test/dotty/tools/repl/ReplTest.scala +++ b/compiler/test/dotty/tools/repl/ReplTest.scala @@ -98,10 +98,7 @@ extends ReplDriver(options, new PrintStream(out, true, StandardCharsets.UTF_8.na FileDiff.dump(checkFile.toPath.toString, actualOutput) println(s"Wrote updated script file to $checkFile") else - println("expected =========>") - println(expectedOutput.mkString(EOL)) - println("actual ===========>") - println(actualOutput.mkString(EOL)) + println(dotc.util.DiffUtil.mkColoredHorizontalLineDiff(actualOutput.mkString(EOL), expectedOutput.mkString(EOL))) fail(s"Error in script $name, expected output did not match actual") end if From 4649d5e7abe5fe968621f700369df90dd22ea6b4 Mon Sep 17 00:00:00 2001 From: aherlihy Date: Wed, 26 Feb 2025 10:36:30 +0100 Subject: [PATCH 14/18] Rename :require to :jar and deprecate :require --- .../src/dotty/tools/repl/ParseResult.scala | 10 +++++- .../src/dotty/tools/repl/ReplDriver.scala | 4 +++ compiler/test-resources/jars/MyLibrary.scala | 2 +- compiler/test-resources/jars/MyLibrary2.scala | 2 +- compiler/test-resources/repl/require-command | 13 -------- compiler/test-resources/repl/require-errors | 8 ----- compiler/test-resources/repl/require-multiple | 32 ------------------- .../dotty/tools/repl/TabcompleteTests.scala | 1 + 8 files changed, 16 insertions(+), 56 deletions(-) delete mode 100644 compiler/test-resources/repl/require-command delete mode 100644 compiler/test-resources/repl/require-errors delete mode 100644 compiler/test-resources/repl/require-multiple diff --git a/compiler/src/dotty/tools/repl/ParseResult.scala b/compiler/src/dotty/tools/repl/ParseResult.scala index f61d2be5274d..ae21f735edae 100644 --- a/compiler/src/dotty/tools/repl/ParseResult.scala +++ b/compiler/src/dotty/tools/repl/ParseResult.scala @@ -52,13 +52,20 @@ object Load { val command: String = ":load" } -/** `:require ` adds a jar to the classpath +/** `:require` is a deprecated alias for :jar` */ case class Require(path: String) extends Command object Require { val command: String = ":require" } +/** `:jar ` adds a jar to the classpath + */ +case class JarCmd(path: String) extends Command +object JarCmd { + val command: String = ":jar" +} + /** `:kind ` display the kind of a type. see also :help kind */ case class KindOf(expr: String) extends Command @@ -152,6 +159,7 @@ object ParseResult { Help.command -> (_ => Help), Reset.command -> (arg => Reset(arg)), Imports.command -> (_ => Imports), + JarCmd.command -> (arg => JarCmd(arg)), KindOf.command -> (arg => KindOf(arg)), Load.command -> (arg => Load(arg)), Require.command -> (arg => Require(arg)), diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 12065be7cdd0..d47892b64e89 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -515,6 +515,10 @@ class ReplDriver(settings: Array[String], } case Require(path) => + out.println(":require has been deprecated and replaced with :jar. Please use :jar") + state + + case JarCmd(path) => val jarFile = AbstractFile.getDirectory(path) if (jarFile == null) out.println(s"""Cannot add "$path" to classpath.""") diff --git a/compiler/test-resources/jars/MyLibrary.scala b/compiler/test-resources/jars/MyLibrary.scala index cc492696c139..bd752d2df864 100644 --- a/compiler/test-resources/jars/MyLibrary.scala +++ b/compiler/test-resources/jars/MyLibrary.scala @@ -1,5 +1,5 @@ /** - * JAR used for testing repl :require + * JAR used for testing repl :jar * Generated using: mkdir out; scalac -d out MyLibrary.scala; jar cf mylibrary.jar -C out . */ package mylibrary diff --git a/compiler/test-resources/jars/MyLibrary2.scala b/compiler/test-resources/jars/MyLibrary2.scala index ca288fcfbf85..df5a944c0bcf 100644 --- a/compiler/test-resources/jars/MyLibrary2.scala +++ b/compiler/test-resources/jars/MyLibrary2.scala @@ -1,5 +1,5 @@ /** - * JAR used for testing repl :require + * JAR used for testing repl :jar * Generated using: mkdir out2; scalac -d out MyLibrary2.scala; jar cf mylibrary2.jar -C out2 . */ package mylibrary2 diff --git a/compiler/test-resources/repl/require-command b/compiler/test-resources/repl/require-command deleted file mode 100644 index 1af49ccff6fb..000000000000 --- a/compiler/test-resources/repl/require-command +++ /dev/null @@ -1,13 +0,0 @@ -scala> val z = 1 -val z: Int = 1 - -scala>:require sbt-test/source-dependencies/canon/actual/a.jar -Added 'sbt-test/source-dependencies/canon/actual/a.jar' to classpath. - -scala> import A.x - -scala> x -val res0: Int = 3 - -scala> z -val res1: Int = 1 diff --git a/compiler/test-resources/repl/require-errors b/compiler/test-resources/repl/require-errors deleted file mode 100644 index 8e26c673f9b3..000000000000 --- a/compiler/test-resources/repl/require-errors +++ /dev/null @@ -1,8 +0,0 @@ -scala>:require path/does/not/exist -Cannot add "path/does/not/exist" to classpath. - -scala>:require sbt-test/source-dependencies/canon/actual/a.jar -Added 'sbt-test/source-dependencies/canon/actual/a.jar' to classpath. - -scala>:require sbt-test/source-dependencies/canon/actual/a.jar -The path 'sbt-test/source-dependencies/canon/actual/a.jar' cannot be loaded, it contains a classfile that already exists on the classpath: sbt-test/source-dependencies/canon/actual/a.jar(A.class) diff --git a/compiler/test-resources/repl/require-multiple b/compiler/test-resources/repl/require-multiple deleted file mode 100644 index 3803234141d6..000000000000 --- a/compiler/test-resources/repl/require-multiple +++ /dev/null @@ -1,32 +0,0 @@ -scala> val z = 1 -val z: Int = 1 - -scala>:require compiler/test-resources/jars/mylibrary.jar -Added 'compiler/test-resources/jars/mylibrary.jar' to classpath. - -scala> import mylibrary.Utils - -scala> Utils.greet("Alice") -val res0: String = Hello, Alice! - -scala>:require compiler/test-resources/jars/mylibrary2.jar -Added 'compiler/test-resources/jars/mylibrary2.jar' to classpath. - -scala> import mylibrary2.Utils2 - -scala> Utils2.greet("Alice") -val res1: String = Greetings, Alice! - -scala> Utils.greet("Alice") -val res2: String = Hello, Alice! - -scala> import mylibrary.Utils.greet - -scala> greet("Tom") -val res3: String = Hello, Tom! - -scala> Utils.greet("Alice") -val res4: String = Hello, Alice! - -scala> z -val res5: Int = 1 diff --git a/compiler/test/dotty/tools/repl/TabcompleteTests.scala b/compiler/test/dotty/tools/repl/TabcompleteTests.scala index 328725cf411a..3e537d195bdf 100644 --- a/compiler/test/dotty/tools/repl/TabcompleteTests.scala +++ b/compiler/test/dotty/tools/repl/TabcompleteTests.scala @@ -213,6 +213,7 @@ class TabcompleteTests extends ReplTest { ":exit", ":help", ":imports", + ":jar", ":kind", ":load", ":quit", From 46aef8759adfd970d062e4980ab0bb6e82f0d6fc Mon Sep 17 00:00:00 2001 From: anna herlihy Date: Tue, 4 Mar 2025 12:54:24 +0100 Subject: [PATCH 15/18] Update compiler/src/dotty/tools/repl/ReplDriver.scala MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Oliver Bračevac --- compiler/src/dotty/tools/repl/ReplDriver.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index d47892b64e89..228992f16a7b 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -73,7 +73,6 @@ case class State(objectIndex: Int, quiet: Boolean, context: Context): def validObjectIndexes = (1 to objectIndex).filterNot(invalidObjectIndexes.contains(_)) - //def copy() = this /** Main REPL instance, orchestrating input, compilation and presentation */ class ReplDriver(settings: Array[String], From 7b142b821126771a6a986f9ad998f870f8f5d356 Mon Sep 17 00:00:00 2001 From: aherlihy Date: Tue, 4 Mar 2025 13:00:15 +0100 Subject: [PATCH 16/18] re-add tests --- compiler/test-resources/repl/jar-command | 13 +++++++++ compiler/test-resources/repl/jar-errors | 11 ++++++++ compiler/test-resources/repl/jar-multiple | 32 +++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 compiler/test-resources/repl/jar-command create mode 100644 compiler/test-resources/repl/jar-errors create mode 100644 compiler/test-resources/repl/jar-multiple diff --git a/compiler/test-resources/repl/jar-command b/compiler/test-resources/repl/jar-command new file mode 100644 index 000000000000..b0ded22d3654 --- /dev/null +++ b/compiler/test-resources/repl/jar-command @@ -0,0 +1,13 @@ +scala> val z = 1 +val z: Int = 1 + +scala>:jar sbt-test/source-dependencies/canon/actual/a.jar +Added 'sbt-test/source-dependencies/canon/actual/a.jar' to classpath. + +scala> import A.x + +scala> x +val res0: Int = 3 + +scala> z +val res1: Int = 1 diff --git a/compiler/test-resources/repl/jar-errors b/compiler/test-resources/repl/jar-errors new file mode 100644 index 000000000000..53a3ba965c94 --- /dev/null +++ b/compiler/test-resources/repl/jar-errors @@ -0,0 +1,11 @@ +scala>:jar path/does/not/exist +Cannot add "path/does/not/exist" to classpath. + +scala>:jar sbt-test/source-dependencies/canon/actual/a.jar +Added 'sbt-test/source-dependencies/canon/actual/a.jar' to classpath. + +scala>:jar sbt-test/source-dependencies/canon/actual/a.jar +The path 'sbt-test/source-dependencies/canon/actual/a.jar' cannot be loaded, it contains a classfile that already exists on the classpath: sbt-test/source-dependencies/canon/actual/a.jar(A.class) + +scala>:require sbt-test/source-dependencies/canon/actual/a.jar +:require has been deprecated and replaced with :jar. Please use :jar \ No newline at end of file diff --git a/compiler/test-resources/repl/jar-multiple b/compiler/test-resources/repl/jar-multiple new file mode 100644 index 000000000000..453ccc40dbf6 --- /dev/null +++ b/compiler/test-resources/repl/jar-multiple @@ -0,0 +1,32 @@ +scala> val z = 1 +val z: Int = 1 + +scala>:jar compiler/test-resources/jars/mylibrary.jar +Added 'compiler/test-resources/jars/mylibrary.jar' to classpath. + +scala> import mylibrary.Utils + +scala> Utils.greet("Alice") +val res0: String = Hello, Alice! + +scala>:jar compiler/test-resources/jars/mylibrary2.jar +Added 'compiler/test-resources/jars/mylibrary2.jar' to classpath. + +scala> import mylibrary2.Utils2 + +scala> Utils2.greet("Alice") +val res1: String = Greetings, Alice! + +scala> Utils.greet("Alice") +val res2: String = Hello, Alice! + +scala> import mylibrary.Utils.greet + +scala> greet("Tom") +val res3: String = Hello, Tom! + +scala> Utils.greet("Alice") +val res4: String = Hello, Alice! + +scala> z +val res5: Int = 1 From 4bb08f7c7fe315a7281879a01d6a8d200b40df29 Mon Sep 17 00:00:00 2001 From: anna herlihy Date: Mon, 10 Mar 2025 13:23:25 +0100 Subject: [PATCH 17/18] Update compiler/src/dotty/tools/repl/ReplDriver.scala Co-authored-by: Piotr Chabelski --- compiler/src/dotty/tools/repl/ReplDriver.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 228992f16a7b..cc0f6ca4346a 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -514,7 +514,7 @@ class ReplDriver(settings: Array[String], } case Require(path) => - out.println(":require has been deprecated and replaced with :jar. Please use :jar") + out.println(":require is no longer supported, but has been replaced with :jar. Please use :jar") state case JarCmd(path) => From 42c4e89c738042f32690de7a2a70fad68bfeb213 Mon Sep 17 00:00:00 2001 From: aherlihy Date: Mon, 10 Mar 2025 13:33:23 +0100 Subject: [PATCH 18/18] Add wrapper around :jar to avoid crashing repl on invalid jar --- .../src/dotty/tools/repl/ReplDriver.scala | 50 ++++++++++--------- compiler/test-resources/repl/jar-errors | 2 +- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index cc0f6ca4346a..3c8618cf3198 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -527,8 +527,6 @@ class ReplDriver(settings: Array[String], if (f.isClassContainer) f.iterator.flatMap(flatten) else Iterator(f) - val entries = flatten(jarFile) - def tryClassLoad(classFile: AbstractFile): Option[String] = { val input = classFile.input try { @@ -543,27 +541,33 @@ class ReplDriver(settings: Array[String], } } - val existingClass = entries.filter(_.ext.isClass).find(tryClassLoad(_).isDefined) - if (existingClass.nonEmpty) - out.println(s"The path '$path' cannot be loaded, it contains a classfile that already exists on the classpath: ${existingClass.get}") - state - else inContext(state.context): - val jarClassPath = ClassPathFactory.newClassPath(jarFile) - val prevOutputDir = ctx.settings.outputDir.value - - // add to compiler class path - ctx.platform.addToClassPath(jarClassPath) - SymbolLoaders.mergeNewEntries(defn.RootClass, ClassPath.RootPackage, jarClassPath, ctx.platform.classPath) - - // new class loader with previous output dir and specified jar - val prevClassLoader = rendering.classLoader() - val jarClassLoader = fromURLsParallelCapable( - jarClassPath.asURLs, prevClassLoader) - rendering.myClassLoader = new AbstractFileClassLoader( - prevOutputDir, jarClassLoader) - - out.println(s"Added '$path' to classpath.") - state + try { + val entries = flatten(jarFile) + + val existingClass = entries.filter(_.ext.isClass).find(tryClassLoad(_).isDefined) + if (existingClass.nonEmpty) + out.println(s"The path '$path' cannot be loaded, it contains a classfile that already exists on the classpath: ${existingClass.get}") + else inContext(state.context): + val jarClassPath = ClassPathFactory.newClassPath(jarFile) + val prevOutputDir = ctx.settings.outputDir.value + + // add to compiler class path + ctx.platform.addToClassPath(jarClassPath) + SymbolLoaders.mergeNewEntries(defn.RootClass, ClassPath.RootPackage, jarClassPath, ctx.platform.classPath) + + // new class loader with previous output dir and specified jar + val prevClassLoader = rendering.classLoader() + val jarClassLoader = fromURLsParallelCapable( + jarClassPath.asURLs, prevClassLoader) + rendering.myClassLoader = new AbstractFileClassLoader( + prevOutputDir, jarClassLoader) + + out.println(s"Added '$path' to classpath.") + } catch { + case e: Throwable => + out.println(s"Failed to load '$path' to classpath: ${e.getMessage}") + } + state case KindOf(expr) => out.println(s"""The :kind command is not currently supported.""") diff --git a/compiler/test-resources/repl/jar-errors b/compiler/test-resources/repl/jar-errors index 53a3ba965c94..7d5720fcb233 100644 --- a/compiler/test-resources/repl/jar-errors +++ b/compiler/test-resources/repl/jar-errors @@ -8,4 +8,4 @@ scala>:jar sbt-test/source-dependencies/canon/actual/a.jar The path 'sbt-test/source-dependencies/canon/actual/a.jar' cannot be loaded, it contains a classfile that already exists on the classpath: sbt-test/source-dependencies/canon/actual/a.jar(A.class) scala>:require sbt-test/source-dependencies/canon/actual/a.jar -:require has been deprecated and replaced with :jar. Please use :jar \ No newline at end of file +:require is no longer supported, but has been replaced with :jar. Please use :jar \ No newline at end of file