smallrye / jandex

Java Annotation Indexer
Apache License 2.0
394 stars 94 forks source link

Error when running on Java 21-ea #310

Closed beikov closed 1 year ago

beikov commented 1 year ago

Trying to run the 3.1.1 Maven plugin with Java 21-ea, I get the following error:

Error:  Failed to execute goal io.smallrye:jandex-maven-plugin:3.1.1:jandex (make-index) on project blaze-persistence-integration-graphql-spqr: A type incompatibility occurred while executing io.smallrye:jandex-maven-plugin:3.1.1:jandex: class java.lang.Object cannot be cast to class [B (java.lang.Object and [B are in module java.base of loader 'bootstrap')
Error:  -----------------------------------------------------
Error:  realm =    plugin>io.smallrye:jandex-maven-plugin:3.1.1
Error:  strategy = org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy
Error:  urls[0] = file:/home/runner/.m2/repository/io/smallrye/jandex-maven-plugin/3.1.1/jandex-maven-plugin-3.1.1.jar
Error:  urls[1] = file:/home/runner/.m2/repository/org/codehaus/plexus/plexus-utils/3.5.0/plexus-utils-3.5.0.jar
Error:  urls[2] = file:/home/runner/.m2/repository/io/smallrye/jandex/3.1.1/jandex-3.1.1.jar
Error:  Number of foreign imports: 1
Error:  import: Entry[import  from realm ClassRealm[maven.api, parent: null]]

Any idea what the issue could be?

Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f) Maven home: /home/runner/.m2/wrapper/dists/apache-maven-3.6.3-bin/1iopthnavndlasol9gbrbg6bf2/apache-maven-3.6.3 Java version: 21-ea, vendor: Azul Systems, Inc., runtime: /opt/hostedtoolcache/Java_Zulu_jdk/21.0.0-ea.22/x64 Default locale: en, platform encoding: UTF-8 OS name: "linux", version: "5.15.0-1037-azure", arch: "amd64", family: "unix"

Ladicek commented 1 year ago

I've downloaded https://search.maven.org/artifact/com.blazebit/blaze-persistence-integration-graphql-spqr/1.6.8/jar and trying to index it with these JDKs:

openjdk version "21-beta" 2023-09-19
OpenJDK Runtime Environment Temurin-21+22-202305251619 (build 21-beta+22-202305251619)
OpenJDK 64-Bit Server VM Temurin-21+22-202305251619 (build 21-beta+22-202305251619, mixed mode, sharing)
openjdk version "21-ea" 2023-09-19
OpenJDK Runtime Environment Zulu21+57-CA (build 21-ea+22)
OpenJDK 64-Bit Server VM Zulu21+57-CA (build 21-ea+22, mixed mode, sharing)

They both seem to work just fine (as in, they write an index file and don't fail).

Could you please provide a JAR on which indexing fails? Alternatively, a stack trace might be enough (mvn -X).

beikov commented 1 year ago

The problem appears when the module is compiled with the Java 21 class file version. I guess it doesn't really matter what the content of the JAR file is, as long as there is one class file compiled with the latest version.

beikov commented 1 year ago

So far, I reproduced this issue locally with Azul Zulu JDK 21-ea build 22. Here is the JAR (added .zip to the end because GitHub doesn't allow uploading JAR files): blaze-persistence-integration-graphql-spqr-1.6.9-SNAPSHOT.jar.zip

Here is the full error:

Caused by: org.apache.maven.plugin.PluginExecutionException: A type incompatibility occurred while executing io.smallrye:jandex-maven-plugin:3.1.1:jandex: class java.lang.Object cannot be cast to class [B (java.lang.Object and [B are in module java.base of loader 'bootstrap')
-----------------------------------------------------
realm =    plugin>io.smallrye:jandex-maven-plugin:3.1.1
strategy = org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy
urls[0] = file:/C:/Users/cbeikov/.m2/repository/io/smallrye/jandex-maven-plugin/3.1.1/jandex-maven-plugin-3.1.1.jar
urls[1] = file:/C:/Users/cbeikov/.m2/repository/org/codehaus/plexus/plexus-utils/3.5.0/plexus-utils-3.5.0.jar
urls[2] = file:/C:/Users/cbeikov/.m2/repository/io/smallrye/jandex/3.1.1/jandex-3.1.1.jar
Number of foreign imports: 1
import: Entry[import  from realm ClassRealm[maven.api, parent: null]]

-----------------------------------------------------

    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:199)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:210)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:156)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:148)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:117)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:81)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:56)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:128)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:305)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:192)
    at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:105)
    at org.apache.maven.cli.MavenCli.execute (MavenCli.java:957)
    at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:289)
    at org.apache.maven.cli.MavenCli.main (MavenCli.java:193)
    at jdk.internal.reflect.DirectMethodHandleAccessor.invoke (DirectMethodHandleAccessor.java:103)
    at java.lang.reflect.Method.invoke (Method.java:578)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:282)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:225)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:406)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:347)
    at jdk.internal.reflect.DirectMethodHandleAccessor.invoke (DirectMethodHandleAccessor.java:103)
    at java.lang.reflect.Method.invoke (Method.java:578)
    at org.apache.maven.wrapper.BootstrapMainStarter.start (BootstrapMainStarter.java:39)
    at org.apache.maven.wrapper.WrapperExecutor.execute (WrapperExecutor.java:122)
    at org.apache.maven.wrapper.MavenWrapperMain.main (MavenWrapperMain.java:61)
