Bug 532029 - [annotation processor] RoundDispatcher.handleProcessor throws NoClassDefFoundError
Summary: [annotation processor] RoundDispatcher.handleProcessor throws NoClassDefFound...
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: APT (show other bugs)
Version: 4.7.2   Edit
Hardware: PC All
: P3 normal with 16 votes (vote)
Target Milestone: 4.14 M1   Edit
Assignee: Stephan Herrmann CLA
QA Contact:
URL:
Whiteboard:
Keywords: regression
: 547171 (view as bug list)
Depends on:
Blocks:
 
Reported: 2018-03-05 11:44 EST by Doug Simon CLA
Modified: 2020-09-30 19:43 EDT (History)
14 users (show)

See Also:
jarthana: review+


Attachments
JMH and its dependencies in one jar (2.19 MB, application/java-archive)
2018-03-05 11:44 EST, Doug Simon CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Doug Simon CLA 2018-03-05 11:44:16 EST
Created attachment 273004 [details]
JMH and its dependencies in one jar

Since roughly ECJ 4.7.2, we are seeing a NoClassDefFoundError when using an annotation processor from JMH[1]. This should be reproducible with the following TestJMH_1_18 class:

----
package test.jmh_1_18;
import org.openjdk.jmh.annotations.*;

@Warmup(iterations = 2)
@Measurement(iterations = 2)
@Fork(2)
public class TestJMH_1_18 {

    @Benchmark
    @BenchmarkMode(Mode.All)
    public void testJMH() {
    }
}
----

rm -rf bin; java -jar ecj-4.8M5.jar -cp jmh-all.jar -nowarn -d bin -processorpath jmh-all.jar -source 1.8 TestJMH_1_18.java
----------
1. ERROR in /Users/dsimon/fff/test/jmh_1_18/generated/TestJMH_1_18_testJMH_jmhTest.java (at line 0)
	package test.jmh_1_18.generated;
	^
Internal compiler error: java.lang.Exception: java.lang.NoClassDefFoundError: org/openjdk/jmh/runner/BenchmarkListEntry at org.eclipse.jdt.internal.compiler.apt.dispatch.RoundDispatcher.handleProcessor(RoundDispatcher.java:169)
----------
1 problem (1 error)

rm -rf bin; java -jar ecj-4.5.1.jar -cp jmh-all.jar -nowarn -d bin -processorpath jmh-all.jar -source 1.8 TestJMH_1_18.java

[1] http://openjdk.java.net/projects/code-tools/jmh/
Comment 1 Testo Nakada CLA 2018-08-30 02:45:52 EDT
I ran into this as well with Tycho 1.2.0
Comment 2 Doug Simon CLA 2018-12-10 03:26:36 EST
Any chance of this getting some attention? It's preventing the Graal project from supporting development on recent versions of Eclipse.
Comment 3 Stephan Herrmann CLA 2019-01-08 14:33:20 EST
I can reproduce with ecj starting from 4.7.2 and 4.8M3.

It fails in this stack (running JDT HEAD on JDK-9 in this case):

URLClassPath.getLoader(int) line: 352	
URLClassPath.getResource(String, boolean) line: 243	
URLClassLoader$1.run() line: 450	
URLClassLoader$1.run() line: 447	
AccessController.doPrivileged(PrivilegedExceptionAction<T>, AccessControlContext) line: not available [native method]	
URLClassLoader.findClass(String) line: 446	
URLClassLoader(ClassLoader).loadClass(String, boolean) line: 563	
URLClassLoader(ClassLoader).loadClass(String) line: 496	
BenchmarkGenerator.complete(GeneratorSource, GeneratorDestination) line: 132	
BenchmarkProcessor.process(Set<TypeElement>, RoundEnvironment) line: 59	
RoundDispatcher.handleProcessor(ProcessorInfo) line: 142	
RoundDispatcher.round() line: 113	
BatchAnnotationProcessorManager(BaseAnnotationProcessorManager).processAnnotations(CompilationUnitDeclaration[], ReferenceBinding[], boolean) line: 171	
Compiler.processAnnotations() line: 978	
Compiler.compile(ICompilationUnit[], boolean) line: 450	
Compiler.compile(ICompilationUnit[]) line: 426	
Main.performCompilation() line: 4704	
Main.compile(String[]) line: 1768	
Main.main(String[]) line: 1491	

