Skip to content

Commit

Permalink
Merge pull request #617 from adpi2/3.1.x-backport1
Browse files Browse the repository at this point in the history
[3.1.x] Backport #594 #615 #616
  • Loading branch information
adpi2 authored Nov 20, 2023
2 parents 77c9e75 + 365dfd9 commit d52fa45
Show file tree
Hide file tree
Showing 12 changed files with 229 additions and 64 deletions.
4 changes: 2 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ lazy val expressionCompiler = projectMatrix
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")
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")
case (3, 0) => Seq("3.0.2", "3.0.1", "3.0.0")
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")
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")
}
.toSeq
.flatten,
Expand Down Expand Up @@ -202,7 +202,7 @@ lazy val scala3StepFilter: Project = project
scalaVersion := Dependencies.scala31Plus,
Compile / doc / sources := Seq.empty,
libraryDependencies ++= Seq(
"ch.epfl.scala" %% "tasty-query" % "0.8.4",
"ch.epfl.scala" %% "tasty-query" % "0.9.3",
"org.scala-lang" %% "tasty-core" % scalaVersion.value,
Dependencies.munit % Test
),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
package ch.epfl.scala.debugadapter.internal

import ch.epfl.scala.debugadapter._
import ch.epfl.scala.debugadapter.internal.ScalaExtension.*
import ch.epfl.scala.debugadapter.internal.scalasig.Decompiler
import ch.epfl.scala.debugadapter.internal.scalasig.ScalaSig
import org.objectweb.asm._

import java.net.URI
import java.nio.file._
import scala.jdk.CollectionConverters.*
import scala.collection.mutable
import ClassEntryLookUp.readSourceContent

import scala.util.matching.Regex
import ch.epfl.scala.debugadapter.internal.scalasig.ScalaSig
import ch.epfl.scala.debugadapter.internal.scalasig.Decompiler
import ch.epfl.scala.debugadapter.internal.ScalaExtension.*
import scala.jdk.CollectionConverters.*
import scala.util.Properties
import scala.util.Try
import scala.util.matching.Regex

private case class SourceLine(uri: URI, lineNumber: Int)
import ClassEntryLookUp.readSourceContent

