Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use the most up-to-date analysis for binary to source class name lookup #1287

Merged
merged 1 commit into from
Dec 7, 2023

Conversation

Friendseeker
Copy link
Member

@Friendseeker Friendseeker commented Nov 26, 2023

This PR fixes #1268

Cause

During Dependency Extraction, internalBinaryToSourceClassName is used to map binary class name to source class name. It is constructed at the first cycle. Hence in subsequent cycles, it is outdated. This results in missing internal member reference dependency between a.Use pkgName.Test.

To fix it, we rebuild lookup using the most up-to-date analysis.

Other changes

This PR also removed internalSourceToClassNamesMap. It is currently not used, but it has the same staleness problem as internalBinaryToSourceClassName. So might as well remove it to prevent someone from accidentally using it in the future.

Why empty-package erroneously passed on zinc repo

The complier bridge did recognize a dependency between a.Use and pkgName.Test exists. However, since internalBinaryToSourceClassName was stale, zinc failed to map pkgName.Test back to its .scala source file, and instead misclassified the dependency as a external library dependency. This can be observed in the zinc test log:

[debug] previous = Stamps for: 4 products, 2 sources, 1 libraries
[debug] current source = Set(${BASE}/pro/Use.scala)
[debug] Invalidating '${BASE}/pro/target/classes/pkgName/Test.class' because pkgName.Test$ came from analysis Analysis: 2 Scala sources, 4 classes

Then in invalidateInitial, all sources that depends on pkgName.Test library is invalidated via val byLibraryDep = changes.libraryDeps.flatMap(previous.usesLibrary)

Validating the fix

empty-package test is modified to assert dependency between a.Use pkgName.Test must exist after the 2nd compilation run.

Running the modified empty-package on current latest develop branch commit now results in test failure.

@Friendseeker Friendseeker changed the title Use the most up-to-date analysis for binary source lookup Use the most up-to-date analysis for binary source to source class name lookup Nov 26, 2023
@Friendseeker Friendseeker changed the title Use the most up-to-date analysis for binary source to source class name lookup Use the most up-to-date analysis for binary name to source class name lookup Nov 26, 2023
@Friendseeker Friendseeker changed the title Use the most up-to-date analysis for binary name to source class name lookup Use the most up-to-date analysis for binary to source class name lookup Nov 26, 2023
@Friendseeker
Copy link
Member Author

Friendseeker commented Dec 6, 2023

TODO:

  • The edited empty-package test passes in current latest 'develop' commit.
    • Expected: edited empty-package test should fail.

Remove internalBinaryToSourceClassName altogether

Remove internalSourceToClassNamesMap too

Strengthen empty-package test

Call checkDependencies properly

It seems that in other exams, a previous compile immediately before is required

Strengthened empty-package properly, along with comments explaining why it is being done
Copy link
Member

@eed3si9n eed3si9n left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel slightly nervous about deleting things, but hopefully this is ok.

@eed3si9n eed3si9n merged commit be2bb54 into sbt:develop Dec 7, 2023
7 checks passed
@Friendseeker
Copy link
Member Author

Friendseeker commented Dec 7, 2023

I feel slightly nervous about deleting things, but hopefully this is ok.

Here's my chain of thought:

The only case for problem to emerge is for incHandlerOpt to be None while internalBinaryToSourceClassName lookup is still needed. For Scala compilation, incHandlerOpt is never None. For Java compilation, incHandlerOpt is None, but internalBinaryToSourceClassName is called in one place only:

// JavaAnalyze.scala
(getMappedSource(fromBinaryName), getMappedSource(onBinaryName)) match {
  case (Some(fromClassName), Some(onClassName)) =>
    trapAndLog(log) {
      analysis.classDependency(onClassName, fromClassName, context)
    }
  case (Some(fromClassName), None) =>
    trapAndLog(log) {
      val cachedOrigin = classfilesCache.get(onBinaryName)
      for (file <- cachedOrigin.orElse(loadFromClassloader())) {
        val binaryFile: Path = remapClassFile(file)
        analysis.binaryDependency(
          binaryFile,
          onBinaryName,
          fromClassName,
          source,
          context
        )
      }
    }
  case (None, _) => // It could be a stale class file, ignore

In the above code, analysis.binaryDependency calls internalBinaryToSourceClassName, however it only happens when getMappedSource(onBinaryName) is None, which means the source file does not exist anyways.


Therefore, if the above chain of thought is sound, this change should be okay.

@Friendseeker Friendseeker deleted the empty-package-fix branch December 7, 2023 05:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Missing invalidation when deleting source file
2 participants