Here null is returned because URLClassPath.closed == true;

Closing happens in this call stack:

URLClassPath.closeLoaders() line: 166	
URLClassLoader.close() line: 376	
BatchAnnotationProcessorManager.reset() line: 263	
Compiler.processAnnotations() line: 967	
Compiler.compile(ICompilationUnit[], boolean) line: 450	
Compiler.compile(ICompilationUnit[]) line: 426	
Main.performCompilation() line: 4704	
Main.compile(String[]) line: 1768	
Main.main(String[]) line: 1491	

This doesn't look awfully suspicious, if you ask me...

Note, that here:
  Compiler.compile(ICompilationUnit[], boolean) line: 450	
we have a flag 'lastRound'.

Should that be passed down so we this.annotationProcessorManager.reset() only for the last round?? Or is some of the reset() work needed between rounds?

Is that enough information for you to proceed?
Comment 4 Stephan Herrmann CLA 2019-05-10 16:44:16 EDT
*** Bug 547171 has been marked as a duplicate of this bug. ***
Comment 6 Jay Arthanareeswaran CLA 2019-05-29 05:58:34 EDT
(In reply to Stephan Herrmann from comment #3)

> Should that be passed down so we this.annotationProcessorManager.reset()
> only for the last round?? Or is some of the reset() work needed between
> rounds?

Thanks Stephan, for the investigation. The reset() is certainly needed between rounds, mostly for what we in super.reset(), but we need to find a way to do the close() only at the end of final round. I need to see if this information is already passed to the BaseAnnotationProcessorManager.
Comment 7 Jay Arthanareeswaran CLA 2019-05-29 06:11:08 EDT
(In reply to Jay Arthanareeswaran from comment #6)
> Thanks Stephan, for the investigation. The reset() is certainly needed
> between rounds, mostly for what we in super.reset(), but we need to find a
> way to do the close() only at the end of final round. I need to see if this
> information is already passed to the BaseAnnotationProcessorManager.

On closer look, this may not be sufficient. The "lastRound" in the compiler doesn't necessarily mean there won't be another round as the annotation processors can still generate more files triggering compilation. Perhaps, we can check for newly added units and classes and if none then invoke close(). Will see what it takes during 4.13.
Comment 8 Manoj N Palat CLA 2019-08-27 02:37:05 EDT
Bulk Move Out of 4.13
Comment 9 Thomas Wolf CLA 2019-09-09 10:12:51 EDT
What's the status here? We're running into this with org.eclipse.jdt:ecj:3.18.0.

See also https://github.com/spring-projects/sts4/issues/171, which is exactly the error we're seeing.

Note that the AspectJ developer(s) have actually fixed this in their patched ecj version. Unfortunately they include only a zip of the compiler sources in their repo, but:

* download https://github.com/eclipse/org.aspectj/blob/2bcf229024608c411d8effa248e63c8742b35508/org.eclipse.jdt.core/jdtcore-for-aspectj-src.zip (their sources before their fix)

* download https://github.com/eclipse/org.aspectj/blob/380ef0ec470fcf02820bde4d22d3bd1a9efaf999/org.eclipse.jdt.core/jdtcore-for-aspectj-src.zip (their sources after their fix)

Unpack both in different directories and compare to see the 

I cannot tell whether that way to fix it is complete or sufficient. It goes in the direction that is suggested here. (Delaying closing the classpath.)
Comment 10 Jay Arthanareeswaran CLA 2019-09-11 02:50:42 EDT
(In reply to Thomas Wolf from comment #9)
> What's the status here? We're running into this with
> org.eclipse.jdt:ecj:3.18.0.

Sorry, we couldn't spend time on this as the team is currently busy with the Java 13 work. However, since you have already found the fix, would you be able to bring that fix in the form of a patch? If there's a patch, we will be able to take this to closure sooner.
Comment 11 Eclipse Genie CLA 2019-09-11 04:46:03 EDT
New Gerrit change created: https://git.eclipse.org/r/149331
Comment 12 Thomas Wolf CLA 2019-09-11 04:48:24 EDT
(In reply to Eclipse Genie from comment #11)
> New Gerrit change created: https://git.eclipse.org/r/149331

There you go. As I wrote above: I have no idea if this way to fix it is sound. Please take over this change. (I also wouldn't know how to write tests for this, and I don't have the time to figure it out.)
Comment 13 Stephan Herrmann CLA 2019-09-12 08:23:11 EDT
(In reply to Thomas Wolf from comment #12)
> (In reply to Eclipse Genie from comment #11)
> > New Gerrit change created: https://git.eclipse.org/r/149331
> 
> There you go. As I wrote above: I have no idea if this way to fix it is
> sound. Please take over this change. (I also wouldn't know how to write
> tests for this, and I don't have the time to figure it out.)

Thanks, I slightly modified the patch:

- new method shouldn't be abstract, since this left some sub classes without an implementation

- renamed the method to speak more about intent (final cleanUp) rather than implementation (close a class loader, which only one subclass actually owns).

I manually checked with the steps from comment 0 that this indeed fixes the problem.

@Jay: Just like Thomas I'm not sufficiently versed in writing tests for annotation processing. Do you insist in having a test case, or may I push the fix without a test?
Comment 14 Jay Arthanareeswaran CLA 2019-09-12 09:14:04 EDT
(In reply to Stephan Herrmann from comment #13)
> @Jay: Just like Thomas I'm not sufficiently versed in writing tests for
> annotation processing. Do you insist in having a test case, or may I push
> the fix without a test?

+1 for the fix without a test. Thanks Stephan and Thomas!
Comment 16 Stephan Herrmann CLA 2019-09-12 12:05:05 EDT
(In reply to Eclipse Genie from comment #15)
> Gerrit change https://git.eclipse.org/r/149331 was merged to [master].
> Commit:
> http://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/
> ?id=328421a0239051bed525c280adc9937c7a561a78

Released for 4.14 M1
thanks
Comment 17 Manoj N Palat CLA 2019-10-09 06:17:16 EDT
Verified for Eclipse Version: 2019-12 (4.14) M1 with  Build id: I20191008-1800
Comment 18 Henry Tung CLA 2020-01-07 15:59:57 EST
This might need to be reopened. As far as I can tell, Compiler.processAnnotations() is called into multiple times, so closing after one call is still too premature.

Example from Gradle build:

Stacktrace where close happens:

java.lang.Throwable
        at java.net.URLClassLoader.close(URLClassLoader.java:288)
        at org.eclipse.jdt.internal.compiler.apt.dispatch.BatchAnnotationProcessorManager.cleanUp(BatchAnnotationProcessorManager.java:264)
        at org.eclipse.jdt.internal.compiler.Compiler.processAnnotations(Compiler.java:933)
        at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:450)
        at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:426)
        at org.eclipse.jdt.internal.compiler.batch.Main.performCompilation(Main.java:4771)
        at org.eclipse.jdt.internal.compiler.batch.Main.compile(Main.java:1773)
        at org.eclipse.jdt.internal.compiler.batch.Main.main(Main.java:1496)

Stacktrace where NoClassDefFoundError occurs:

java.lang.Throwable
        at dagger.internal.codegen.DaggerStatisticsCollector.processingStopped(DaggerStatisticsCollector.java:80)
        at dagger.internal.codegen.ComponentProcessor.postRound(ComponentProcessor.java:188)
        at dagger.shaded.auto.common.BasicAnnotationProcessor.process(BasicAnnotationProcessor.java:176)
        at org.eclipse.jdt.internal.compiler.apt.dispatch.RoundDispatcher.handleProcessor(RoundDispatcher.java:142)
        at org.eclipse.jdt.internal.compiler.apt.dispatch.RoundDispatcher.round(RoundDispatcher.java:113)
        at org.eclipse.jdt.internal.compiler.apt.dispatch.BaseAnnotationProcessorManager.processAnnotations(BaseAnnotationProcessorManager.java:171)
        at org.eclipse.jdt.internal.compiler.Compiler.processAnnotationsInternal(Compiler.java:995)
        at org.eclipse.jdt.internal.compiler.Compiler.processAnnotations(Compiler.java:931)
        at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:450)
        at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:469)
        at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:426)
        at org.eclipse.jdt.internal.compiler.batch.Main.performCompilation(Main.java:4771)
        at org.eclipse.jdt.internal.compiler.batch.Main.compile(Main.java:1773)
        at org.eclipse.jdt.internal.compiler.batch.Main.main(Main.java:1496)

Gradle error:

Internal compiler error: java.lang.Exception: java.lang.BootstrapMethodError: java.lang.NoClassDefFoundError: dagger/internal/codegen/DaggerStatisticsRecorder at org.eclipse.jdt.internal.compiler.apt.dispatch.RoundDispatcher.handleProcessor(RoundDispatcher.java:172)

Tested with ecj batch compiler 4.14 release.
Comment 19 Arthur van Dorp CLA 2020-08-26 04:26:19 EDT
(In reply to Henry Tung from comment #18)
> This might need to be reopened. As far as I can tell,
> Compiler.processAnnotations() is called into multiple times, so closing
> after one call is still too premature.

In the Scout project we see this error too. It is less common than before the fix but it still occurs. Possibly moving
this.annotationProcessorManager.cleanUp();
to the end of
Compiler.compile(ICompilationUnit[] sourceUnits, boolean lastRound)
instead of calling it prematurely in Compiler processAnnotations() could work?
Comment 20 Eclipse Genie CLA 2020-09-20 08:36:04 EDT
New Gerrit change created: https://git.eclipse.org/r/c/jdt/eclipse.jdt.core/+/169633
Comment 21 Ralph Steiner CLA 2020-09-20 08:43:54 EDT
Scout project here again. I have created a new patch, which will now hopefully fix this issue for us (graalvm and others). I assume the problem is that in case of a SourceTypeCollisionException (with lastRound == false) the new cleanUp method is called prematurely.
Comment 22 Jay Arthanareeswaran CLA 2020-09-21 23:40:59 EDT
(In reply to Ralph Steiner from comment #21)
> Scout project here again. I have created a new patch, which will now
> hopefully fix this issue for us (graalvm and others). I assume the problem
> is that in case of a SourceTypeCollisionException (with lastRound == false)
> the new cleanUp method is called prematurely.

When you say 'hopefully' and the patch doesn't have a test, it's that much more work for those reviewing :)

Will take a look at it this week.
Comment 23 Jay Arthanareeswaran CLA 2020-09-22 02:18:34 EDT
Patch looks alright to me. Something to note: In our entire test suite, there's just one testcase in BuilderTests.testBug468893() that triggers this code.
Comment 25 Jay Arthanareeswaran CLA 2020-09-22 02:20:56 EDT
Moving back to resolved. Not changing the target, but setting the whiteboard so that this shows in our verification list for 4.18 M1.
Comment 26 Carsten Hammer CLA 2020-09-22 15:03:54 EDT
Is it possible that because of this change some tests in jdt.ui fail now? There are annotations that now have a newline. 

Content not as expected: is
package test1;

import org.eclipse.jdt.annotation.NonNull;

public class E {
    public <T extends Number> double foo(T t) {
        @NonNull
        Number n=t;
        return n.doubleValue();
    }
}

Differs at pos 141: onNull^
     
expected:
package test1;

import org.eclipse.jdt.annotation.NonNull;

public class E {
    public <T extends Number> double foo(T t) {
        @NonNull Number n=t;
        return n.doubleValue();
    }
}
 expected:<...) {
        @NonNull[] Number n=t;
       ...> but was:<...) {
        @NonNull[
       ] Number n=t;
       ...> 

Could easily be that it is one of our jdt.ui gerrits that is responsible but just want to be sure and don't understand this right now...
Comment 27 Jay Arthanareeswaran CLA 2020-09-22 22:55:08 EDT
(In reply to Carsten Hammer from comment #26)
> Is it possible that because of this change some tests in jdt.ui fail now?
> There are annotations that now have a newline. 
> 
...
> Could easily be that it is one of our jdt.ui gerrits that is responsible but
> just want to be sure and don't understand this right now...

I highly doubt that. This change does nothing to alter the annotations. Hypothetically, it's possible that this fixed some annotation processor that is producing a different result now. But chances are very slim.
Comment 28 Manoj N Palat CLA 2020-09-30 19:43:39 EDT
Verified for 4.18 m1 Version: 2020-12 (4.18) Build id: I20200928-1800
[by code walkthrough]