private[internal] case class ClassFile(
fullyQualifiedName: String,
Expand All @@ -35,35 +34,36 @@ private[internal] case class ClassFile(
private class ClassEntryLookUp(
val entry: ClassEntry,
fqcnToClassFile: Map[String, ClassFile],
sourceUriToSourceFile: Map[URI, SourceFile],
sourceUriToClassFiles: Map[URI, Seq[ClassFile]],
sourceUriToSourceFile: Map[SourceFileKey, SourceFile],
sourceUriToClassFiles: Map[SourceFileKey, Seq[ClassFile]],
classNameToSourceFile: Map[String, SourceFile],
missingSourceFileClassFiles: Seq[ClassFile],
val orphanClassFiles: Seq[ClassFile],
logger: Logger
) {
private val cachedSourceLines = mutable.Map[SourceLine, Seq[ClassFile]]()
private val cachedSourceLines = mutable.Map[SourceLineKey, Seq[ClassFile]]()

def sources: Iterable[URI] = sourceUriToSourceFile.keys
def sources: Iterable[SourceFileKey] = sourceUriToSourceFile.keys
def fullyQualifiedNames: Iterable[String] = {
classNameToSourceFile.keys ++
orphanClassFiles.map(_.fullyQualifiedName) ++
missingSourceFileClassFiles.map(_.fullyQualifiedName)
}

def getFullyQualifiedClassName(
sourceUri: URI,
sourceKey: SourceFileKey,
lineNumber: Int
): Option[String] = {
val line = SourceLine(sourceUri, lineNumber)
val line = SourceLineKey(sourceKey, lineNumber)

if (!cachedSourceLines.contains(line)) {
// read and cache line numbers from class files
sourceUriToClassFiles(sourceUri)
sourceUriToClassFiles
.getOrElse(sourceKey, Nil)
.groupBy(_.classSystem)
.foreach { case (classSystem, classFiles) =>
classSystem
.within((_, root) => loadLineNumbers(root, classFiles, sourceUri))
.within((_, root) => loadLineNumbers(root, classFiles, sourceKey))
.warnFailure(logger, s"Cannot load line numbers in ${classSystem.name}")
}
}
Expand All @@ -80,7 +80,7 @@ private class ClassEntryLookUp(
private def loadLineNumbers(
root: Path,
classFiles: Seq[ClassFile],
sourceUri: URI
sourceKey: SourceFileKey
): Unit = {
for (classFile <- classFiles) {
val path = root.resolve(classFile.relativePath)
Expand Down Expand Up @@ -108,7 +108,7 @@ private class ClassEntryLookUp(
reader.accept(visitor, 0)

for (n <- lineNumbers) {
val line = SourceLine(sourceUri, n)
val line = SourceLineKey(sourceKey, n)
cachedSourceLines.update(
line,
cachedSourceLines.getOrElse(line, Seq.empty) :+ classFile
Expand All @@ -121,16 +121,16 @@ private class ClassEntryLookUp(
}

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

def getSourceFile(fqcn: String): Option[URI] =
def getSourceFileURI(fqcn: String): Option[URI] =
classNameToSourceFile.get(fqcn).map(_.uri)

def getSourceContentFromClassName(fqcn: String): Option[String] =
getSourceFile(fqcn).flatMap(getSourceContent)
getSourceFileURI(fqcn).flatMap(getSourceContent)

def getClassFiles(sourceUri: URI): Seq[ClassFile] =
sourceUriToClassFiles.get(sourceUri).getOrElse(Seq.empty)
sourceUriToClassFiles.get(SourceFileKey(sourceUri)).getOrElse(Seq.empty)

def getClassFile(fqcn: String): Option[ClassFile] =
fqcnToClassFile.get(fqcn)
Expand All @@ -145,7 +145,7 @@ private class ClassEntryLookUp(
def fromSource = {
val scalaSigs =
for {
sourceFile <- getSourceFile(fqcn).toSeq
sourceFile <- getSourceFileURI(fqcn).toSeq
if sourceFile.toString.endsWith(".scala")
classFile <- getClassFiles(sourceFile)
if fqcn.startsWith(classFile.fullyQualifiedName + "$")
Expand Down Expand Up @@ -182,21 +182,21 @@ private object ClassEntryLookUp {
classFiles.map(c => (c.fullyQualifiedName, c)).toMap

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

val classNameToSourceFile = mutable.Map[String, SourceFile]()
val sourceUriToClassFiles = mutable.Map[URI, Seq[ClassFile]]()
val sourceUriToClassFiles = mutable.Map[SourceFileKey, Seq[ClassFile]]()
val orphanClassFiles = mutable.Buffer[ClassFile]()
val missingSourceFileClassFiles = mutable.Buffer[ClassFile]()

for (classFile <- classFiles) {
def recordSourceFile(sourceFile: SourceFile): Unit = {
classNameToSourceFile.put(classFile.fullyQualifiedName, sourceFile)
sourceUriToClassFiles.update(
sourceFile.uri,
SourceFileKey(sourceFile.uri),
sourceUriToClassFiles.getOrElse(
sourceFile.uri,
SourceFileKey(sourceFile.uri),
Seq.empty
) :+ classFile
)
Expand Down Expand Up @@ -358,3 +358,31 @@ private object ClassEntryLookUp {
}
}
}

/**
* On a case-insensitive system we need to sanitize all URIs to use them as Map keys.
*/
private case class SourceFileKey private (sanitizeUri: URI)

private object SourceFileKey {
private val isCaseSensitiveFileSystem = Properties.isWin || Properties.isMac

def apply(uri: URI): SourceFileKey = {
val sanitizeUri: URI =
if (isCaseSensitiveFileSystem) {
uri.getScheme match {
case "file" => URI.create(uri.toString.toUpperCase)
case "jar" | "zip" if uri.toString.contains("!/") =>
// The contents of jars are case-sensitive no matter what the filesystem is.
val parts = uri.toString.split("!/", 2).toSeq
val head = parts.head.toUpperCase()
val tail = parts.tail.mkString("!/")
URI.create(s"$head!/$tail")
case _ => uri
}
} else uri
new SourceFileKey(sanitizeUri)
}
}

private case class SourceLineKey(sourceFile: SourceFileKey, lineNumber: Int)
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import java.net.URI
import ch.epfl.scala.debugadapter.Logger
import ch.epfl.scala.debugadapter.internal.ScalaExtension.*
import scala.util.control.NonFatal
import scala.util.Properties

private case class SourceFile(
entry: SourceEntry,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ import scala.collection.parallel.immutable.ParVector

private[debugadapter] final class SourceLookUpProvider(
private[internal] val classPathEntries: Seq[ClassEntryLookUp],
sourceUriToClassPathEntry: Map[URI, ClassEntryLookUp],
sourceUriToClassPathEntry: Map[SourceFileKey, ClassEntryLookUp],
fqcnToClassPathEntry: Map[String, ClassEntryLookUp]
) extends ISourceLookUpProvider {
override def supportsRealtimeBreakpointVerification(): Boolean = true

override def getSourceFileURI(fqcn: String, path: String): String = {
getSourceFile(fqcn).map(_.toString).orNull
getSourceFileByClassname(fqcn).map(_.toString).orNull
}

override def getSourceContents(uri: String): String = {
val sourceUri = URI.create(uri)
sourceUriToClassPathEntry
.get(sourceUri)
.get(SourceFileKey(sourceUri))
.flatMap(_.getSourceContent(sourceUri))
.orNull
}
Expand All @@ -33,15 +33,17 @@ private[debugadapter] final class SourceLookUpProvider(
columns: Array[Int]
): Array[String] = {
val uri = URI.create(uriRepr)

uri.getScheme match {
case "dap-fqcn" =>
val resolvedName = uri.getSchemeSpecificPart
lines.map(_ => resolvedName)
case _ =>
sourceUriToClassPathEntry.get(uri) match {
val key = SourceFileKey(uri);
sourceUriToClassPathEntry.get(key) match {
case None => lines.map(_ => null)
case Some(entry) =>
lines.map(line => entry.getFullyQualifiedClassName(uri, line).orNull)
lines.map(line => entry.getFullyQualifiedClassName(key, line).orNull)
}
}
}
Expand Down Expand Up @@ -70,10 +72,10 @@ private[debugadapter] final class SourceLookUpProvider(
} yield scalaSig
}

private def getSourceFile(className: String): Option[URI] = {
private def getSourceFileByClassname(className: String): Option[URI] = {
fqcnToClassPathEntry
.get(className)
.flatMap(_.getSourceFile(className))
.flatMap(_.getSourceFileURI(className))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ private[internal] object RuntimeStepFilter {
"java.lang.invoke.DirectMethodHandle.constructorMethod(java.lang.Object)"
)
private val scala3ClassesToSkip = Set("scala.runtime.LazyVals$")
private val scala2ClassesToSkip = Set.empty[String]
private val scala2ClassesToSkip = Set("scala.runtime.BoxesRunTime")

def apply(scalaVersion: ScalaVersion): StepFilter = {
if (scalaVersion.isScala2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,11 @@ private object DebuggeeProcess {
val builder = Process(command, forkOptions.workingDirectory, envVars.toSeq: _*)
val processLogger = new DebuggeeProcessLogger(listener)

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

val process = new DebuggeeProcess(builder.run(processLogger))
process.future.onComplete {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ package ch.epfl.scala.debugadapter.internal.stepfilter
import ch.epfl.scala.debugadapter.internal.jdi
import tastyquery.Contexts
import tastyquery.Contexts.Context
import tastyquery.Flags
import tastyquery.Names.*
import tastyquery.Signatures.*
import tastyquery.Symbols.*
import tastyquery.Types.*
import tastyquery.jdk.ClasspathLoaders
import tastyquery.jdk.ClasspathLoaders.FileKind
import tastyquery.Modifiers.TermSymbolKind

import java.nio.file.Path
import java.util.function.Consumer
Expand Down Expand Up @@ -83,14 +83,14 @@ class ScalaStepFilterBridge(
else ctx.defn.EmptyPackage
val className = javaParts.last
val clsSymbols = findSymbolsRecursively(packageSym, className)
val obj = clsSymbols.filter(_.is(Flags.Module))
val cls = clsSymbols.filter(!_.is(Flags.Module))
val obj = clsSymbols.filter(_.isModuleClass)
val cls = clsSymbols.filter(!_.isModuleClass)
assert(obj.size <= 1 && cls.size <= 1)
if isObject && !isExtensionMethod then obj.headOption else cls.headOption

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

private def skip(symbol: TermSymbol): Boolean =
val isNonLazyGetterOrSetter =
(!symbol.flags.is(Flags.Method) || symbol.is(Flags.Accessor)) &&
!symbol.is(Flags.Lazy)
isNonLazyGetterOrSetter || symbol.is(Flags.Synthetic)
(!symbol.isMethod || symbol.isSetter) && symbol.kind != TermSymbolKind.LazyVal
isNonLazyGetterOrSetter || symbol.isSynthetic || symbol.isExport
Original file line number Diff line number Diff line change
Expand Up @@ -1861,7 +1861,7 @@ abstract class ScalaEvaluationTests(scalaVersion: ScalaVersion) extends DebugTes
check(Breakpoint(5), Evaluation.success("Foo.msg", "x"))
}

test("evaluate by-name param") {
test("evaluate by-name param".ignore) {
val source =
"""|package example
|
Expand Down Expand Up @@ -2572,7 +2572,7 @@ abstract class Scala3EvaluationTests(scalaVersion: ScalaVersion) extends ScalaEv
)
}

test("instance of local class in method of value class") {
test("instance of local class in method of value class".ignore) {
// only Scala 3 because:
// "implementation restriction: nested class is not allowed in value class
// This restriction is planned to be removed in subsequent releases."
Expand Down
Loading

0 comments on commit d52fa45

Please sign in to comment.