Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport "use new zinc 1.8 api for VirtualFile" to LTS #19128

Merged
merged 10 commits into from
Dec 8, 2023
Merged
12 changes: 5 additions & 7 deletions compiler/src/dotty/tools/backend/jvm/CodeGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import scala.tools.asm
import scala.tools.asm.tree._
import tpd._
import dotty.tools.io.AbstractFile
import dotty.tools.dotc.util
import dotty.tools.dotc.util.NoSourcePosition


Expand Down Expand Up @@ -106,7 +107,7 @@ class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)(
}

// Creates a callback that will be evaluated in PostProcessor after creating a file
private def onFileCreated(cls: ClassNode, claszSymbol: Symbol, sourceFile: interfaces.SourceFile): AbstractFile => Unit = clsFile => {
private def onFileCreated(cls: ClassNode, claszSymbol: Symbol, sourceFile: util.SourceFile): AbstractFile => Unit = clsFile => {
val (fullClassName, isLocal) = atPhase(sbtExtractDependenciesPhase) {
(ExtractDependencies.classNameAsString(claszSymbol), claszSymbol.isLocal)
}
Expand All @@ -115,12 +116,9 @@ class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)(
if (ctx.compilerCallback != null)
ctx.compilerCallback.onClassGenerated(sourceFile, convertAbstractFile(clsFile), className)

if (ctx.sbtCallback != null) {
val jSourceFile = sourceFile.jfile.orElse(null)
val cb = ctx.sbtCallback
if (isLocal) cb.generatedLocalClass(jSourceFile, clsFile.file)
else cb.generatedNonLocalClass(jSourceFile, clsFile.file, className, fullClassName)
}
ctx.withIncCallback: cb =>
if (isLocal) cb.generatedLocalClass(sourceFile, clsFile.jpath)
else cb.generatedNonLocalClass(sourceFile, clsFile.jpath, className, fullClassName)
}

/** Convert a `dotty.tools.io.AbstractFile` into a
Expand Down
38 changes: 24 additions & 14 deletions compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,25 @@ import scala.annotation.internal.sharable

import DenotTransformers.DenotTransformer
import dotty.tools.dotc.profile.Profiler
import dotty.tools.dotc.sbt.interfaces.IncrementalCallback
import util.Property.Key
import util.Store
import xsbti.AnalysisCallback
import plugins._
import java.util.concurrent.atomic.AtomicInteger
import java.nio.file.InvalidPathException

object Contexts {

private val (compilerCallbackLoc, store1) = Store.empty.newLocation[CompilerCallback]()
private val (sbtCallbackLoc, store2) = store1.newLocation[AnalysisCallback]()
private val (printerFnLoc, store3) = store2.newLocation[Context => Printer](new RefinedPrinter(_))
private val (settingsStateLoc, store4) = store3.newLocation[SettingsState]()
private val (compilationUnitLoc, store5) = store4.newLocation[CompilationUnit]()
private val (runLoc, store6) = store5.newLocation[Run | Null]()
private val (profilerLoc, store7) = store6.newLocation[Profiler]()
private val (notNullInfosLoc, store8) = store7.newLocation[List[NotNullInfo]]()
private val (importInfoLoc, store9) = store8.newLocation[ImportInfo | Null]()
private val (typeAssignerLoc, store10) = store9.newLocation[TypeAssigner](TypeAssigner)
private val (compilerCallbackLoc, store1) = Store.empty.newLocation[CompilerCallback]()
private val (incCallbackLoc, store2) = store1.newLocation[IncrementalCallback | Null]()
private val (printerFnLoc, store3) = store2.newLocation[Context => Printer](new RefinedPrinter(_))
private val (settingsStateLoc, store4) = store3.newLocation[SettingsState]()
private val (compilationUnitLoc, store5) = store4.newLocation[CompilationUnit]()
private val (runLoc, store6) = store5.newLocation[Run | Null]()
private val (profilerLoc, store7) = store6.newLocation[Profiler]()
private val (notNullInfosLoc, store8) = store7.newLocation[List[NotNullInfo]]()
private val (importInfoLoc, store9) = store8.newLocation[ImportInfo | Null]()
private val (typeAssignerLoc, store10) = store9.newLocation[TypeAssigner](TypeAssigner)

private val initialStore = store10

Expand Down Expand Up @@ -164,8 +164,18 @@ object Contexts {
/** The compiler callback implementation, or null if no callback will be called. */
def compilerCallback: CompilerCallback = store(compilerCallbackLoc)

/** The sbt callback implementation if we are run from sbt, null otherwise */
def sbtCallback: AnalysisCallback = store(sbtCallbackLoc)
/** The Zinc callback implementation if we are run from Zinc, null otherwise */
def incCallback: IncrementalCallback | Null = store(incCallbackLoc)

/** Run `op` if there exists an incremental callback */
inline def withIncCallback(inline op: IncrementalCallback => Unit): Unit =
val local = incCallback
if local != null then op(local)

def runZincPhases: Boolean =
def forceRun = settings.YdumpSbtInc.value || settings.YforceSbtPhases.value
val local = incCallback
local != null && local.enabled || forceRun

/** The current plain printer */
def printerFn: Context => Printer = store(printerFnLoc)
Expand Down Expand Up @@ -664,7 +674,7 @@ object Contexts {
}

def setCompilerCallback(callback: CompilerCallback): this.type = updateStore(compilerCallbackLoc, callback)
def setSbtCallback(callback: AnalysisCallback): this.type = updateStore(sbtCallbackLoc, callback)
def setIncCallback(callback: IncrementalCallback): this.type = updateStore(incCallbackLoc, callback)
def setPrinterFn(printer: Context => Printer): this.type = updateStore(printerFnLoc, printer)
def setSettings(settingsState: SettingsState): this.type = updateStore(settingsStateLoc, settingsState)
def setRun(run: Run | Null): this.type = updateStore(runLoc, run)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/sbt/APIUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ object APIUtils {
* a dummy empty class can be registered instead, using this method.
*/
def registerDummyClass(classSym: ClassSymbol)(using Context): Unit = {
if (ctx.sbtCallback != null) {
ctx.withIncCallback { cb =>
val classLike = emptyClassLike(classSym)
ctx.sbtCallback.api(ctx.compilationUnit.source.file.file, classLike)
cb.api(ctx.compilationUnit.source, classLike)
}
}

Expand Down
20 changes: 9 additions & 11 deletions compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ class ExtractAPI extends Phase {
override def description: String = ExtractAPI.description

override def isRunnable(using Context): Boolean = {
def forceRun = ctx.settings.YdumpSbtInc.value || ctx.settings.YforceSbtPhases.value
super.isRunnable && (ctx.sbtCallback != null || forceRun)
super.isRunnable && ctx.runZincPhases
}

// Check no needed. Does not transform trees
Expand All @@ -65,28 +64,27 @@ class ExtractAPI extends Phase {

override def run(using Context): Unit = {
val unit = ctx.compilationUnit
val sourceFile = unit.source.file
if (ctx.sbtCallback != null)
ctx.sbtCallback.startSource(sourceFile.file)
val sourceFile = unit.source
ctx.withIncCallback: cb =>
cb.startSource(sourceFile)

val apiTraverser = new ExtractAPICollector
val classes = apiTraverser.apiSource(unit.tpdTree)
val mainClasses = apiTraverser.mainClasses

if (ctx.settings.YdumpSbtInc.value) {
// Append to existing file that should have been created by ExtractDependencies
val pw = new PrintWriter(File(sourceFile.jpath).changeExtension("inc").toFile
val pw = new PrintWriter(File(sourceFile.file.jpath).changeExtension("inc").toFile
.bufferedWriter(append = true), true)
try {
classes.foreach(source => pw.println(DefaultShowAPI(source)))
} finally pw.close()
}

if ctx.sbtCallback != null &&
!ctx.compilationUnit.suspendedAtInliningPhase // already registered before this unit was suspended
then
classes.foreach(ctx.sbtCallback.api(sourceFile.file, _))
mainClasses.foreach(ctx.sbtCallback.mainClass(sourceFile.file, _))
ctx.withIncCallback: cb =>
if !ctx.compilationUnit.suspendedAtInliningPhase then // already registered before this unit was suspended
classes.foreach(cb.api(sourceFile, _))
mainClasses.foreach(cb.mainClass(sourceFile, _))
}
}

Expand Down
35 changes: 13 additions & 22 deletions compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package sbt
import scala.language.unsafeNulls

import java.io.File
import java.nio.file.Path
import java.util.{Arrays, EnumSet}

import dotty.tools.dotc.ast.tpd
Expand Down Expand Up @@ -54,8 +55,7 @@ class ExtractDependencies extends Phase {
override def description: String = ExtractDependencies.description

override def isRunnable(using Context): Boolean = {
def forceRun = ctx.settings.YdumpSbtInc.value || ctx.settings.YforceSbtPhases.value
super.isRunnable && (ctx.sbtCallback != null || forceRun)
super.isRunnable && ctx.runZincPhases
}

// Check no needed. Does not transform trees
Expand Down Expand Up @@ -90,18 +90,16 @@ class ExtractDependencies extends Phase {
} finally pw.close()
}

if (ctx.sbtCallback != null) {
ctx.withIncCallback: cb =>
collector.usedNames.foreach {
case (clazz, usedNames) =>
val className = classNameAsString(clazz)
usedNames.names.foreach {
case (usedName, scopes) =>
ctx.sbtCallback.usedName(className, usedName.toString, scopes)
cb.usedName(className, usedName.toString, scopes)
}
}

collector.dependencies.foreach(recordDependency)
}
}

/*
Expand All @@ -111,27 +109,20 @@ class ExtractDependencies extends Phase {
*/
def recordDependency(dep: ClassDependency)(using Context): Unit = {
val fromClassName = classNameAsString(dep.from)
val sourceFile = ctx.compilationUnit.source.file.file
val sourceFile = ctx.compilationUnit.source

def binaryDependency(file: File, binaryClassName: String) =
ctx.sbtCallback.binaryDependency(file, binaryClassName, fromClassName, sourceFile, dep.context)
def binaryDependency(file: Path, binaryClassName: String) =
ctx.withIncCallback(_.binaryDependency(file, binaryClassName, fromClassName, sourceFile, dep.context))

def processExternalDependency(depFile: AbstractFile, binaryClassName: String) = {
depFile match {
case ze: ZipArchive#Entry => // The dependency comes from a JAR
ze.underlyingSource match
case Some(zip) if zip.file != null =>
binaryDependency(zip.file, binaryClassName)
case Some(zip) if zip.jpath != null =>
binaryDependency(zip.jpath, binaryClassName)
case _ =>
case pf: PlainFile => // The dependency comes from a class file
// FIXME: pf.file is null for classfiles coming from the modulepath
// (handled by JrtClassPath) because they cannot be represented as
// java.io.File, since the `binaryDependency` callback must take a
// java.io.File, this means that we cannot record dependencies coming
// from the modulepath. For now this isn't a big deal since we only
// support having the standard Java library on the modulepath.
if pf.file != null then
binaryDependency(pf.file, binaryClassName)
case pf: PlainFile => // The dependency comes from a class file, Zinc handles JRT filesystem
binaryDependency(pf.jpath, binaryClassName)
case _ =>
internalError(s"Ignoring dependency $depFile of unknown class ${depFile.getClass}}", dep.from.srcPos)
}
Expand All @@ -144,11 +135,11 @@ class ExtractDependencies extends Phase {
if (depFile.extension == "class") {
// Dependency is external -- source is undefined
processExternalDependency(depFile, dep.to.binaryClassName)
} else if (allowLocal || depFile.file != sourceFile) {
} else if (allowLocal || depFile != sourceFile.file) {
// We cannot ignore dependencies coming from the same source file because
// the dependency info needs to propagate. See source-dependencies/trait-trait-211.
val toClassName = classNameAsString(dep.to)
ctx.sbtCallback.classDependency(toClassName, fromClassName, dep.context)
ctx.withIncCallback(_.classDependency(toClassName, fromClassName, dep.context))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package dotty.tools.dotc.sbt.interfaces;

import dotty.tools.dotc.util.SourceFile;

import java.util.EnumSet;
import java.nio.file.Path;

/* User code should not implement this interface, it is intended to be a wrapper around xsbti.AnalysisCallback. */
public interface IncrementalCallback {
default void api(SourceFile sourceFile, xsbti.api.ClassLike classApi) {
}

default void startSource(SourceFile sourceFile) {
}

default void mainClass(SourceFile sourceFile, String className) {
}

default boolean enabled() {
return false;
}

default void usedName(String className, String name, EnumSet<xsbti.UseScope> useScopes) {
}

default void binaryDependency(Path onBinaryEntry, String onBinaryClassName, String fromClassName,
SourceFile fromSourceFile, xsbti.api.DependencyContext context) {
}

default void classDependency(String onClassName, String sourceClassName, xsbti.api.DependencyContext context) {
}

default void generatedLocalClass(SourceFile source, Path classFile) {
}

default void generatedNonLocalClass(SourceFile source, Path classFile, String binaryClassName,
String srcClassName) {
}
}
41 changes: 35 additions & 6 deletions project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,33 @@ object Build {
recur(lines, false)
}

/** replace imports of `com.google.protobuf.*` with compiler implemented version */
def replaceProtobuf(lines: List[String]): List[String] = {
def recur(ls: List[String]): List[String] = ls match {
case l :: rest =>
val lt = l.trim()
if (lt.isEmpty || lt.startsWith("package ") || lt.startsWith("import ")) {
val newLine =
if (lt.startsWith("import com.google.protobuf.")) {
if (lt == "import com.google.protobuf.CodedInputStream") {
"import dotty.tools.dotc.semanticdb.internal.SemanticdbInputStream as CodedInputStream"
} else if (lt == "import com.google.protobuf.CodedOutputStream") {
"import dotty.tools.dotc.semanticdb.internal.SemanticdbOutputStream as CodedOutputStream"
} else {
l
}
} else {
l
}
newLine :: recur(rest)
} else {
ls // don't check rest of file
}
case _ => ls
}
recur(lines)
}

// Settings shared between scala3-compiler and scala3-compiler-bootstrapped
lazy val commonDottyCompilerSettings = Seq(
// Generate compiler.properties, used by sbt
Expand All @@ -551,7 +578,7 @@ object Build {
// get libraries onboard
libraryDependencies ++= Seq(
"org.scala-lang.modules" % "scala-asm" % "9.5.0-scala-1", // used by the backend
Dependencies.oldCompilerInterface, // we stick to the old version to avoid deprecation warnings
Dependencies.compilerInterface,
"org.jline" % "jline-reader" % "3.19.0", // used by the REPL
"org.jline" % "jline-terminal" % "3.19.0",
"org.jline" % "jline-terminal-jna" % "3.19.0", // needed for Windows
Expand Down Expand Up @@ -668,7 +695,8 @@ object Build {
val dottyTastyInspector = jars("scala3-tasty-inspector")
val dottyInterfaces = jars("scala3-interfaces")
val tastyCore = jars("tasty-core")
run(insertClasspathInArgs(args1, List(dottyCompiler, dottyInterfaces, asm, dottyStaging, dottyTastyInspector, tastyCore).mkString(File.pathSeparator)))
val compilerInterface = findArtifactPath(externalDeps, "compiler-interface")
run(insertClasspathInArgs(args1, List(dottyCompiler, dottyInterfaces, asm, dottyStaging, dottyTastyInspector, tastyCore, compilerInterface).mkString(File.pathSeparator)))
} else run(args)
},

Expand Down Expand Up @@ -707,7 +735,8 @@ object Build {
val dottyTastyInspector = jars("scala3-tasty-inspector")
val tastyCore = jars("tasty-core")
val asm = findArtifactPath(externalDeps, "scala-asm")
extraClasspath ++= Seq(dottyCompiler, dottyInterfaces, asm, dottyStaging, dottyTastyInspector, tastyCore)
val compilerInterface = findArtifactPath(externalDeps, "compiler-interface")
extraClasspath ++= Seq(dottyCompiler, dottyInterfaces, asm, dottyStaging, dottyTastyInspector, tastyCore, compilerInterface)
}

val fullArgs = main :: (if (printTasty) args else insertClasspathInArgs(args, extraClasspath.mkString(File.pathSeparator)))
Expand Down Expand Up @@ -1051,8 +1080,7 @@ object Build {
// when sbt reads the settings.
Test / test := (LocalProject("scala3-sbt-bridge-tests") / Test / test).value,

// The `newCompilerInterface` is backward compatible with the `oldCompilerInterface`
libraryDependencies += Dependencies.newCompilerInterface % Provided
libraryDependencies += Dependencies.compilerInterface % Provided
)

// We use a separate project for the bridge tests since they can only be run
Expand Down Expand Up @@ -1134,7 +1162,8 @@ object Build {
val mtagsSharedSources = (targetDir ** "*.scala").get.toSet
mtagsSharedSources.foreach(f => {
val lines = IO.readLines(f)
IO.writeLines(f, insertUnsafeNullsImport(lines))
val substitutions = (replaceProtobuf(_)) andThen (insertUnsafeNullsImport(_))
IO.writeLines(f, substitutions(lines))
})
mtagsSharedSources
} (Set(mtagsSharedSourceJar)).toSeq
Expand Down
3 changes: 1 addition & 2 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,5 @@ object Dependencies {
"com.vladsch.flexmark" % "flexmark-ext-yaml-front-matter" % flexmarkVersion,
)

val newCompilerInterface = "org.scala-sbt" % "compiler-interface" % "1.9.0"
val oldCompilerInterface = "org.scala-sbt" % "compiler-interface" % "1.3.5"
val compilerInterface = "org.scala-sbt" % "compiler-interface" % "1.9.0"
}
Loading