Skip to content

Commit d52fa45

Browse files
authored
Merge pull request #617 from adpi2/3.1.x-backport1
[3.1.x] Backport #594 #615 #616
2 parents 77c9e75 + 365dfd9 commit d52fa45

File tree

12 files changed

+229
-64
lines changed

12 files changed

+229
-64
lines changed

build.sbt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ lazy val expressionCompiler = projectMatrix
169169
case (2, 12) => Seq("2.12.18", "2.12.17", "2.12.16", "2.12.15", "2.12.14", "2.12.13", "2.12.12", "2.12.11")
170170
case (2, 13) => Seq("2.13.11", "2.13.10", "2.13.9", "2.13.8", "2.13.7", "2.13.6", "2.13.5", "2.13.4", "2.13.3")
171171
case (3, 0) => Seq("3.0.2", "3.0.1", "3.0.0")
172-
case (3, _) => Seq("3.3.0", "3.2.2", "3.2.1", "3.2.0", "3.1.3", "3.1.2", "3.1.1", "3.1.0")
172+
case (3, _) => Seq("3.3.1", "3.3.0", "3.2.2", "3.2.1", "3.2.0", "3.1.3", "3.1.2", "3.1.1", "3.1.0")
173173
}
174174
.toSeq
175175
.flatten,
@@ -202,7 +202,7 @@ lazy val scala3StepFilter: Project = project
202202
scalaVersion := Dependencies.scala31Plus,
203203
Compile / doc / sources := Seq.empty,
204204
libraryDependencies ++= Seq(
205-
"ch.epfl.scala" %% "tasty-query" % "0.8.4",
205+
"ch.epfl.scala" %% "tasty-query" % "0.9.3",
206206
"org.scala-lang" %% "tasty-core" % scalaVersion.value,
207207
Dependencies.munit % Test
208208
),

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

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

33
import ch.epfl.scala.debugadapter._
4+
import ch.epfl.scala.debugadapter.internal.ScalaExtension.*
5+
import ch.epfl.scala.debugadapter.internal.scalasig.Decompiler
6+
import ch.epfl.scala.debugadapter.internal.scalasig.ScalaSig
47
import org.objectweb.asm._
58

69
import java.net.URI
710
import java.nio.file._
8-
import scala.jdk.CollectionConverters.*
911
import scala.collection.mutable
10-
import ClassEntryLookUp.readSourceContent
11-
12-
import scala.util.matching.Regex
13-
import ch.epfl.scala.debugadapter.internal.scalasig.ScalaSig
14-
import ch.epfl.scala.debugadapter.internal.scalasig.Decompiler
15-
import ch.epfl.scala.debugadapter.internal.ScalaExtension.*
12+
import scala.jdk.CollectionConverters.*
13+
import scala.util.Properties
1614
import scala.util.Try
15+
import scala.util.matching.Regex
1716

18-
private case class SourceLine(uri: URI, lineNumber: Int)
17+
import ClassEntryLookUp.readSourceContent
1918