Caused by: java.lang.ClassCastException: class java.lang.Object cannot be cast to class [B (java.lang.Object and [B are in module java.base of loader 'bootstrap')
    at org.jboss.jandex.StrongInternPool$ByteArrayInternPool.hash (StrongInternPool.java:625)
    at org.jboss.jandex.StrongInternPool.intern (StrongInternPool.java:237)
    at org.jboss.jandex.NameTable.intern (NameTable.java:101)
    at org.jboss.jandex.IndexWriterV2.deepIntern (IndexWriterV2.java:1027)
    at org.jboss.jandex.IndexWriterV2.addMethodList (IndexWriterV2.java:1011)
    at org.jboss.jandex.IndexWriterV2.addClass (IndexWriterV2.java:932)
    at org.jboss.jandex.IndexWriterV2.buildTables (IndexWriterV2.java:888)
    at org.jboss.jandex.IndexWriterV2.write (IndexWriterV2.java:199)
    at org.jboss.jandex.IndexWriter.write (IndexWriter.java:102)
    at org.jboss.jandex.IndexWriter.write (IndexWriter.java:66)
    at org.jboss.jandex.maven.JandexGoal.execute (JandexGoal.java:172)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:137)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:210)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:156)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:148)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:117)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:81)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:56)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:128)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:305)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:192)
    at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:105)
    at org.apache.maven.cli.MavenCli.execute (MavenCli.java:957)
    at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:289)
    at org.apache.maven.cli.MavenCli.main (MavenCli.java:193)
    at jdk.internal.reflect.DirectMethodHandleAccessor.invoke (DirectMethodHandleAccessor.java:103)
    at java.lang.reflect.Method.invoke (Method.java:578)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:282)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:225)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:406)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:347)
    at jdk.internal.reflect.DirectMethodHandleAccessor.invoke (DirectMethodHandleAccessor.java:103)
    at java.lang.reflect.Method.invoke (Method.java:578)
    at org.apache.maven.wrapper.BootstrapMainStarter.start (BootstrapMainStarter.java:39)
    at org.apache.maven.wrapper.WrapperExecutor.execute (WrapperExecutor.java:122)
    at org.apache.maven.wrapper.MavenWrapperMain.main (MavenWrapperMain.java:61)
Ladicek commented 1 year ago

No, that doesn't seem to reproduce. Do you have a Maven project I can clone and build myself? https://github.com/Blazebit/blaze-persistence doesn't seem to use Jandex 3.1.1 anywhere :thinking:

beikov commented 1 year ago

I do. Checkout the contents of this PR and first build with .\mvnw clean install -P "hibernate-5.6,h2,spring-data-2.7.x,deltaspike-1.9" "-Duser.country=US" "-Duser.language=en" "-Dmaven.javadoc.skip" "-Dmain.java.version=21" "-Dtest.java.version=21" "-Djdk8.home=<PATH_TO_JDK_8>" -DskipTests

It will fail at that module, but then you can build just that single module by appending -pl .\integration\graphql-spqr\

beikov commented 1 year ago

And don't forget to make sure that your JAVA_HOME env variable points to a JDK 21

Ladicek commented 1 year ago

OK, reproduced it now. Thanks!

Ladicek commented 1 year ago

OK, the issue is caused by the performance improvements made in Jandex 3.1.0. As part of those improvements, the hash table used for object interning was refactored to be more type-safe (with one subclass of the hash table per interned type), but the null marker object was left unchanged (a single new Object() instance). Replacing that global marker object with a per-subclass marker object solves the problem, but isn't exactly nice. I'll see if I can come up with something better.

Ladicek commented 1 year ago

I kept digging, because while my last comment is correct, there must be more to it, and indeed I found a really strange behavior of JDK 21.

Say I have these 2 classes:

public class Foo {
    public Foo(String... strings) {
    }
}
public class Bar {
    public Foo getFoo() {
        return new Foo() {};
    }
}

Then compiling them using javac -g *.java and then decompiling the inner class using javap -v -p Bar\$1.class leads to the following results:

JDK 8, 11, 17:

...

  Bar$1(Bar, java.lang.String...);
    descriptor: (LBar;[Ljava/lang/String;)V
    flags: ACC_VARARGS
    Code:
      stack=2, locals=3, args_size=3
         0: aload_0
         1: aload_1
         2: putfield      #1                  // Field this$0:LBar;
         5: aload_0
         6: aload_2
         7: invokespecial #2                  // Method Foo."<init>":([Ljava/lang/String;)V
        10: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  this   LBar$1;
            0      11     1 this$0   LBar;
            0      11     2 strings   [Ljava/lang/String;

...

JDK 21-ea:

...

  Bar$1(Bar, java.lang.String...);
    descriptor: (LBar;[Ljava/lang/String;)V
    flags: (0x0080) ACC_VARARGS
    Code:
      stack=2, locals=3, args_size=3
         0: aload_0
         1: aload_2
         2: invokespecial #1                  // Method Foo."<init>":([Ljava/lang/String;)V
         5: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       6     0  this   LBar$1;
            0       6     1 this$0   LBar;
            0       6     2 strings   [Ljava/lang/String;
    MethodParameters:
      Name                           Flags
      <no name>                      final mandated
      <no name>

...

That is, javac from JDK 21-ea produces the MethodParameters attribute even if -parameters was not used. The attribute has a name_index of 0 for each parameter, which is valid per the JVMS, but Jandex can't cope with it. So that needs fixing too.

Ladicek commented 1 year ago

Aside: if -parameters is passed to javac, then the MethodParameters attribute is as-expected on all tested JDKs:

    MethodParameters:
      Name                           Flags
      this$0                         final mandated
      strings
Ladicek commented 1 year ago

For historical reference, here are JDK issues that explain the differences in bytecode demonstrated above: