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

Fix NPE in ClasspathMultiDirectory#directoryList() #545

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

jdneo
Copy link
Contributor

@jdneo jdneo commented Nov 17, 2022

Signed-off-by: Sheng Chen sheche@microsoft.com

What it does

Fix a potential NPE issue in ClasspathMultiDirectory. the member this.directoryCache could be null if cleanup() is called

How to test

  1. Use the sample code https://github.com/micronaut-projects/micronaut-examples/tree/master/hello-world-java
  2. Enable Maven JDT APT (Window > Preferences > Maven > Annotation Processing > Automatically configure JDT APT)
  3. Import as a Maven project
  4. Build the project, and run the test case HelloControllerTest

Behavior without the change

  1. Some class files are not generated into target/test-classes/example
  2. The test case HelloControllerTest fails

Behavior with the change

  1. class files are generated successfully into target/test-classes/example
  2. The test case HelloControllerTest passes

Author checklist

@fbricon
Copy link

fbricon commented Nov 17, 2022

@jdneo do you know why directoryCache is null in the context of those micronaut projects?

@iloveeclipse
Copy link
Member

What is missing is same change in the parent class, and a test.

@jdneo
Copy link
Contributor Author

jdneo commented Nov 17, 2022

@fbricon Not yet, I can investigate more tomorrow. But might need some time since I'm not quite familiar with the code base now.

If anyone has some background knowledge/hints that will be helpful :)

@jdneo
Copy link
Contributor Author

jdneo commented Nov 17, 2022

@iloveeclipse yeah, I could do that after I get some certain understanding of this problem

@stephan-herrmann
Copy link
Contributor

Fix a potential NPE issue in ClasspathMultiDirectory. the member this.directoryCache could be null if cleanup() is called

Scanning the call hierarchy of ClasspathDirectory.cleanup() I see many clients who systematically discard the NameEnvironment after cleanup, where the name environment should hold the only reference to the classpath directory. Hence, nobody should be able to see the null in directoryCache.

Seams like one of those clients didn't observe that discipline?

@jdneo
Copy link
Contributor Author

jdneo commented Nov 18, 2022

I found that the same NPE issues can be found in bugzilla as well, but for different use cases.

For this PR, looks like it is caused by multiple instances reference the same NameEnvironment. See following screenshots:

When triggering update maven project, the code goes to:

BatchImageBuilder imageBuilder = new BatchImageBuilder(this, true, CompilationGroup.MAIN);
BatchImageBuilder testImageBuilder = new BatchImageBuilder(imageBuilder, true, CompilationGroup.TEST);

image

And then, during the build, some typebindings are generated with the same nameEnvironment

image

After imageBuilder.build();, it starts to clean up
image

At this time, the directoryCache is set to null.

Then, the testImageBuilder starts to build and the code goes to:

image

And NPE happens.

I'm thinking that, maybe reset directoryCache is better than set it to null when cleaning up?


Update:

Looks like the root cause is that, the annotation processor dependency (In this case, it's micronaut) caches the AnnotationMemberValue in a static map, whose nameEnvironment has been cleaned up after building the main source set.
When building the test source set, the 'outdated' AnnotationMemberValue is picked from the cache and causes the NPE when it's trying to get the binding.

Though we can make sure we can discard the NameEnvironment after cleanup in JDT's implementation, but for those annotation processors there is no guarantee that the implementation is aware of this. That makes setting it to null kind of dangerous, I think.

To simply fix this issue, maybe we can just add null checks and return null directly if the directoryCache is null.

@stephan-herrmann
Copy link
Contributor

To simply fix this issue, maybe we can just add null checks and return null directly if the directoryCache is null.

That sounds fair.

Still I'm a bit worried that perhaps we are leaking internal structures, inviting all kinds of unexpected usage.

@jarthana can you say a word on the life cycle of IdeBuildProcessingEnvImpl? It seems to hold on to stuff like a LookupEnvironment which may not be suitable for infinite usage by clients. Reminds me of similar problems where clean-up of scopes breaks clients of DOM bindings triggering on-demand resolving. How much out-off-band resolving can methods on TypeElementImpl and friends trigger, that might find the LE beyond its best-before date?

@jarthana
Copy link
Member

@jarthana can you say a word on the life cycle of IdeBuildProcessingEnvImpl? It seems to hold on to stuff like a LookupEnvironment which may not be suitable for infinite usage by clients. Reminds me of similar problems where clean-up of scopes breaks clients of DOM bindings triggering on-demand resolving. How much out-off-band resolving can methods on TypeElementImpl and friends trigger, that might find the LE beyond its best-before date?

I will have to check, but a quick search for BaseProcessingEnvImpl.getLookupEnvironment() brings up a lot of results :)

Signed-off-by: Sheng Chen <sheche@microsoft.com>
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.

5 participants