2019
private[internal] case class ClassFile(
2120
fullyQualifiedName: String,
@@ -35,35 +34,36 @@ private[internal] case class ClassFile(
3534
private class ClassEntryLookUp(
3635
val entry: ClassEntry,
3736
fqcnToClassFile: Map[String, ClassFile],
38-
sourceUriToSourceFile: Map[URI, SourceFile],
39-
sourceUriToClassFiles: Map[URI, Seq[ClassFile]],
37+
sourceUriToSourceFile: Map[SourceFileKey, SourceFile],
38+
sourceUriToClassFiles: Map[SourceFileKey, Seq[ClassFile]],
4039
classNameToSourceFile: Map[String, SourceFile],
4140
missingSourceFileClassFiles: Seq[ClassFile],
4241
val orphanClassFiles: Seq[ClassFile],
4342
logger: Logger
4443
) {
45-
private val cachedSourceLines = mutable.Map[SourceLine, Seq[ClassFile]]()
44+
private val cachedSourceLines = mutable.Map[SourceLineKey, Seq[ClassFile]]()
4645

47-
def sources: Iterable[URI] = sourceUriToSourceFile.keys
46+
def sources: Iterable[SourceFileKey] = sourceUriToSourceFile.keys
4847
def fullyQualifiedNames: Iterable[String] = {
4948
classNameToSourceFile.keys ++
5049
orphanClassFiles.map(_.fullyQualifiedName) ++
5150
missingSourceFileClassFiles.map(_.fullyQualifiedName)
5251
}
5352

5453
def getFullyQualifiedClassName(
55-
sourceUri: URI,
54+
sourceKey: SourceFileKey,
5655
lineNumber: Int
5756
): Option[String] = {
58-
val line = SourceLine(sourceUri, lineNumber)
57+
val line = SourceLineKey(sourceKey, lineNumber)
5958

6059
if (!cachedSourceLines.contains(line)) {
6160
// read and cache line numbers from class files
62-
sourceUriToClassFiles(sourceUri)
61+
sourceUriToClassFiles
62+
.getOrElse(sourceKey, Nil)
6363
.groupBy(_.classSystem)
6464
.foreach { case (classSystem, classFiles) =>
6565
classSystem
66-
.within((_, root) => loadLineNumbers(root, classFiles, sourceUri))
66+
.within((_, root) => loadLineNumbers(root, classFiles, sourceKey))
6767
.warnFailure(logger, s"Cannot load line numbers in ${classSystem.name}")
6868
}
6969
}
@@ -80,7 +80,7 @@ private class ClassEntryLookUp(
8080
private def loadLineNumbers(
8181
root: Path,
8282
classFiles: Seq[ClassFile],
83-
sourceUri: URI
83+
sourceKey: SourceFileKey
8484
): Unit = {
8585
for (classFile <- classFiles) {
8686
val path = root.resolve(classFile.relativePath)
@@ -108,7 +108,7 @@ private class ClassEntryLookUp(
108108
reader.accept(visitor, 0)
109109

110110
for (n <- lineNumbers) {
111-
val line = SourceLine(sourceUri, n)
111+
val line = SourceLineKey(sourceKey, n)
112112
cachedSourceLines.update(
113113
line,
114114
cachedSourceLines.getOrElse(line, Seq.empty) :+ classFile
@@ -121,16 +121,16 @@ private class ClassEntryLookUp(
121121
}
122122

123123
def getSourceContent(sourceUri: URI): Option[String] =
124-
sourceUriToSourceFile.get(sourceUri).flatMap(readSourceContent(_, logger))
124+
sourceUriToSourceFile.get(SourceFileKey(sourceUri)).flatMap(readSourceContent(_, logger))
125125

126-
def getSourceFile(fqcn: String): Option[URI] =
126+
def getSourceFileURI(fqcn: String): Option[URI] =
127127
classNameToSourceFile.get(fqcn).map(_.uri)
128128

129129
def getSourceContentFromClassName(fqcn: String): Option[String] =
130-
getSourceFile(fqcn).flatMap(getSourceContent)
130+
getSourceFileURI(fqcn).flatMap(getSourceContent)
131131

132132
def getClassFiles(sourceUri: URI): Seq[ClassFile] =
133-
sourceUriToClassFiles.get(sourceUri).getOrElse(Seq.empty)
133+
sourceUriToClassFiles.get(SourceFileKey(sourceUri)).getOrElse(Seq.empty)
134134

135135
def getClassFile(fqcn: String): Option[ClassFile] =
136136
fqcnToClassFile.get(fqcn)
@@ -145,7 +145,7 @@ private class ClassEntryLookUp(
145145
def fromSource = {
146146
val scalaSigs =
147147
for {
148-
sourceFile <- getSourceFile(fqcn).toSeq
148+
sourceFile <- getSourceFileURI(fqcn).toSeq
149149
if sourceFile.toString.endsWith(".scala")
150150
classFile <- getClassFiles(sourceFile)
151151
if fqcn.startsWith(classFile.fullyQualifiedName + "$")
@@ -182,21 +182,21 @@ private object ClassEntryLookUp {
182182
classFiles.map(c => (c.fullyQualifiedName, c)).toMap
183183

184184
val sourceFileToRoot = sourceLookUps.flatMap(l => l.sourceFiles.map(f => (f -> l.root))).toMap
185-
val sourceUriToSourceFile = sourceLookUps.flatMap(_.sourceFiles).map(f => (f.uri, f)).toMap
185+
val sourceUriToSourceFile = sourceLookUps.flatMap(_.sourceFiles).map(f => (SourceFileKey(f.uri), f)).toMap
186186
val sourceNameToSourceFile = sourceLookUps.flatMap(_.sourceFiles).groupBy(f => f.fileName)
187187

188188
val classNameToSourceFile = mutable.Map[String, SourceFile]()
189-
val sourceUriToClassFiles = mutable.Map[URI, Seq[ClassFile]]()
189+
val sourceUriToClassFiles = mutable.Map[SourceFileKey, Seq[ClassFile]]()
190190
val orphanClassFiles = mutable.Buffer[ClassFile]()
191191
val missingSourceFileClassFiles = mutable.Buffer[ClassFile]()
192192

193193
for (classFile <- classFiles) {
194194
def recordSourceFile(sourceFile: SourceFile): Unit = {
195195
classNameToSourceFile.put(classFile.fullyQualifiedName, sourceFile)
196196
sourceUriToClassFiles.update(
197-
sourceFile.uri,
197+
SourceFileKey(sourceFile.uri),
198198
sourceUriToClassFiles.getOrElse(
199-
sourceFile.uri,
199+
SourceFileKey(sourceFile.uri),
200200
Seq.empty
201201
) :+ classFile
202202
)
@@ -358,3 +358,31 @@ private object ClassEntryLookUp {
358358
}
359359
}
360360
}
361+
362+
/**
363+
* On a case-insensitive system we need to sanitize all URIs to use them as Map keys.
364+
*/
365+
private case class SourceFileKey private (sanitizeUri: URI)
366+
367+
private object SourceFileKey {
368+
private val isCaseSensitiveFileSystem = Properties.isWin || Properties.isMac
369+
370+
def apply(uri: URI): SourceFileKey = {
371+
val sanitizeUri: URI =
372+
if (isCaseSensitiveFileSystem) {
373+
uri.getScheme match {
374+
case "file" => URI.create(uri.toString.toUpperCase)
375+
case "jar" | "zip" if uri.toString.contains("!/") =>
376+
// The contents of jars are case-sensitive no matter what the filesystem is.
377+
val parts = uri.toString.split("!/", 2).toSeq
378+
val head = parts.head.toUpperCase()
379+
val tail = parts.tail.mkString("!/")
380+
URI.create(s"$head!/$tail")
381+
case _ => uri
382+
}
383+
} else uri
384+
new SourceFileKey(sanitizeUri)
385+
}
386+
}
387+
388+
private case class SourceLineKey(sourceFile: SourceFileKey, lineNumber: Int)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import java.net.URI
1313
import ch.epfl.scala.debugadapter.Logger
1414
import ch.epfl.scala.debugadapter.internal.ScalaExtension.*
1515
import scala.util.control.NonFatal
16+
import scala.util.Properties
1617

1718
private case class SourceFile(
1819
entry: SourceEntry,

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,19 @@ import scala.collection.parallel.immutable.ParVector
1010

1111
private[debugadapter] final class SourceLookUpProvider(
1212
private[internal] val classPathEntries: Seq[ClassEntryLookUp],
13-
sourceUriToClassPathEntry: Map[URI, ClassEntryLookUp],
13+
sourceUriToClassPathEntry: Map[SourceFileKey, ClassEntryLookUp],
1414
fqcnToClassPathEntry: Map[String, ClassEntryLookUp]
1515
) extends ISourceLookUpProvider {
1616
override def supportsRealtimeBreakpointVerification(): Boolean = true
1717

1818
override def getSourceFileURI(fqcn: String, path: String): String = {
19-
getSourceFile(fqcn).map(_.toString).orNull
19+
getSourceFileByClassname(fqcn).map(_.toString).orNull
2020
}
2121

2222
override def getSourceContents(uri: String): String = {
2323
val sourceUri = URI.create(uri)
2424
sourceUriToClassPathEntry
25-
.get(sourceUri)
25+
.get(SourceFileKey(sourceUri))
2626
.flatMap(_.getSourceContent(sourceUri))
2727
.orNull
2828
}
@@ -33,15 +33,17 @@ private[debugadapter] final class SourceLookUpProvider(
3333
columns: Array[Int]
3434
): Array[String] = {
3535
val uri = URI.create(uriRepr)
36+
3637
uri.getScheme match {
3738
case "dap-fqcn" =>
3839
val resolvedName = uri.getSchemeSpecificPart
3940
lines.map(_ => resolvedName)
4041
case _ =>
41-
sourceUriToClassPathEntry.get(uri) match {
42+
val key = SourceFileKey(uri);
43+
sourceUriToClassPathEntry.get(key) match {
4244
case None => lines.map(_ => null)
4345
case Some(entry) =>
44-
lines.map(line => entry.getFullyQualifiedClassName(uri, line).orNull)
46+
lines.map(line => entry.getFullyQualifiedClassName(key, line).orNull)
4547
}
4648
}
4749
}
@@ -70,10 +72,10 @@ private[debugadapter] final class SourceLookUpProvider(
7072
} yield scalaSig
7173
}
7274

73-
private def getSourceFile(className: String): Option[URI] = {
75+
private def getSourceFileByClassname(className: String): Option[URI] = {
7476
fqcnToClassPathEntry
7577
.get(className)
76-
.flatMap(_.getSourceFile(className))
78+
.flatMap(_.getSourceFileURI(className))
7779
}
7880
}
7981

modules/core/src/main/scala/ch/epfl/scala/debugadapter/internal/stepfilter/RuntimeStepFilter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ private[internal] object RuntimeStepFilter {
2525
"java.lang.invoke.DirectMethodHandle.constructorMethod(java.lang.Object)"
2626
)
2727
private val scala3ClassesToSkip = Set("scala.runtime.LazyVals$")
28-
private val scala2ClassesToSkip = Set.empty[String]
28+
private val scala2ClassesToSkip = Set("scala.runtime.BoxesRunTime")
2929

3030
def apply(scalaVersion: ScalaVersion): StepFilter = {
3131
if (scalaVersion.isScala2)

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,11 @@ private object DebuggeeProcess {
5656
val builder = Process(command, forkOptions.workingDirectory, envVars.toSeq: _*)
5757
val processLogger = new DebuggeeProcessLogger(listener)
5858

59-
logger.info("Starting debuggee process")
60-
logger.debug(command.mkString(" "))
61-
logger.debug(s"working directory: ${forkOptions.workingDirectory.getOrElse("null")}")
62-
logger.debug(s"env vars:\n${envVars.map { case (key, value) => s" $key=$value " }.mkString("\n")}")
59+
logger.info("Starting debuggee process:")
60+
logger.info("- working directory:" + forkOptions.workingDirectory.getOrElse("null"))
61+
logger.info("- command: " + command.mkString(" "))
62+
val formattedEnvVars = envVars.map { case (key, value) => s" $key=$value" }.mkString("\n")
63+
logger.info("- environment variables:\n" + formattedEnvVars)
6364

6465
val process = new DebuggeeProcess(builder.run(processLogger))
6566
process.future.onComplete {

modules/scala-3-step-filter/src/main/scala/ch/epfl/scala/debugadapter/internal/stepfilter/ScalaStepFilterBridge.scala

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ package ch.epfl.scala.debugadapter.internal.stepfilter
33
import ch.epfl.scala.debugadapter.internal.jdi
44
import tastyquery.Contexts
55
import tastyquery.Contexts.Context
6-
import tastyquery.Flags
76
import tastyquery.Names.*
87
import tastyquery.Signatures.*
98
import tastyquery.Symbols.*
109
import tastyquery.Types.*
1110
import tastyquery.jdk.ClasspathLoaders
1211
import tastyquery.jdk.ClasspathLoaders.FileKind
12+
import tastyquery.Modifiers.TermSymbolKind
1313

1414
import java.nio.file.Path
1515
import java.util.function.Consumer
@@ -83,14 +83,14 @@ class ScalaStepFilterBridge(
8383
else ctx.defn.EmptyPackage
8484
val className = javaParts.last
8585
val clsSymbols = findSymbolsRecursively(packageSym, className)
86-
val obj = clsSymbols.filter(_.is(Flags.Module))
87-
val cls = clsSymbols.filter(!_.is(Flags.Module))
86+
val obj = clsSymbols.filter(_.isModuleClass)
87+
val cls = clsSymbols.filter(!_.isModuleClass)
8888
assert(obj.size <= 1 && cls.size <= 1)
8989
if isObject && !isExtensionMethod then obj.headOption else cls.headOption
9090

91-
private def findSymbolsRecursively(owner: DeclaringSymbol, encodedName: String): Seq[DeclaringSymbol] =
91+
private def findSymbolsRecursively(owner: DeclaringSymbol, encodedName: String): Seq[ClassSymbol] =
9292
owner.declarations
93-
.collect { case sym: DeclaringSymbol => sym }
93+
.collect { case sym: ClassSymbol => sym }
9494
.flatMap { sym =>
9595
val encodedSymName = NameTransformer.encode(sym.name.toString)
9696
val Symbol = s"${Regex.quote(encodedSymName)}\\$$?(.*)".r
@@ -180,6 +180,5 @@ class ScalaStepFilterBridge(
180180

181181
private def skip(symbol: TermSymbol): Boolean =
182182
val isNonLazyGetterOrSetter =
183-
(!symbol.flags.is(Flags.Method) || symbol.is(Flags.Accessor)) &&
184-
!symbol.is(Flags.Lazy)
185-
isNonLazyGetterOrSetter || symbol.is(Flags.Synthetic)
183+
(!symbol.isMethod || symbol.isSetter) && symbol.kind != TermSymbolKind.LazyVal
184+
isNonLazyGetterOrSetter || symbol.isSynthetic || symbol.isExport

modules/tests/src/test/scala/ch/epfl/scala/debugadapter/ScalaEvaluationTests.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1861,7 +1861,7 @@ abstract class ScalaEvaluationTests(scalaVersion: ScalaVersion) extends DebugTes
18611861
check(Breakpoint(5), Evaluation.success("Foo.msg", "x"))
18621862
}
18631863

1864-
test("evaluate by-name param") {
1864+
test("evaluate by-name param".ignore) {
18651865
val source =
18661866
"""|package example
18671867
|
@@ -2572,7 +2572,7 @@ abstract class Scala3EvaluationTests(scalaVersion: ScalaVersion) extends ScalaEv
25722572
)
25732573
}
25742574

2575-
test("instance of local class in method of value class") {
2575+
test("instance of local class in method of value class".ignore) {
25762576
// only Scala 3 because:
25772577
// "implementation restriction: nested class is not allowed in value class
25782578
// This restriction is planned to be removed in subsequent releases."

0 commit comments

Comments
 (0)