Skip to content

Commit

Permalink
Correctly map sources for nested inner classes
Browse files Browse the repository at this point in the history
  • Loading branch information
ephemerist committed Jun 25, 2021
1 parent a7fed64 commit 40f515d
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,6 @@ private[sbt] object JavaAnalyze {
} {
binaryClassNameToLoadedClass.update(binaryClassName, loadedClass)

def loadEnclosingClass(clazz: Class[_]): Option[String] = {
binaryToSourceName(clazz) match {
case None if clazz.getEnclosingClass != null =>
loadEnclosingClass(clazz.getEnclosingClass)
case other => other
}
}

val srcClassName = loadEnclosingClass(loadedClass)

val finalClassFile: Path = remapClassFile(newClass)
Expand All @@ -116,8 +108,7 @@ private[sbt] object JavaAnalyze {
val localClassesToSources = {
val localToSourcesSeq = for {
cls <- localClassesOrStale
enclosingCls <- Option(cls.getEnclosingClass)
sourceOfEnclosing <- binaryToSourceName(enclosingCls)
sourceOfEnclosing <- loadEnclosingClass(cls)
} yield (cls.getName, sourceOfEnclosing)
localToSourcesSeq.toMap
}
Expand Down Expand Up @@ -272,6 +263,14 @@ private[sbt] object JavaAnalyze {
private def binaryToSourceName(loadedClass: Class[_]): Option[String] =
Option(loadedClass.getCanonicalName)

private def loadEnclosingClass(clazz: Class[_]): Option[String] = {
binaryToSourceName(clazz) match {
case None if clazz.getEnclosingClass != null =>
loadEnclosingClass(clazz.getEnclosingClass)
case other => other
}
}

/*
* given mapping between getName and sources, try to guess
* where the *.class file is coming from.
Expand Down
17 changes: 16 additions & 1 deletion zinc/src/test/scala/sbt/inc/BaseCompilerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import java.nio.file.{ Files, Path }

import sbt.internal.inc.{ Analysis, BridgeProviderSpecification }
import sbt.util.Logger
import xsbti.compile.IncOptions
import xsbti.compile.{ CompileResult, IncOptions }

class BaseCompilerSpec extends BridgeProviderSpecification {
val scalaVersion: String = scala.util.Properties.versionNumberString
Expand All @@ -32,6 +32,21 @@ class BaseCompilerSpec extends BridgeProviderSpecification {
}.last
}

def classes(a: Analysis) = {
a.compilations.allCompilations.flatMap { c =>
a.apis.internal.collect {
case (className, api) if api.compilationTimestamp == c.getStartTime =>
className -> c.getStartTime
}
}
}.toMap

def recompiled(res1: CompileResult, res2: CompileResult): Set[String] = {
val classes1 = classes(res1.analysis.asInstanceOf[Analysis])
val classes2 = classes(res2.analysis.asInstanceOf[Analysis])
classes2.collect { case (clazz, time) if !classes1.get(clazz).contains(time) => clazz }.toSet
}

implicit class ProjectSetupOps(setup: ProjectSetup) {
def createCompiler(): CompilerSetup = setup.createCompiler(scalaVersion, incOptions)

Expand Down
48 changes: 46 additions & 2 deletions zinc/src/test/scala/sbt/inc/IncrementalCompilerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ package sbt.inc
import sbt.internal.inc._
import sbt.io.IO.{ withTemporaryDirectory => withTmpDir }
import sbt.io.syntax._
import xsbti.compile.{ AnalysisStore, CompileAnalysis, DefaultExternalHooks }

import xsbti.compile.{ AnalysisStore, CompileAnalysis, DefaultExternalHooks, Inputs, Output }
import java.util.Optional

import xsbti.VirtualFile

class IncrementalCompilerSpec extends BaseCompilerSpec {
//override val logLevel = sbt.util.Level.Debug
behavior.of("incremental compiler")
Expand Down Expand Up @@ -145,4 +146,47 @@ class IncrementalCompilerSpec extends BaseCompilerSpec {
comp.close()
}
}

it should "track dependencies for nested inner Java classes" in withTmpDir { tmp =>
val project = VirtualSubproject(tmp.toPath / "p1")
val comp = project.setup.createCompiler()
try {
val s1 =
"public class A { public Object i = new Object() { public Object ii = new Object() { public int i = B.b; }; }; }"
val s2 = "public class B { public static int b = 1; }"
val s2b = "public class B { public static int b = 1; public static int b2 = 1; }"
val s3 = "public class C { public static int c = 1; }"

val f1 = StringVirtualFile("A.java", s1)
val f2 = StringVirtualFile("B.java", s2)
val f2b = StringVirtualFile("B.java", s2b)
val f3 = StringVirtualFile("C.java", s3)

def compileJava(sources: VirtualFile*) = {
def incrementalJavaInputs(sources: VirtualFile*)(in: Inputs): Inputs = {
comp.withSrcs(sources.toArray)(
in.withOptions(
in.options
.withEarlyOutput(Optional.empty[Output])
// remove -YpickleXXX args
.withScalacOptions(comp.scalacOptions.toArray)
)
.withSetup(
in.setup.withIncrementalCompilerOptions(
// remove pipelining
in.setup.incrementalCompilerOptions.withPipelining(false)
)
)
)
}
comp.doCompileWithStore(newInputs = incrementalJavaInputs(sources: _*))
}

val res1 = compileJava(f1, f2, f3)
val res2 = compileJava(f1, f2b, f3)
assert(recompiled(res1, res2) == Set("A", "B"))
} finally {
comp.close()
}
}
}

0 comments on commit 40f515d

Please sign in to comment.