micronaut-projects / micronaut-test

Repository for Test Related Utilities for Micronaut
Apache License 2.0
83 stars 61 forks source link

NativeTest fails with UnsupportedFeatureException using Kotlin and Kotest on Java 21 #933

Open davidsonnabend opened 9 months ago

davidsonnabend commented 9 months ago

Expected Behavior

Running ./gradlew nativeTest will compile and execute the tests as native code.

Actual Behaviour

When running ./gradlew nativeTest, the gradle task nativeTestCompile fails during [2/8] Performing analysis... with the following erorr:

Fatal error: org.graalvm.compiler.debug.GraalError: org.graalvm.compiler.debug.GraalError: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: An object of type 'kotlin.reflect.jvm.internal.impl.builtins.jvm.JvmBuiltInClassDescriptorFactory' was found in the image heap. This type, however, is marked for initialization at image run time for the following reason: classes are initialized at run time by default.
This is not allowed for correctness reasons: All objects that are stored in the image heap must be initialized at build time.

You now have two options to resolve this:

1) If it is intended that objects of type 'kotlin.reflect.jvm.internal.impl.builtins.jvm.JvmBuiltInClassDescriptorFactory' are persisted in the image heap, add 

    '--initialize-at-build-time=kotlin.reflect.jvm.internal.impl.builtins.jvm.JvmBuiltInClassDescriptorFactory'

to the native-image arguments. Note that initializing new types can store additional objects to the heap. It is advised to check the static fields of 'kotlin.reflect.jvm.internal.impl.builtins.jvm.JvmBuiltInClassDescriptorFactory' to see if they are safe for build-time initialization,  and that they do not contain any sensitive data that should not become part of the image.

2) If these objects should not be stored in the image heap, you can use 

    '--trace-object-instantiation=kotlin.reflect.jvm.internal.impl.builtins.jvm.JvmBuiltInClassDescriptorFactory'

to find classes that instantiate these objects. Once you found such a class, you can mark it explicitly for run time initialization with 

    '--initialize-at-run-time=<culprit>'

to prevent the instantiation of the object.

The following detailed trace displays from which field in the code the object was reached.
        at org.graalvm.nativeimage.pointsto/com.oracle.graal.pointsto.util.AnalysisFuture.setException(AnalysisFuture.java:49)
------------------------------------------------------------------------------------------------------------------------
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:322)
        at org.graalvm.nativeimage.pointsto/com.oracle.graal.pointsto.util.AnalysisFuture.ensureDone(AnalysisFuture.java:63)
        at org.graalvm.nativeimage.pointsto/com.oracle.graal.pointsto.heap.ImageHeapInstance.readFieldValue(ImageHeapInstance.java:110)
        at org.graalvm.nativeimage.pointsto/com.oracle.graal.pointsto.heap.ImageHeapScanner.onObjectReachable(ImageHeapScanner.java:472)
        at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.heap.SVMImageHeapScanner.onObjectReachable(SVMImageHeapScanner.java:156)
        at org.graalvm.nativeimage.pointsto/com.oracle.graal.pointsto.heap.ImageHeapScanner.lambda$markReachable$5(ImageHeapScanner.java:452)
        at org.graalvm.nativeimage.pointsto/com.oracle.graal.pointsto.heap.ImageHeapScanner.lambda$postTask$14(ImageHeapScanner.java:695)
        at org.graalvm.nativeimage.pointsto/com.oracle.graal.pointsto.util.CompletionExecutor.executeCommand(CompletionExecutor.java:187)
        at org.graalvm.nativeimage.pointsto/com.oracle.graal.pointsto.util.CompletionExecutor.lambda$executeService$0(CompletionExecutor.java:171)
        at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1423)
        at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387)
        at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312)
        at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843)
        at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808)
        at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188)

Steps To Reproduce

  1. Create new micronaut application using micronaut launch or clone example application
    • Java Version: 21
    • Language: Kotlin
    • Build Tool: Gradle Kotlin
    • Test Framework: Kotest
  2. Run native test using ./gradlew nativeTest

Environment Information

Example Application

https://github.com/davidsonnabend/micronaut-native-kotest

Version

4.2.x

rawilder commented 2 months ago

This is happening with Java 17 as well. Discovered while trying to get one of my applications running on graalvm. Funnily enough, it works for compiling and running, it's just the nativeTest that fails (for the original example above, java 17 example, and my own app).