Skip to content

Commit

Permalink
enable hcr + single source test
Browse files Browse the repository at this point in the history
  • Loading branch information
iusildra committed Aug 28, 2023
1 parent 87f1615 commit 9da6664
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 61 deletions.
2 changes: 1 addition & 1 deletion frontend/src/main/scala/bloop/bsp/BloopBspServices.scala
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ final class BloopBspServices(
BloopDebuggeeRunner.forTestSuite(projects, testClasses, state, ioScheduler)
})
case bsp.DebugSessionParamsDataKind.ScalaAttachRemote =>
Right(BloopDebuggeeRunner.forAttachRemote(state, ioScheduler, projects))
Right(BloopDebuggeeRunner.forAttachRemote(projects, state, ioScheduler))
case dataKind => Left(Response.invalidRequest(s"Unsupported data kind: $dataKind"))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ class BloopDebugToolsResolver(logger: Logger) extends DebugToolsResolver {
}
}

override def resolveStepFilter(scalaVersion: ScalaVersion): Try[ClassLoader] = {
override def resolveUnpickler(scalaVersion: ScalaVersion): Try[ClassLoader] = {
getOrTryUpdate(stepFilterCache, scalaVersion) {
val stepFilterModule = s"${BuildInfo.scala3StepFilterName}_${scalaVersion.binaryVersion}"
val stepFilter = Artifact(BuildInfo.organization, stepFilterModule, BuildInfo.version)
val unpicklerModule = s"${BuildInfo.unpicklerName}_${scalaVersion.binaryVersion}"
val stepFilter = Artifact(BuildInfo.organization, unpicklerModule, BuildInfo.version)
val tastyCore = Artifact("org.scala-lang", "tasty-core_3", scalaVersion.value)
DependencyResolution
.resolveWithErrors(List(stepFilter, tastyCore), logger)
Expand Down
92 changes: 52 additions & 40 deletions frontend/src/main/scala/bloop/dap/BloopDebuggee.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,22 @@ import bloop.testing.DebugLoggingEventHandler
import bloop.testing.TestInternals

import monix.execution.Scheduler
import io.reactivex.Observable

abstract class BloopDebuggee(
initialState: State,
ioScheduler: Scheduler,
debugeeScalaVersion: Option[String]
ioScheduler: Scheduler
) extends Debuggee {

val debuggeeScalaVersion: Option[String]
// The version doesn't matter for project without Scala version (Java only)
val scalaVersion = ScalaVersion(debugeeScalaVersion.getOrElse("2.13.8"))
lazy val scalaVersion = ScalaVersion(debuggeeScalaVersion.getOrElse("2.13.8"))

override def run(listener: DebuggeeListener): CancelableFuture[Unit] = {
val debugSessionLogger = new DebuggeeLogger(listener, initialState.logger)

val task = start(initialState.copy(logger = debugSessionLogger), listener)
.map { status =>
if (!status.isOk) throw new Exception(s"debugee failed with ${status.name}")
if (!status.isOk) throw new Exception(s"debuggee failed with ${status.name}")
}
DapCancellableFuture.runAsync(task, ioScheduler)
}
Expand All @@ -51,9 +51,13 @@ private final class MainClassDebugAdapter(
val unmanagedEntries: Seq[UnmanagedEntry],
env: JdkConfig,
initialState: State,
ioScheduler: Scheduler,
scalaVersion: Option[String]
) extends BloopDebuggee(initialState, ioScheduler, scalaVersion) {
ioScheduler: Scheduler
) extends BloopDebuggee(initialState, ioScheduler) {

override val debuggeeScalaVersion = project.scalaInstance.map(_.version)

val classesToUpdate: Observable[Seq[String]] = project.classObserver

val javaRuntime: Option[JavaRuntime] = JavaRuntime(env.javaHome.underlying)
def name: String = s"${getClass.getSimpleName}(${project.name}, ${mainClass.`class`})"
def start(state: State, listener: DebuggeeListener): Task[ExitStatus] = {
Expand Down Expand Up @@ -84,9 +88,14 @@ private final class TestSuiteDebugAdapter(
val unmanagedEntries: Seq[UnmanagedEntry],
val javaRuntime: Option[JavaRuntime],
initialState: State,
ioScheduler: Scheduler,
val debugeeScalaVersion: Option[String]
) extends BloopDebuggee(initialState, ioScheduler, debugeeScalaVersion) {
ioScheduler: Scheduler
) extends BloopDebuggee(initialState, ioScheduler) {

override val debuggeeScalaVersion = projects.headOption.flatMap(_.scalaInstance.map(_.version))

val classesToUpdate: Observable[Seq[String]] =
projects.map(_.classObserver).fold(Observable.empty[Seq[String]])(_ mergeWith _)

override def name: String = {
val projectsStr = projects.map(_.bspUri).mkString("[", ", ", "]")
val selectedTests = testClasses.suites
Expand Down Expand Up @@ -116,21 +125,37 @@ private final class TestSuiteDebugAdapter(
}

private final class AttachRemoteDebugAdapter(
projects: Seq[Project],
val modules: Seq[Module],
val libraries: Seq[Library],
val unmanagedEntries: Seq[UnmanagedEntry],
val javaRuntime: Option[JavaRuntime],
initialState: State,
ioScheduler: Scheduler,
val debugeeScalaVersion: Option[String]
) extends BloopDebuggee(initialState, ioScheduler, debugeeScalaVersion) {
ioScheduler: Scheduler
) extends BloopDebuggee(initialState, ioScheduler) {

override val debuggeeScalaVersion = projects.headOption.flatMap(_.scalaInstance.map(_.version))

override def name: String = s"${getClass.getSimpleName}(${initialState.build.origin})"
override def start(state: State, listener: DebuggeeListener): Task[ExitStatus] = Task(
ExitStatus.Ok
)
val classesToUpdate: Observable[Seq[String]] =
projects.map(_.classObserver).fold(Observable.empty[Seq[String]])(_ mergeWith _)
}

object BloopDebuggeeRunner {
def getEntries(
project: Project,
state: State
): (Seq[Module], Seq[Library], Seq[UnmanagedEntry]) = {
val dag = state.build.getDagFor(project)
val modules = getModules(dag, state.client)
val libraries = getLibraries(dag)
val unmanagedEntries =
getUnmanagedEntries(project, dag, state.client, modules ++ libraries)
(modules, libraries, unmanagedEntries)
}

def forMainClass(
projects: Seq[Project],
Expand All @@ -143,11 +168,7 @@ object BloopDebuggeeRunner {
case Seq(project) =>
project.platform match {
case jvm: Platform.Jvm =>
val dag = state.build.getDagFor(project)
val modules = getModules(dag, state.client)
val libraries = getLibraries(dag)
val unmanagedEntries =
getUnmanagedEntries(project, dag, state.client, modules ++ libraries)
val (modules, libraries, unmanagedEntries) = getEntries(project, state)
Right(
new MainClassDebugAdapter(
project,
Expand All @@ -157,8 +178,7 @@ object BloopDebuggeeRunner {
unmanagedEntries,
jvm.config,
state,
ioScheduler,
project.scalaInstance.map(_.version)
ioScheduler
)
)
case platform =>
Expand All @@ -178,10 +198,7 @@ object BloopDebuggeeRunner {
case Seq() =>
Left(s"No projects specified for the test suites: [${testClasses.classNames.sorted}]")
case Seq(project) if project.platform.isInstanceOf[Platform.Jvm] =>
val dag = state.build.getDagFor(project)
val modules = getModules(dag, state.client)
val libraries = getLibraries(dag)
val unmanagedEntries = getUnmanagedEntries(project, dag, state.client, modules ++ libraries)
val (modules, libraries, unmanagedEntries) = getEntries(project, state)
val Platform.Jvm(config, _, _, _, _, _) = project.platform
val javaRuntime = JavaRuntime(config.javaHome.underlying)
Right(
Expand All @@ -193,12 +210,11 @@ object BloopDebuggeeRunner {
unmanagedEntries,
javaRuntime,
state,
ioScheduler,
project.scalaInstance.map(_.version)
ioScheduler
)
)

case project :: _ =>
case _ =>
Right(
new TestSuiteDebugAdapter(
projects,
Expand All @@ -208,45 +224,41 @@ object BloopDebuggeeRunner {
Seq.empty,
None,
state,
ioScheduler,
project.scalaInstance.map(_.version)
ioScheduler
)
)

}
}

def forAttachRemote(
projects: Seq[Project],
state: State,
ioScheduler: Scheduler,
projects: Seq[Project]
ioScheduler: Scheduler
): Debuggee = {
projects match {
case Seq(project) if project.platform.isInstanceOf[Platform.Jvm] =>
val dag = state.build.getDagFor(project)
val libraries = getLibraries(dag)
val modules = getModules(dag, state.client)
val unmanagedEntries = getUnmanagedEntries(project, dag, state.client, modules ++ libraries)
val (modules, libraries, unmanagedEntries) = getEntries(project, state)
val Platform.Jvm(config, _, _, _, _, _) = project.platform
val javaRuntime = JavaRuntime(config.javaHome.underlying)
new AttachRemoteDebugAdapter(
Seq(project),
modules,
libraries,
unmanagedEntries,
javaRuntime,
state,
ioScheduler,
project.scalaInstance.map(_.version)
ioScheduler
)
case projects =>
new AttachRemoteDebugAdapter(
projects,
Seq.empty,
Seq.empty,
Seq.empty,
None,
state,
ioScheduler,
projects.headOption.flatMap(_.scalaInstance).map(_.version)
ioScheduler
)
}
}
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/main/scala/bloop/data/Project.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import com.typesafe.config.ConfigSyntax
import scalaz.Cord
import xsbti.compile.ClasspathOptions
import xsbti.compile.CompileOrder
import io.reactivex.subjects.PublishSubject

final case class Project(
name: String,
Expand Down Expand Up @@ -59,6 +60,8 @@ final case class Project(
origin: Origin
) {

val classObserver = PublishSubject.create[Seq[String]]()

/** The bsp uri associated with this project. */
val bspUri: Bsp.Uri = Bsp.Uri(ProjectUris.toURI(baseDirectory, name))

Expand Down
62 changes: 57 additions & 5 deletions frontend/src/main/scala/bloop/engine/tasks/CompileTask.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ import bloop.tracing.BraveTracer
import monix.execution.CancelableFuture
import monix.reactive.MulticastStrategy
import monix.reactive.Observable
import xsbti.compile.analysis.ReadStamps
import xsbti.compile.analysis.Stamp
import xsbti.VirtualFileRef
import scala.jdk.CollectionConverters._

object CompileTask {
private implicit val logContext: DebugFilter = DebugFilter.Compilation
Expand Down Expand Up @@ -226,6 +230,52 @@ object CompileTask {
}
}

def registerUpdatedClasses(res: FinalNormalCompileResult, project: Project) = {
res.result.fromCompiler match {
case s: Success =>
val currentAnalysis =
Option(s.products.resultForFutureCompilationRuns.analysis().orElse(null))
val previous = res.result.previous
val previousAnalysis =
if (previous.isEmpty) None
else Option(previous.get.previous.analysis().orElse(null))

(currentAnalysis, previousAnalysis) match {
case (None, _) | (_, None) => ()
case (Some(curr), Some(prev)) =>
val currentStamps = curr.readStamps
val previousStamps = prev.readStamps
val newClasses = getNewClasses(currentStamps, previousStamps)
project.classObserver.onNext(newClasses)
}
case _ => ()
}
}

def getNewClasses(currentStamps: ReadStamps, previousStamps: ReadStamps): Seq[String] = {
def isNewer(current: Stamp, previous: Stamp) = {
if (previous == null) true
else {
val newHash = current.getHash
val oldHash = previous.getHash
newHash.isPresent && (!oldHash.isPresent || newHash.get != oldHash.get)
}
}

object ClassFile {
def unapply(vf: VirtualFileRef): Option[String] = {
val fqcn = vf.name
if (fqcn.toString.endsWith(".class")) Some(fqcn.stripSuffix(".class"))
else None
}
}

val oldStamps = previousStamps.getAllProductStamps
currentStamps.getAllProductStamps.asScala.collect {
case (file @ ClassFile(fqcn), stamp) if isNewer(stamp, oldStamps.get(file)) => fqcn
}.toSeq
}

def setup(inputs: CompileDefinitions.BundleInputs): Task[CompileBundle] = {
// Create a multicast observable stream to allow multiple mirrors of loggers
val (observer, obs) = {
Expand Down Expand Up @@ -295,6 +345,11 @@ object CompileTask {
val newState: State = {
val stateWithResults = state.copy(results = state.results.addFinalResults(results))
if (failures.isEmpty) {
results.foreach {
case res: FinalNormalCompileResult =>
registerUpdatedClasses(res, res.project)
case _ => ()
}
stateWithResults.copy(status = ExitStatus.Ok)
} else {
results.foreach {
Expand Down Expand Up @@ -327,12 +382,9 @@ object CompileTask {
runIOTasksInParallel(cleanUpTasksToRunInBackground)

val runningTasksRequiredForCorrectness = Task.sequence {
results.flatMap {
results.collect {
case FinalNormalCompileResult(_, result) =>
val tasksAtEndOfBuildCompilation =
Task.fromFuture(result.runningBackgroundTasks)
List(tasksAtEndOfBuildCompilation)
case _ => Nil
Task.fromFuture(result.runningBackgroundTasks)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import com.microsoft.java.debug.core.protocol.Events
import com.microsoft.java.debug.core.protocol.Requests._
import com.microsoft.java.debug.core.protocol.Responses.ContinueResponseBody
import com.microsoft.java.debug.core.protocol.Responses.EvaluateResponseBody
import com.microsoft.java.debug.core.protocol.Responses.RedefineClassesResponse
import com.microsoft.java.debug.core.protocol.Responses.ScopesResponseBody
import com.microsoft.java.debug.core.protocol.Responses.SetBreakpointsResponseBody
import com.microsoft.java.debug.core.protocol.Responses.StackTraceResponseBody
Expand Down Expand Up @@ -119,6 +120,11 @@ private[dap] final class DebugAdapterConnection(
adapter.request(Attach, arguments)
}

def redefineClasses(): Task[RedefineClassesResponse] = {
val args = new RedefineClassesArguments()
adapter.request(RedefineClasses, args)
}

def close(): Unit = {
try socket.close()
finally {
Expand Down
Loading

0 comments on commit 9da6664

Please sign in to comment.