Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ lazy val core = projectMatrix
BuildInfoKey.action("scala213")(Dependencies.scala213),
BuildInfoKey.action("scala30")(Dependencies.scala30),
BuildInfoKey.action("scala31Plus")(Dependencies.scala31Plus),
BuildInfoKey.action("scala34Plus")(Dependencies.scala34Plus)
BuildInfoKey.action("scala34Plus")(Dependencies.scala34Plus),
BuildInfoKey.action("scala372Plus")(Dependencies.scala372Plus)
),
buildInfoPackage := "ch.epfl.scala.debugadapter"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,22 @@ final case class Module(
final case class Library(artifactId: String, version: String, absolutePath: Path, sourceEntries: Seq[SourceEntry])
extends ManagedEntry {
override def name: String = artifactId

private def versionSuffix = artifactId.split('_').lastOption

override def isScala2: Boolean =
scalaVersion.exists(_.isScala2) || versionSuffix.exists(_.startsWith("2."))
override def isScala3: Boolean =
scalaVersion.exists(_.isScala3) || versionSuffix.exists(_.startsWith("3"))
override def isJava: Boolean =
scalaVersion.isEmpty && !versionSuffix.exists(suffix => suffix.startsWith("2.") || suffix.startsWith("3."))

def scalaVersion: Option[ScalaVersion] = {
if (artifactId == "scala-library") Some(ScalaVersion(version))
else {
artifactId
.split('_')
.lastOption
.filter(bv => bv.startsWith("2.12") || bv.startsWith("2.13") || bv.startsWith("3"))
.map(ScalaVersion.apply)
}
if (
artifactId == "scala-library" || artifactId.startsWith("scala3-library_3") ||
artifactId.startsWith("scala-compiler") || artifactId.startsWith("scala-compiler_3")
)
Some(ScalaVersion(version))
else None
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ case class ScalaVersion(value: String) extends Ordered[ScalaVersion] {
.get
}

def minor: Int = parts match {
case (_, minor, _) => minor
}

override def compare(that: ScalaVersion): Int =
(parts, that.parts) match {
case ((x, _, _), (y, _, _)) if x != y => x - y
Expand All @@ -41,5 +45,6 @@ object ScalaVersion {
val `3.0` = ScalaVersion(BuildInfo.scala30)
val `3.1+` = ScalaVersion(BuildInfo.scala31Plus)
val `3.4+` = ScalaVersion(BuildInfo.scala34Plus)
val `3.7.2+` = ScalaVersion(BuildInfo.scala372Plus)
val `3.5.0` = ScalaVersion("3.5.0")
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,11 @@ object DebugTools {
}
for {
classLoader <- if (entry.isScala2) scala2Loader else if (entry.isScala3) scala3Loader else None
scalaVersion <- entry.scalaVersion
scalaVersion <- entry.scalaVersion.orElse {
if (entry.isScala2) Some(scala2Version)
else if (entry.isScala3) Some(scala3Version)
else None
}
compiler <- ExpressionCompiler(scalaVersion, scalacOptions, classPath, classLoader)
.warnFailure(logger, s"Cannot load expression compiler of Scala $scalaVersion")
} yield entry -> compiler
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package ch.epfl.scala.debugadapter.internal

import ch.epfl.scala.debugadapter.BuildInfo
import ch.epfl.scala.debugadapter.ClassEntry
import ch.epfl.scala.debugadapter.DebugConfig
import ch.epfl.scala.debugadapter.Debuggee
Expand Down Expand Up @@ -143,15 +142,16 @@ private[internal] class EvaluationProvider(
case m: ManagedEntry =>
m.scalaVersion match {
case None =>
s"Failed resolving scala-expression-compiler:${BuildInfo.version} for ${entry.name} (Missing Scala Version)"
s"Failed resolving expression compiler for ${entry.name} (Missing Scala Version)"
case Some(sv) =>
s"""|Failed resolving scala-expression-compiler:${BuildInfo.version} for Scala $sv.
val compilerSource = if (sv.isScala3 && sv.minor >= 7) "scala3-compiler" else "scala-expression-compiler"
s"""|Failed resolving $compilerSource for Scala $sv.
|Please open an issue at https://github.com/scalacenter/scala-debug-adapter.""".stripMargin
}
case _: JavaRuntime =>
s"Failed resolving scala-expression-compiler:${BuildInfo.version} for ${entry.name} (Missing Scala Version)"
s"Failed resolving expression compiler for ${entry.name} (Missing Scala Version)"
case _ =>
s"Failed resolving scala-expression-compiler:${BuildInfo.version} for ${entry.name} (Unknown Scala Version)"
s"Failed resolving expression compiler for ${entry.name} (Unknown Scala Version)"
}

private def containsMethodCall(tree: RuntimeEvaluationTree): Boolean = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,26 @@ import scala.util.Failure
import scala.util.Success
import scala.util.Try

private[debugadapter] class ExpressionCompiler(
private[debugadapter] trait ExpressionCompiler {
def compile(
outDir: Path,
expressionClassName: String,
sourceFile: Path,
line: Int,
expression: String,
localNames: Set[String],
pckg: String,
testMode: Boolean
): Try[Unit]
}

private[debugadapter] class ExpressionCompilerPre37(
instance: Any,
compileMethod: Method,
val scalaVersion: ScalaVersion,
scalacOptions: Seq[String],
classPath: String
) {
) extends ExpressionCompiler {
def compile(
outDir: Path,
expressionClassName: String,
Expand Down Expand Up @@ -55,6 +68,78 @@ private[debugadapter] class ExpressionCompiler(
}
}

private[debugadapter] class ExpressionCompilerPost37(
instance: Any,
compileMethod: Method,
classLoader: ClassLoader,
val scalaVersion: ScalaVersion,
scalacOptions: Seq[String],
classPath: String
) extends ExpressionCompiler {

private def expressionCompilerConfig(
packageName: String,
outputClassName: String,
breakpointLine: Int,
expression: String,
localVariables: java.util.Set[String],
errorReporter: Consumer[String],
testMode: Boolean
): Object = {
val clazz = Class.forName("dotty.tools.debug.ExpressionCompilerConfig", true, classLoader)
val instance = clazz.getDeclaredConstructor().newInstance()
val withPackageName = clazz.getMethod("withPackageName", classOf[String]).invoke(instance, packageName)
val withOutputClassName =
clazz.getMethod("withOutputClassName", classOf[String]).invoke(withPackageName, outputClassName)
val withBreakpointLine = clazz
.getMethod("withBreakpointLine", classOf[Int])
.invoke(withOutputClassName, Integer.valueOf(breakpointLine))
val withExpression = clazz.getMethod("withExpression", classOf[String]).invoke(withBreakpointLine, expression)
val withLocalVariables =
clazz.getMethod("withLocalVariables", classOf[java.util.Set[String]]).invoke(withExpression, localVariables)
val withErrorReporter =
clazz.getMethod("withErrorReporter", classOf[Consumer[String]]).invoke(withLocalVariables, errorReporter)
withErrorReporter.asInstanceOf[Object]
}

def compile(
outDir: Path,
expressionClassName: String,
sourceFile: Path,
line: Int,
expression: String,
localNames: Set[String],
pckg: String,
testMode: Boolean
): Try[Unit] = {
try {
val errors = Buffer.empty[String]
val configInstance = expressionCompilerConfig(
pckg,
expressionClassName,
line,
expression,
localNames.asJava,
{ error => errors += error }: Consumer[String],
testMode
)
val res = compileMethod
.invoke(
instance,
outDir,
classPath,
scalacOptions.toArray,
sourceFile,
configInstance
)
.asInstanceOf[Boolean]
if (res) Success(()) else Failure(Errors.compilationFailure(errors.toSeq))
} catch {
case cause: InvocationTargetException => Failure(cause.getCause())
}
}
}

private[debugadapter] object ExpressionCompiler {
def apply(
scalaVersion: ScalaVersion,
Expand All @@ -64,13 +149,30 @@ private[debugadapter] object ExpressionCompiler {
): Try[ExpressionCompiler] = {
val className =
if (scalaVersion.isScala2) "scala.tools.nsc.ExpressionCompilerBridge"
else "dotty.tools.dotc.ExpressionCompilerBridge"
else if (scalaVersion.isScala3 && scalaVersion.minor >= 7) {
"dotty.tools.debug.ExpressionCompilerBridge"
} else {
"dotty.tools.dotc.ExpressionCompilerBridge"
}

try {
val clazz = Class.forName(className, true, classLoader)
val instance = clazz.getDeclaredConstructor().newInstance()
val method = clazz.getMethods.find(_.getName == "run").get
Success(new ExpressionCompiler(instance, method, scalaVersion, scalacOptions, classPath))
if (scalaVersion.isScala3 && scalaVersion.minor >= 7) {
Success(
new ExpressionCompilerPost37(
instance,
method,
classLoader,
scalaVersion,
scalacOptions,
classPath
)
)
} else {
Success(new ExpressionCompilerPre37(instance, method, scalaVersion, scalacOptions, classPath))
}
} catch {
case cause: Throwable => Failure(cause)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,36 +25,38 @@ class SbtDebugToolsResolver(
) extends DebugToolsResolver {

override def resolveExpressionCompiler(scalaVersion: ScalaVersion): Try[ClassLoader] = {
val org = BuildInfo.organization
val artifact = s"${BuildInfo.expressionCompilerName}_$scalaVersion"
val version = BuildInfo.version

for (report <- fetchArtifactsOf(org % artifact % version, Seq.empty))
yield
if (scalaInstance.version == scalaVersion.value) {
val expressionCompilerJars = report
.select(
configurationFilter(Runtime.name),
moduleFilter(org, artifact, version) | moduleFilter(
"org.scala-lang.modules",
"scala-collection-compat_2.12"
),
artifactFilter(extension = "jar", classifier = "")
)
.map(_.toURI.toURL)
.toArray
new URLClassLoader(expressionCompilerJars, scalaInstance.loader)
} else {
val expressionCompilerJars = report
.select(
configurationFilter(Runtime.name),
moduleFilter(),
artifactFilter(extension = "jar", classifier = "")
)
.map(_.toURI.toURL)
.toArray
new URLClassLoader(expressionCompilerJars, null)
}
if (scalaVersion.isScala3 && scalaVersion.minor >= 7) {
Success(scalaInstance.loader)
} else {
val (org, artifact, version) =
(BuildInfo.organization, s"${BuildInfo.expressionCompilerName}_$scalaVersion", BuildInfo.version)
for (report <- fetchArtifactsOf(org % artifact % version, Seq.empty))
yield
if (scalaInstance.version == scalaVersion.value) {
val expressionCompilerJars = report
.select(
configurationFilter(Runtime.name),
moduleFilter(org, artifact, version) | moduleFilter(
"org.scala-lang.modules",
"scala-collection-compat_2.12"
),
artifactFilter(extension = "jar", classifier = "")
)
.map(_.toURI.toURL)
.toArray
new URLClassLoader(expressionCompilerJars, scalaInstance.loader)
} else {
val expressionCompilerJars = report
.select(
configurationFilter(Runtime.name),
moduleFilter(),
artifactFilter(extension = "jar", classifier = "")
)
.map(_.toURI.toURL)
.toArray
new URLClassLoader(expressionCompilerJars, null)
}
}
}

override def resolveDecoder(scalaVersion: ScalaVersion): Try[Seq[java.nio.file.Path]] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@ import java.io.File
sealed abstract class ScalaInstance(
val libraryJars: Seq[Library],
compilerJars: Seq[Library],
expressionCompilerJar: Library,
expressionCompilerJar: Option[Library],
val decoderJars: Seq[Library]
) {
val libraryClassLoader = new URLClassLoader(libraryJars.map(_.toURL).toArray, null)
val compilerClassLoader = new URLClassLoader(compilerJars.map(_.toURL).toArray, libraryClassLoader)
val expressionCompilerClassLoader = new URLClassLoader(Array(expressionCompilerJar.toURL), compilerClassLoader)
val expressionCompilerClassLoader = expressionCompilerJar match {
case Some(jar) =>
new URLClassLoader(Array(jar.toURL), compilerClassLoader)
case None =>
compilerClassLoader
}

def compile(
classDir: Path,
Expand All @@ -40,7 +45,7 @@ final class Scala2Instance(
libraryJars: Seq[Library],
compilerJars: Seq[Library],
expressionCompilerJar: Library
) extends ScalaInstance(libraryJars, compilerJars, expressionCompilerJar, Seq.empty) {
) extends ScalaInstance(libraryJars, compilerJars, Some(expressionCompilerJar), Seq.empty) {
override protected def compileInternal(args: Array[String]): Unit = {
val main = compilerClassLoader.loadClass("scala.tools.nsc.Main")
val process = main.getMethod("process", classOf[Array[String]])
Expand All @@ -52,7 +57,7 @@ final class Scala2Instance(
final class Scala3Instance(
libraryJars: Seq[Library],
compilerJars: Seq[Library],
expressionCompilerJar: Library,
expressionCompilerJar: Option[Library],
stepFilterJars: Seq[Library]
) extends ScalaInstance(libraryJars, compilerJars, expressionCompilerJar, stepFilterJars) {
override protected def compileInternal(args: Array[String]): Unit = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,16 @@ object TestingResolver extends DebugToolsResolver {
}

private def fetchScala3(scalaVersion: ScalaVersion): Scala3Instance = {
val expressionCompilerArtifact =
s"${BuildInfo.expressionCompilerName}_${scalaVersion.value}"
val builtInExpressionCompiler = scalaVersion.isScala3 && scalaVersion.minor >= 7
val (expressionCompilerOrg, expressionCompilerArtifact, expressionCompilerVersion) =
if (builtInExpressionCompiler) {
("org.scala-lang", s"scala3-compiler_3", scalaVersion.value)
} else {
(BuildInfo.organization, s"${BuildInfo.expressionCompilerName}_${scalaVersion.value}", BuildInfo.version)
}
val expressionCompilerDep = Dependency(
coursier.Module(Organization(BuildInfo.organization), ModuleName(expressionCompilerArtifact)),
BuildInfo.version
coursier.Module(Organization(expressionCompilerOrg), ModuleName(expressionCompilerArtifact)),
expressionCompilerVersion
)

val decoderDep = Dependency(
Expand All @@ -131,8 +136,11 @@ object TestingResolver extends DebugToolsResolver {
val decoderJars = fetch(decoderDep, tastyDep)
val libraryJars =
jars.filter(jar => jar.name.startsWith("scala-library") || jar.name.startsWith("scala3-library_3"))
val expressionCompilerJar = jars.find(jar => jar.name.startsWith(expressionCompilerArtifact)).get
val compilerJars = jars.filter(jar => !libraryJars.contains(jar) && jar != expressionCompilerJar)
val expressionCompilerJar =
if (builtInExpressionCompiler) None
else Some(jars.find(jar => jar.name.startsWith(expressionCompilerArtifact)).get)
val compilerJars =
jars.filter(jar => !libraryJars.contains(jar) && !expressionCompilerJar.exists(_ == jar))

new Scala3Instance(libraryJars, compilerJars, expressionCompilerJar, decoderJars)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class Scala212EvaluationTests extends ScalaEvaluationTests(ScalaVersion.`2.12`)
class Scala213EvaluationTests extends ScalaEvaluationTests(ScalaVersion.`2.13`)
class Scala31PlusEvaluationTests extends ScalaEvaluationTests(ScalaVersion.`3.1+`)
class Scala34PlusEvaluationTests extends ScalaEvaluationTests(ScalaVersion.`3.4+`)
class Scala372PlusEvaluationTests extends ScalaEvaluationTests(ScalaVersion.`3.7.2+`)

abstract class ScalaEvaluationTests(scalaVersion: ScalaVersion) extends DebugTestSuite {
protected override def defaultConfig: DebugConfig =
Expand Down
Loading
Loading