Skip to content

Commit ef20b5a

Browse files
committed
Merge pull request #884 from tgodzik/update-evaluator
improvement: Use expression evaluator from the compiler for 3.7.0+
2 parents 2373bb9 + ca0672f commit ef20b5a

File tree

13 files changed

+209
-66
lines changed

13 files changed

+209
-66
lines changed

.github/workflows/release-expression-compiler.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,21 +49,21 @@ jobs:
4949
VERSION='${{ inputs.scala-version }}'
5050
if [[ $VERSION == 2.12.* ]]; then
5151
echo "Using 2.12 publish task"
52-
sbt 'expressionCompiler212/publishSigned;sonaBundle'
52+
sbt 'expressionCompiler212/publishSigned;sonaBundle;sonaRelease'
5353
5454
elif [[ $VERSION == 2.13.* ]]; then
5555
echo "Using 2.13 publish task"
56-
sbt 'expressionCompiler213/publishSigned;sonaBundle'
56+
sbt 'expressionCompiler213/publishSigned;sonaBundle;sonaRelease'
5757
5858
elif [[ $VERSION == 3.0.* ]]; then
5959
echo "Using 3.0 publish task"
60-
sbt 'expressionCompiler30/publishSigned;sonaBundle'
60+
sbt 'expressionCompiler30/publishSigned;sonaBundle;sonaRelease'
6161
6262
elif [[ $VERSION == 3.1.* || $VERSION == 3.2.* || $VERSION == 3.3.* ]]; then
6363
echo "Using 3.1+ publish task"
64-
sbt 'expressionCompiler31Plus/publishSigned;sonaBundle'
64+
sbt 'expressionCompiler31Plus/publishSigned;sonaBundle;sonaRelease'
6565
6666
elif [[ $VERSION == 3.* ]]; then
6767
echo "Using 3.4+ publish task"
68-
sbt 'expressionCompiler34Plus/publishSigned;sonaBundle'
68+
sbt 'expressionCompiler34Plus/publishSigned;sonaBundle;sonaRelease'
6969
fi

build.sbt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ lazy val core = projectMatrix
9898
BuildInfoKey.action("scala213")(Dependencies.scala213),
9999
BuildInfoKey.action("scala30")(Dependencies.scala30),
100100
BuildInfoKey.action("scala31Plus")(Dependencies.scala31Plus),
101-
BuildInfoKey.action("scala34Plus")(Dependencies.scala34Plus)
101+
BuildInfoKey.action("scala34Plus")(Dependencies.scala34Plus),
102+
BuildInfoKey.action("scala372Plus")(Dependencies.scala372Plus)
102103
),
103104
buildInfoPackage := "ch.epfl.scala.debugadapter"
104105
)

