Skip to content

Commit

Permalink
Fix :require adding classes/pkgs
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand committed Feb 13, 2025
1 parent 92232be commit 40593a5
Show file tree
Hide file tree
Showing 9 changed files with 52 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/config/JavaPlatform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/config/Platform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 1 addition & 5 deletions compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.*
Expand Down Expand Up @@ -919,11 +918,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()
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2220,7 +2220,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
Expand Down
29 changes: 28 additions & 1 deletion compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down
6 changes: 1 addition & 5 deletions compiler/src/dotty/tools/repl/ReplCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,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
Expand Down
35 changes: 16 additions & 19 deletions compiler/src/dotty/tools/repl/ReplDriver.scala
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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],
Expand All @@ -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
Expand All @@ -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 =>
Expand Down Expand Up @@ -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 TypeOf(expr) =>
expr match {
Expand Down
5 changes: 1 addition & 4 deletions compiler/test/dotty/tools/repl/ReplTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 40593a5

Please sign in to comment.