modules/core/src/main/scala/ch/epfl/scala/debugadapter/ClassPathEntry.scala

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,22 @@ final case class Module(
3838
final case class Library(artifactId: String, version: String, absolutePath: Path, sourceEntries: Seq[SourceEntry])
3939
extends ManagedEntry {
4040
override def name: String = artifactId
41+
42+
private def versionSuffix = artifactId.split('_').lastOption
43+
44+
override def isScala2: Boolean =
45+
scalaVersion.exists(_.isScala2) || versionSuffix.exists(_.startsWith("2."))
46+
override def isScala3: Boolean =
47+
scalaVersion.exists(_.isScala3) || versionSuffix.exists(_.startsWith("3"))
48+
override def isJava: Boolean =
49+
scalaVersion.isEmpty && !versionSuffix.exists(suffix => suffix.startsWith("2.") || suffix.startsWith("3."))
50+
4151
def scalaVersion: Option[ScalaVersion] = {
42-
if (artifactId == "scala-library") Some(ScalaVersion(version))
43-
else {
44-
artifactId
45-
.split('_')
46-
.lastOption
47-
.filter(bv => bv.startsWith("2.12") || bv.startsWith("2.13") || bv.startsWith("3"))
48-
.map(ScalaVersion.apply)
49-
}
52+
if (
53+
artifactId == "scala-library" || artifactId.startsWith("scala3-library_3") ||
54+
artifactId.startsWith("scala-compiler") || artifactId.startsWith("scala-compiler_3")
55+
)
56+
Some(ScalaVersion(version))
57+
else None
5058
}
5159
}

modules/core/src/main/scala/ch/epfl/scala/debugadapter/ScalaVersion.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ case class ScalaVersion(value: String) extends Ordered[ScalaVersion] {
1919
.get
2020
}
2121

22+
def minor: Int = parts match {
23+
case (_, minor, _) => minor
24+
}
25+
2226
override def compare(that: ScalaVersion): Int =
2327
(parts, that.parts) match {
2428
case ((x, _, _), (y, _, _)) if x != y => x - y
@@ -41,5 +45,6 @@ object ScalaVersion {
4145
val `3.0` = ScalaVersion(BuildInfo.scala30)
4246
val `3.1+` = ScalaVersion(BuildInfo.scala31Plus)
4347
val `3.4+` = ScalaVersion(BuildInfo.scala34Plus)
48+
val `3.7.2+` = ScalaVersion(BuildInfo.scala372Plus)
4449
val `3.5.0` = ScalaVersion("3.5.0")
4550
}

modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/DebugTools.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,11 @@ object DebugTools {
139139
}
140140
for {
141141
classLoader <- if (entry.isScala2) scala2Loader else if (entry.isScala3) scala3Loader else None
142-
scalaVersion <- entry.scalaVersion
142+
scalaVersion <- entry.scalaVersion.orElse {
143+
if (entry.isScala2) Some(scala2Version)
144+
else if (entry.isScala3) Some(scala3Version)
145+
else None
146+
}
143147
compiler <- ExpressionCompiler(scalaVersion, scalacOptions, classPath, classLoader)
144148
.warnFailure(logger, s"Cannot load expression compiler of Scala $scalaVersion")
145149
} yield entry -> compiler

modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/EvaluationProvider.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package ch.epfl.scala.debugadapter.internal
22

3-
import ch.epfl.scala.debugadapter.BuildInfo
43
import ch.epfl.scala.debugadapter.ClassEntry
54
import ch.epfl.scala.debugadapter.DebugConfig
65
import ch.epfl.scala.debugadapter.Debuggee
@@ -143,15 +142,16 @@ private[internal] class EvaluationProvider(
143142
case m: ManagedEntry =>
144143
m.scalaVersion match {
145144
case None =>
146-
s"Failed resolving scala-expression-compiler:${BuildInfo.version} for ${entry.name} (Missing Scala Version)"
145+
s"Failed resolving expression compiler for ${entry.name} (Missing Scala Version)"
147146
case Some(sv) =>
148-
s"""|Failed resolving scala-expression-compiler:${BuildInfo.version} for Scala $sv.
147+
val compilerSource = if (sv.isScala3 && sv.minor >= 7) "scala3-compiler" else "scala-expression-compiler"
148+
s"""|Failed resolving $compilerSource for Scala $sv.
149149
|Please open an issue at https://github.com/scalacenter/scala-debug-adapter.""".stripMargin
150150
}
151151
case _: JavaRuntime =>
152-
s"Failed resolving scala-expression-compiler:${BuildInfo.version} for ${entry.name} (Missing Scala Version)"
152+
s"Failed resolving expression compiler for ${entry.name} (Missing Scala Version)"
153153
case _ =>
154-
s"Failed resolving scala-expression-compiler:${BuildInfo.version} for ${entry.name} (Unknown Scala Version)"
154+
s"Failed resolving expression compiler for ${entry.name} (Unknown Scala Version)"
155155
}
156156

157157
private def containsMethodCall(tree: RuntimeEvaluationTree): Boolean = {

modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/evaluator/ExpressionCompiler.scala

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,26 @@ import scala.util.Failure
1313
import scala.util.Success
1414
import scala.util.Try
1515

16-
private[debugadapter] class ExpressionCompiler(
16+
private[debugadapter] trait ExpressionCompiler {
17+
def compile(
18+
outDir: Path,
19+
expressionClassName: String,
20+
sourceFile: Path,
21+
line: Int,
22+
expression: String,
23+
localNames: Set[String],
24+
pckg: String,
25+
testMode: Boolean
26+
): Try[Unit]
27+
}
28+
29+
private[debugadapter] class ExpressionCompilerPre37(
1730
instance: Any,
1831
compileMethod: Method,
1932
val scalaVersion: ScalaVersion,
2033
scalacOptions: Seq[String],
2134
classPath: String
22-
) {
35+
) extends ExpressionCompiler {
2336
def compile(
2437
outDir: Path,
2538
expressionClassName: String,
@@ -55,6 +68,78 @@ private[debugadapter] class ExpressionCompiler(
5568
}
5669
}
5770

71+
private[debugadapter] class ExpressionCompilerPost37(
72+
instance: Any,
73+
compileMethod: Method,
74+
classLoader: ClassLoader,
75+
val scalaVersion: ScalaVersion,
76+
scalacOptions: Seq[String],
77+
classPath: String
78+
) extends ExpressionCompiler {
79+
80+
private def expressionCompilerConfig(
81+
packageName: String,
82+
outputClassName: String,
83+
breakpointLine: Int,
84+
expression: String,
85+
localVariables: java.util.Set[String],
86+
errorReporter: Consumer[String],
87+
testMode: Boolean
88+
): Object = {
89+
val clazz = Class.forName("dotty.tools.debug.ExpressionCompilerConfig", true, classLoader)
90+
val instance = clazz.getDeclaredConstructor().newInstance()
91+
val withPackageName = clazz.getMethod("withPackageName", classOf[String]).invoke(instance, packageName)
92+
val withOutputClassName =
93+
clazz.getMethod("withOutputClassName", classOf[String]).invoke(withPackageName, outputClassName)
94+
val withBreakpointLine = clazz
95+
.getMethod("withBreakpointLine", classOf[Int])
96+
.invoke(withOutputClassName, Integer.valueOf(breakpointLine))
97+
val withExpression = clazz.getMethod("withExpression", classOf[String]).invoke(withBreakpointLine, expression)
98+
val withLocalVariables =
99+
clazz.getMethod("withLocalVariables", classOf[java.util.Set[String]]).invoke(withExpression, localVariables)
100+
val withErrorReporter =
101+
clazz.getMethod("withErrorReporter", classOf[Consumer[String]]).invoke(withLocalVariables, errorReporter)
102+
withErrorReporter.asInstanceOf[Object]
103+
}
104+
105+
def compile(
106+
outDir: Path,
107+
expressionClassName: String,
108+
sourceFile: Path,
109+
line: Int,
110+
expression: String,
111+
localNames: Set[String],
112+
pckg: String,
113+
testMode: Boolean
114+
): Try[Unit] = {
115+
try {
116+
val errors = Buffer.empty[String]
117+
val configInstance = expressionCompilerConfig(
118+
pckg,
119+
expressionClassName,
120+
line,
121+
expression,
122+
localNames.asJava,
123+
{ error => errors += error }: Consumer[String],
124+
testMode
125+
)
126+
val res = compileMethod
127+
.invoke(
128+
instance,
129+
outDir,
130+
classPath,
131+
scalacOptions.toArray,
132+
sourceFile,
133+
configInstance
134+
)
135+
.asInstanceOf[Boolean]
136+
if (res) Success(()) else Failure(Errors.compilationFailure(errors.toSeq))
137+
} catch {
138+
case cause: InvocationTargetException => Failure(cause.getCause())
139+
}
140+
}
141+
}
142+
58143
private[debugadapter] object ExpressionCompiler {
59144
def apply(
60145
scalaVersion: ScalaVersion,
@@ -64,13 +149,30 @@ private[debugadapter] object ExpressionCompiler {
64149
): Try[ExpressionCompiler] = {
65150
val className =
66151
if (scalaVersion.isScala2) "scala.tools.nsc.ExpressionCompilerBridge"
67-
else "dotty.tools.dotc.ExpressionCompilerBridge"
152+
else if (scalaVersion.isScala3 && scalaVersion.minor >= 7) {
153+
"dotty.tools.debug.ExpressionCompilerBridge"
154+
} else {
155+
"dotty.tools.dotc.ExpressionCompilerBridge"
156+
}
68157

69158
try {
70159
val clazz = Class.forName(className, true, classLoader)
71160
val instance = clazz.getDeclaredConstructor().newInstance()
72161
val method = clazz.getMethods.find(_.getName == "run").get
73-
Success(new ExpressionCompiler(instance, method, scalaVersion, scalacOptions, classPath))
162+
if (scalaVersion.isScala3 && scalaVersion.minor >= 7) {
163+
Success(
164+
new ExpressionCompilerPost37(
165+
instance,
166+
method,
167+
classLoader,
168+
scalaVersion,
169+
scalacOptions,
170+
classPath
171+
)
172+
)
173+
} else {
174+
Success(new ExpressionCompilerPre37(instance, method, scalaVersion, scalacOptions, classPath))
175+
}
74176
} catch {
75177
case cause: Throwable => Failure(cause)
76178
}

modules/sbt-plugin/src/main/scala/ch/epfl/scala/debugadapter/sbtplugin/internal/SbtDebugToolsResolver.scala

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -25,36 +25,38 @@ class SbtDebugToolsResolver(
2525
) extends DebugToolsResolver {
2626

2727
override def resolveExpressionCompiler(scalaVersion: ScalaVersion): Try[ClassLoader] = {
28-
val org = BuildInfo.organization
29-
val artifact = s"${BuildInfo.expressionCompilerName}_$scalaVersion"
30-
val version = BuildInfo.version
31-
32-
for (report <- fetchArtifactsOf(org % artifact % version, Seq.empty))
33-
yield
34-
if (scalaInstance.version == scalaVersion.value) {
35-
val expressionCompilerJars = report
36-
.select(
37-
configurationFilter(Runtime.name),
38-
moduleFilter(org, artifact, version) | moduleFilter(
39-
"org.scala-lang.modules",
40-
"scala-collection-compat_2.12"
41-
),
42-
artifactFilter(extension = "jar", classifier = "")
43-
)
44-
.map(_.toURI.toURL)
45-
.toArray
46-
new URLClassLoader(expressionCompilerJars, scalaInstance.loader)
47-
} else {
48-
val expressionCompilerJars = report
49-
.select(
50-
configurationFilter(Runtime.name),
51-
moduleFilter(),
52-
artifactFilter(extension = "jar", classifier = "")
53-
)
54-
.map(_.toURI.toURL)
55-
.toArray
56-
new URLClassLoader(expressionCompilerJars, null)
57-
}
28+
if (scalaVersion.isScala3 && scalaVersion.minor >= 7) {
29+
Success(scalaInstance.loader)
30+
} else {
31+
val (org, artifact, version) =
32+
(BuildInfo.organization, s"${BuildInfo.expressionCompilerName}_$scalaVersion", BuildInfo.version)
33+
for (report <- fetchArtifactsOf(org % artifact % version, Seq.empty))
34+
yield
35+
if (scalaInstance.version == scalaVersion.value) {
36+
val expressionCompilerJars = report
37+
.select(
38+
configurationFilter(Runtime.name),
39+
moduleFilter(org, artifact, version) | moduleFilter(
40+
"org.scala-lang.modules",
41+
"scala-collection-compat_2.12"
42+
),
43+
artifactFilter(extension = "jar", classifier = "")
44+
)
45+
.map(_.toURI.toURL)
46+
.toArray
47+
new URLClassLoader(expressionCompilerJars, scalaInstance.loader)
48+
} else {
49+
val expressionCompilerJars = report
50+
.select(
51+
configurationFilter(Runtime.name),
52+
moduleFilter(),
53+
artifactFilter(extension = "jar", classifier = "")
54+
)
55+
.map(_.toURI.toURL)
56+
.toArray
57+
new URLClassLoader(expressionCompilerJars, null)
58+
}
59+
}
5860
}
5961

6062
override def resolveDecoder(scalaVersion: ScalaVersion): Try[Seq[java.nio.file.Path]] = {

modules/tests/src/main/scala/ch/epfl/scala/debugadapter/testfmk/ScalaInstance.scala

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,17 @@ import java.io.File
99
sealed abstract class ScalaInstance(
1010
val libraryJars: Seq[Library],
1111
compilerJars: Seq[Library],
12-
expressionCompilerJar: Library,
12+
expressionCompilerJar: Option[Library],
1313
val decoderJars: Seq[Library]
1414
) {
1515
val libraryClassLoader = new URLClassLoader(libraryJars.map(_.toURL).toArray, null)
1616
val compilerClassLoader = new URLClassLoader(compilerJars.map(_.toURL).toArray, libraryClassLoader)
17-
val expressionCompilerClassLoader = new URLClassLoader(Array(expressionCompilerJar.toURL), compilerClassLoader)
17+
val expressionCompilerClassLoader = expressionCompilerJar match {
18+
case Some(jar) =>
19+
new URLClassLoader(Array(jar.toURL), compilerClassLoader)
20+
case None =>
21+
compilerClassLoader
22+
}
1823

1924
def compile(
2025
classDir: Path,
@@ -40,7 +45,7 @@ final class Scala2Instance(
4045
libraryJars: Seq[Library],
4146
compilerJars: Seq[Library],
4247
expressionCompilerJar: Library
43-
) extends ScalaInstance(libraryJars, compilerJars, expressionCompilerJar, Seq.empty) {
48+
) extends ScalaInstance(libraryJars, compilerJars, Some(expressionCompilerJar), Seq.empty) {
4449
override protected def compileInternal(args: Array[String]): Unit = {
4550
val main = compilerClassLoader.loadClass("scala.tools.nsc.Main")
4651
val process = main.getMethod("process", classOf[Array[String]])
@@ -52,7 +57,7 @@ final class Scala2Instance(
5257
final class Scala3Instance(
5358
libraryJars: Seq[Library],
5459
compilerJars: Seq[Library],
55-
expressionCompilerJar: Library,
60+
expressionCompilerJar: Option[Library],
5661
stepFilterJars: Seq[Library]
5762
) extends ScalaInstance(libraryJars, compilerJars, expressionCompilerJar, stepFilterJars) {
5863
override protected def compileInternal(args: Array[String]): Unit = {

0 commit comments

Comments
 (0)