oracle / graal

GraalVM compiles Java applications into native executables that start instantly, scale fast, and use fewer compute resources 🚀
https://www.graalvm.org
Other
20.44k stars 1.64k forks source link

[GR-24133] JNI error while building SWT native image on MacOS #2232

Open mbarbeaux opened 4 years ago

mbarbeaux commented 4 years ago

Describe GraalVM and your environment :

Apple clang version 11.0.0 (clang-1100.0.33.17)
Target: x86_64-apple-darwin19.3.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
@(#)PROGRAM:ld  PROJECT:ld64-530
BUILD 18:57:17 Dec 13 2019
configured to support archs: armv6 armv7 armv7s arm64 arm64e arm64_32 i386 x86_64 x86_64h armv6m armv7k armv7m armv7em
Library search paths:
    /usr/local/lib
    /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib
Framework search paths:
    /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/

Have you verified this issue still happens when using the latest snapshot? No

Describe the issue Try to build a simple Hello World SWT application works both on Linux and Windows 10 with GraalVM 20.0.0 java8 CE, but not on Mac OS. Despite running the agent to generate JNI and other configuration files, it fails at build image time with an error :

atal error: com.oracle.graal.pointsto.util.AnalysisError$ParsingError: Error encountered while parsing org.eclipse.swt.widgets.Display.windowProc(long, long, long, long, long, long)
Parsing context:
    parsing com.oracle.svm.jni.JNIJavaCallWrappers.jniInvoke_VARARGS:Lorg_eclipse_swt_widgets_Display_2_0002ewindowProc_00028JJJJJJ_00029J(generated:0)

    at com.oracle.graal.pointsto.util.AnalysisError.parsingError(AnalysisError.java:138)
    at com.oracle.graal.pointsto.flow.MethodTypeFlow.doParse(MethodTypeFlow.java:323)
    at com.oracle.graal.pointsto.flow.MethodTypeFlow.ensureParsed(MethodTypeFlow.java:300)
    at com.oracle.graal.pointsto.flow.MethodTypeFlow.addContext(MethodTypeFlow.java:107)
    at com.oracle.graal.pointsto.flow.StaticInvokeTypeFlow.update(InvokeTypeFlow.java:346)
    at com.oracle.graal.pointsto.BigBang$2.run(BigBang.java:511)
    at com.oracle.graal.pointsto.util.CompletionExecutor.lambda$execute$0(CompletionExecutor.java:171)
    at java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1402)
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
    at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Caused by: org.graalvm.compiler.java.BytecodeParser$BytecodeParserError: org.graalvm.compiler.debug.GraalError: java.lang.reflect.InvocationTargetException
    at parsing org.eclipse.swt.widgets.Display.windowProc(Display.java:6681)
    at org.graalvm.compiler.java.BytecodeParser.throwParserError(BytecodeParser.java:2582)
    at com.oracle.svm.hosted.phases.SharedGraphBuilderPhase$SharedBytecodeParser.throwParserError(SharedGraphBuilderPhase.java:94)
    at org.graalvm.compiler.java.BytecodeParser.iterateBytecodesForBlock(BytecodeParser.java:3402)
    at org.graalvm.compiler.java.BytecodeParser.processBlock(BytecodeParser.java:3204)
    at org.graalvm.compiler.java.BytecodeParser.build(BytecodeParser.java:1085)
    at org.graalvm.compiler.java.BytecodeParser.buildRootMethod(BytecodeParser.java:979)
    at org.graalvm.compiler.java.GraphBuilderPhase$Instance.run(GraphBuilderPhase.java:84)
    at org.graalvm.compiler.phases.Phase.run(Phase.java:49)
    at org.graalvm.compiler.phases.BasePhase.apply(BasePhase.java:197)
    at org.graalvm.compiler.phases.Phase.apply(Phase.java:42)
    at org.graalvm.compiler.phases.Phase.apply(Phase.java:38)
    at com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder.parse(MethodTypeFlowBuilder.java:221)
    at com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder.apply(MethodTypeFlowBuilder.java:340)
    at com.oracle.graal.pointsto.flow.MethodTypeFlow.doParse(MethodTypeFlow.java:310)
    ... 10 more
Caused by: org.graalvm.compiler.debug.GraalError: java.lang.reflect.InvocationTargetException
    at org.graalvm.compiler.debug.GraalError.shouldNotReachHere(GraalError.java:55)
    at com.oracle.graal.pointsto.meta.AnalysisMethod.<init>(AnalysisMethod.java:149)
    at com.oracle.graal.pointsto.meta.AnalysisUniverse.createMethod(AnalysisUniverse.java:412)
    at com.oracle.graal.pointsto.meta.AnalysisUniverse.lookupAllowUnresolved(AnalysisUniverse.java:400)
    at com.oracle.graal.pointsto.infrastructure.WrappedConstantPool.lookupMethod(WrappedConstantPool.java:116)
    at org.graalvm.compiler.java.BytecodeParser.lookupMethodInPool(BytecodeParser.java:4285)
    at com.oracle.svm.hosted.phases.SharedGraphBuilderPhase$SharedBytecodeParser.lookupMethodInPool(SharedGraphBuilderPhase.java:107)
    at org.graalvm.compiler.java.BytecodeParser.lookupMethod(BytecodeParser.java:4279)
    at org.graalvm.compiler.java.BytecodeParser.genInvokeStatic(BytecodeParser.java:1656)
    at org.graalvm.compiler.java.BytecodeParser.processBytecode(BytecodeParser.java:5288)
    at org.graalvm.compiler.java.BytecodeParser.iterateBytecodesForBlock(BytecodeParser.java:3397)
    ... 21 more
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.GeneratedMethodAccessor4.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.oracle.graal.pointsto.meta.AnalysisMethod.<init>(AnalysisMethod.java:147)
    ... 30 more
Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.eclipse.swt.internal.cocoa.Selector
    at org.eclipse.swt.widgets.Display.$SWITCH_TABLE$org$eclipse$swt$internal$cocoa$Selector(Display.java:104)
    ... 34 more

Describe the full native-image command

Capture full native-image command by running with the `--verbose` flag e.g.: ``` native-image --verbose [... other args] ```
native-image -cp target/graalvm-swt-native-image-1.0-SNAPSHOT.jar -H:Class=com.github.mbarbeaux.HelloSW -H:+ReportExceptionStackTraces -H:+TraceClassInitialization -H:+AllowVMInspection -H:+JNI --allow-incomplete-classpath --verbose --no-server --no-fallback --report-unsupported-elements-at-runtime -DremoveUnusedAutoconfig=true

Code snippet or code repository that reproduces the issue

https://github.com/mbarbeaux/graalvm-swt-native-image

Steps to reproduce the issue Please include both build steps as well as run steps

  1. git clone https://github.com/mbarbeaux/graalvm-swt-native-image
  2. ./mvnw -Pagent clean package to generate/update configuration files with GraalVM agent
  3. ./mvnw -Pnative clean package to launch the native image building

Expected behavior On Windows 10 and Ubuntu 18.04 LTS, using same version of GraalVM 20.0.0 java 8 CE, native image building succeed and the given native image work as expected : a SWT window opens with a Hello world message. On Mac OS, it should work the same way.

Additional context Don't know if it's the problem, but on MacOS, I had to add the VM option -XstartOnFirstThread in order to make the Java program and the GraalVM agent work :

java -jar target/target/graalvm-swt-native-image-1.0-SNAPSHOT.jar

produces this error :

***WARNING: Display must be created on main thread due to Cocoa restrictions. Use vmarg -XstartOnFirstThread
Exception in thread "main" org.eclipse.swt.SWTException: Invalid thread access
    at org.eclipse.swt.SWT.error(SWT.java:4720)
    at org.eclipse.swt.SWT.error(SWT.java:4635)
    at org.eclipse.swt.SWT.error(SWT.java:4606)
    at org.eclipse.swt.widgets.Display.error(Display.java:1112)
    at org.eclipse.swt.widgets.Display.createDisplay(Display.java:853)
    at org.eclipse.swt.widgets.Display.create(Display.java:837)
    at org.eclipse.swt.graphics.Device.<init>(Device.java:132)
    at org.eclipse.swt.widgets.Display.<init>(Display.java:736)
    at org.eclipse.swt.widgets.Display.<init>(Display.java:727)
    at com.github.mbarbeaux.HelloSWT.main(HelloSWT.java:12)

After adding the -XstartOnFirstThread VM option to the java command, the agent works as expected the SWT window opens correctly and the GraalVM config files are generated. But still the native image fails at build time. I tried to add the option -J-XstartOnFirstThread to the native-image command but it didn't solve the problem.

mbarbeaux commented 4 years ago

Ok so I have some kind of update for this ticket.

After many tests, I finally discovered it works as expected if I include the SWT source code manually in my project source directory. The native-image performs the compilation and the executable works fine on MacOS now.

So it seems that, using the official SWT jar file, native-image isn't able to perform the compilation. But if I remove the jar from the classpath and instead copy the SWT source code in my source dir, let maven compile it, create a jar file then execute native image, it works flawlessly.

Don't know what's wrong with the official MacOS cocoa SWT jar, here is the URL of the latest version : https://repo1.maven.org/maven2/org/eclipse/platform/org.eclipse.swt.cocoa.macosx.x86_64/3.114.0/org.eclipse.swt.cocoa.macosx.x86_64-3.114.0.jar

Is it because the jar includes shared libraries ? But it was the same for the Windows and Linux versions of the jar, which both work fine

Barteks2x commented 4 years ago

I'm running into the same issue on graalvm java 11. Any idea how to fix it when I'm not using maven?

I'm using gradle for build, and I'm running it on github actions.

mbarbeaux commented 4 years ago

I got it working successfully on Windows, Linux and MacOS with GraalVM 20.0.0 java 11 version.

I just took the SWT java sources and add them to my project source path (do not use the JAR provided by SWT), run the GraalVM agent to generate config files for JNI, reflection, resources and proxies, and it will work at native compilation time. Of course you will have to also download the SWT native shared libraries and add the "-Dswt.library.path=/path/to/shared/libraries" option when running the native image.

Just take care because SWT sources differ for each operating system.

Barteks2x commented 4 years ago

I did some more investigation here and it seems like the main difference is that the SWT jar is compiled using the eclipse compiler, and not the standard java compiler. After some further investigation, it seems to be mainly a difference in static initialization order. I was able to reproduce similar issue even at runtime, with the "wrong" initialization order. With that in might it should be possible to force the right initialization order by adding --initialize-at-build-time or forcing intitialization at runtime arguments but testing it is going to take forever because my only access to mac build is through github actions. it's not possible as that would require statically initializing most of SWT at build time.

The first error that actually appear is:

2020-04-30T15:15:35.1196130Z Parsing context:
2020-04-30T15:15:35.1201600Z    parsing com.oracle.svm.jni.JNIJavaCallWrappers.jniInvoke_VARARGS:Lorg_eclipse_swt_widgets_Display_2_0002eapplicationProc_00028JJJJ_00029J(generated:0)
2020-04-30T15:15:35.1201720Z 
2020-04-30T15:15:35.1201970Z    at com.oracle.graal.pointsto.util.AnalysisError.parsingError(AnalysisError.java:138)
2020-04-30T15:15:35.1202610Z    at com.oracle.graal.pointsto.flow.MethodTypeFlow.doParse(MethodTypeFlow.java:323)
2020-04-30T15:15:35.1202990Z    at com.oracle.graal.pointsto.flow.MethodTypeFlow.ensureParsed(MethodTypeFlow.java:300)
2020-04-30T15:15:35.1203380Z    at com.oracle.graal.pointsto.flow.MethodTypeFlow.addContext(MethodTypeFlow.java:107)
2020-04-30T15:15:35.1203780Z    at com.oracle.graal.pointsto.flow.StaticInvokeTypeFlow.update(InvokeTypeFlow.java:346)
2020-04-30T15:15:35.1204090Z    at com.oracle.graal.pointsto.BigBang$2.run(BigBang.java:511)
2020-04-30T15:15:35.1211560Z    at com.oracle.graal.pointsto.util.CompletionExecutor.lambda$execute$0(CompletionExecutor.java:171)
2020-04-30T15:15:35.1613390Z    at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1426)
2020-04-30T15:15:35.1614360Z    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
2020-04-30T15:15:35.1615170Z    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
2020-04-30T15:15:35.1615330Z    at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
2020-04-30T15:15:35.1615670Z    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
2020-04-30T15:15:35.1615860Z    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
2020-04-30T15:15:35.1616160Z Caused by: org.graalvm.compiler.java.BytecodeParser$BytecodeParserError: org.graalvm.compiler.debug.GraalError: java.lang.reflect.InvocationTargetException
2020-04-30T15:15:35.1636020Z    at parsing org.eclipse.swt.widgets.Display.applicationProc(Display.java:5620)
2020-04-30T15:15:35.1636380Z    at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.throwParserError(BytecodeParser.java:2582)
2020-04-30T15:15:35.1640430Z    at com.oracle.svm.hosted.phases.SharedGraphBuilderPhase$SharedBytecodeParser.throwParserError(SharedGraphBuilderPhase.java:94)
2020-04-30T15:15:35.1645450Z    at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.iterateBytecodesForBlock(BytecodeParser.java:3402)
2020-04-30T15:15:35.1659190Z    at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.processBlock(BytecodeParser.java:3204)
2020-04-30T15:15:35.1690950Z    at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.build(BytecodeParser.java:1085)
2020-04-30T15:15:35.1792240Z    at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.buildRootMethod(BytecodeParser.java:979)
2020-04-30T15:15:35.1822800Z    at jdk.internal.vm.compiler/org.graalvm.compiler.java.GraphBuilderPhase$Instance.run(GraphBuilderPhase.java:84)
2020-04-30T15:15:35.1823080Z    at jdk.internal.vm.compiler/org.graalvm.compiler.phases.Phase.run(Phase.java:49)
2020-04-30T15:15:35.1823220Z    at jdk.internal.vm.compiler/org.graalvm.compiler.phases.BasePhase.apply(BasePhase.java:197)
2020-04-30T15:15:35.1823330Z    at jdk.internal.vm.compiler/org.graalvm.compiler.phases.Phase.apply(Phase.java:42)
2020-04-30T15:15:35.1823460Z    at jdk.internal.vm.compiler/org.graalvm.compiler.phases.Phase.apply(Phase.java:38)
2020-04-30T15:15:35.1823590Z    at com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder.parse(MethodTypeFlowBuilder.java:221)
2020-04-30T15:15:35.1823720Z    at com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder.apply(MethodTypeFlowBuilder.java:340)
2020-04-30T15:15:35.1823870Z    at com.oracle.graal.pointsto.flow.MethodTypeFlow.doParse(MethodTypeFlow.java:310)
2020-04-30T15:15:35.1824490Z    ... 11 more
2020-04-30T15:15:35.1824690Z Caused by: org.graalvm.compiler.debug.GraalError: java.lang.reflect.InvocationTargetException
2020-04-30T15:15:35.1824840Z    at jdk.internal.vm.compiler/org.graalvm.compiler.debug.GraalError.shouldNotReachHere(GraalError.java:55)
2020-04-30T15:15:35.1824960Z    at com.oracle.graal.pointsto.meta.AnalysisMethod.<init>(AnalysisMethod.java:149)
2020-04-30T15:15:35.1825080Z    at com.oracle.graal.pointsto.meta.AnalysisUniverse.createMethod(AnalysisUniverse.java:412)
2020-04-30T15:15:35.1825180Z    at com.oracle.graal.pointsto.meta.AnalysisUniverse.lookupAllowUnresolved(AnalysisUniverse.java:400)
2020-04-30T15:15:35.1825320Z    at com.oracle.graal.pointsto.infrastructure.WrappedConstantPool.lookupMethod(WrappedConstantPool.java:116)
2020-04-30T15:15:35.1825470Z    at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.lookupMethodInPool(BytecodeParser.java:4285)
2020-04-30T15:15:35.1825620Z    at com.oracle.svm.hosted.phases.SharedGraphBuilderPhase$SharedBytecodeParser.lookupMethodInPool(SharedGraphBuilderPhase.java:107)
2020-04-30T15:15:35.1825760Z    at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.lookupMethod(BytecodeParser.java:4279)
2020-04-30T15:15:35.1825870Z    at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.genInvokeStatic(BytecodeParser.java:1656)
2020-04-30T15:15:35.1826010Z    at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.processBytecode(BytecodeParser.java:5288)
2020-04-30T15:15:35.1826150Z    at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.iterateBytecodesForBlock(BytecodeParser.java:3397)
2020-04-30T15:15:35.1826270Z    ... 22 more
2020-04-30T15:15:35.1829890Z Caused by: java.lang.reflect.InvocationTargetException
2020-04-30T15:15:35.1833520Z    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
2020-04-30T15:15:35.1834000Z    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
2020-04-30T15:15:35.1835630Z    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
2020-04-30T15:15:35.1840050Z    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
2020-04-30T15:15:35.1840190Z    at com.oracle.graal.pointsto.meta.AnalysisMethod.<init>(AnalysisMethod.java:147)
2020-04-30T15:15:35.1840340Z    ... 31 more
2020-04-30T15:15:35.1852810Z Caused by: java.lang.ExceptionInInitializerError
2020-04-30T15:15:35.1853260Z    at org.eclipse.swt.internal.cocoa.Selector.<init>(Selector.java:1393)
2020-04-30T15:15:35.1855820Z    at org.eclipse.swt.internal.cocoa.Selector.<clinit>(Selector.java:17)
2020-04-30T15:15:35.1857310Z    at org.eclipse.swt.widgets.Display.$SWITCH_TABLE$org$eclipse$swt$internal$cocoa$Selector(Display.java:104)
2020-04-30T15:15:35.1857490Z    ... 36 more
2020-04-30T15:15:35.1857860Z Caused by: java.lang.NullPointerException
2020-04-30T15:15:35.1858510Z    at org.eclipse.swt.internal.cocoa.OS.<clinit>(OS.java:66)
2020-04-30T15:15:35.1859400Z    ... 39 more

Later errors are probably caused by this one, so I think they should be ignored until proved otherwise.

The issue boils down to a circular dependency between SWT's OS class and Selector class.

OS class has public static final fiels initialized with Selector.some_enum_value.someField. But Selector is an enum whose constructor invokes a public static method on OS which again wants to statically initialize OS.

This somehow appears to work when OS class is initialized first, and OS is what triggers initialization of Selector, but native-image appears to initialize Selector class without initializing OS class first which triggers the issue. I was able to show the same issue with a simple, small standlone example:

public enum EnumClass {
    VAL1("val1");

    public final String x;
    EnumClass(String param) {
        x = param;
        TestClass.takeEnum(this, param);
    }
}

public class TestClass {
    public static final String VAL = EnumClass.VAL1.x;

    private static java.util.Map<String, EnumClass> VALUES;
    public static void takeEnum(EnumClass e, String s) {
        if (VALUES == null) {
                VALUES = new java.util.HashMap<>();
        }
        VALUES.put(s, e);
    }
}

public class Main {
    public static void main(String... args) {
        System.out.println(TestClass.VAL);
    }
}

Now just if we compile and run it with java, it works just fine. It also works when it's compiled "naively" with native-image.

But in SWT case, the generated configuration causes initializing some classes at build time. In this example, TestClass is the equivalent of OS and EnumClass is the equivalent of Selector. If we force initialization of EnumClass at build time with

native-image.cmd -cp testjar.jar Main -H:+ReportExceptionStackTraces --initialize-at-build-time=EnumClass

we get the following stacktrace:

Error: Class initialization of EnumClass failed. Use the option --initialize-at-run-time=EnumClass to explicitly request delayed initialization of this class.
com.oracle.svm.core.util.UserError$UserException: Class initialization of EnumClass failed. Use the option --initialize-at-run-time=EnumClass to explicitly request delayed initialization of this class.
        at com.oracle.svm.core.util.UserError.abort(UserError.java:79)
        at com.oracle.svm.hosted.classinitialization.ConfigurableClassInitialization.reportInitializationError(ConfigurableClassInitialization.java:202)
        at com.oracle.svm.hosted.classinitialization.ConfigurableClassInitialization.ensureClassInitialized(ConfigurableClassInitialization.java:183)
        at com.oracle.svm.hosted.classinitialization.ConfigurableClassInitialization.forceInitializeHosted(ConfigurableClassInitialization.java:457)
        at com.oracle.svm.hosted.classinitialization.ConfigurableClassInitialization.initializeAtBuildTime(ConfigurableClassInitialization.java:385)
        at com.oracle.svm.hosted.classinitialization.ConfigurableClassInitialization.initializeAtBuildTime(ConfigurableClassInitialization.java:232)
        at com.oracle.svm.hosted.classinitialization.InitKind.lambda$stringConsumer$2(InitKind.java:74)
        at com.oracle.svm.hosted.classinitialization.ClassInitializationFeature.processClassInitializationOptions(ClassInitializationFeature.java:145)
        at com.oracle.svm.hosted.NativeImageGenerator.setupNativeImage(NativeImageGenerator.java:813)
        at com.oracle.svm.hosted.NativeImageGenerator.doRun(NativeImageGenerator.java:528)
        at com.oracle.svm.hosted.NativeImageGenerator.lambda$run$0(NativeImageGenerator.java:445)
        at java.base/java.util.concurrent.ForkJoinTask$AdaptedRunnableAction.exec(ForkJoinTask.java:1407)
        at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
        at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
        at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
        at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
        at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
Caused by: java.lang.ExceptionInInitializerError
        at EnumClass.<init>(EnumClass.java:7)
        at EnumClass.<clinit>(EnumClass.java:2)
        at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized0(Native Method)
        at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized(Unsafe.java:1042)
        at jdk.unsupported/sun.misc.Unsafe.ensureClassInitialized(Unsafe.java:698)
        at com.oracle.svm.hosted.classinitialization.ConfigurableClassInitialization.ensureClassInitialized(ConfigurableClassInitialization.java:167)
        ... 14 more
Caused by: java.lang.NullPointerException
        at TestClass.<clinit>(TestClass.java:2)
        ... 20 more

Which is exactly what the issue is in case of SWT, but in different stage of compilation (because I'm forcing it with a parameter, and in case of SWT it's indirectly forced by configuration)

I'm going to investigate further but this is what I found so far.

Barteks2x commented 4 years ago

The main difference that I can see in the bytecode, is that eclipse compiler generates a synthetic method that initializes tableswitch map, while javac compiles it to an entire synthetic class where the data is initialized statically.

With this in mind, I got a small self contained case that reproduces the issue:

// EnumClass.class
public enum EnumClass {
    VAL1("val1");

    public final String x;
    EnumClass(String param) {
        x = param;
        TestClass.takeEnum(this, param);
    }
    static EnumClass value(String s) {
        return TestClass.getEnum(s);
    }
}

// Main.class
import java.lang.reflect.*;

public class Main {
    public static void main(String... args) throws Throwable { // used reflection here to generate configuration that I then reused as "jni" config
        System.out.println(TestClass.getEnum("val1"));
        Method m = Main.class.getDeclaredMethod("doThing", String.class);
        System.out.println(m.invoke(null, "val1"));
    }

    static long doThing(String s) {
        switch (EnumClass.value(s)) {
            case VAL1: return 0;
            default: return -1;
        }
    }
}

// TestClass.class
public class TestClass {
    public static final String VAL = EnumClass.VAL1.x;

    private static java.util.Map<String, EnumClass> VALUES;
    public static void takeEnum(EnumClass e, String s) {
        if (VALUES == null) {
                VALUES = new java.util.HashMap<>();
        }
        VALUES.put(s, e);
    }
    public static EnumClass getEnum(String s) { return VALUES.get(s); }
}

Then I add this to jni config json file:

[
{
  "name":"Main",
  "methods":[{"name":"doThing","parameterTypes":["java.lang.String"] }]
}
]

If the code is compiled with javac, it works fine. If it's compiled with ecj, it fails with the following error:

[main:14188]    classlist:   2,402.71 ms,  1.00 GB
[main:14188]        (cap):   2,860.41 ms,  1.36 GB
[main:14188]        setup:   5,336.81 ms,  1.36 GB
[main:14188]     analysis:   3,630.49 ms,  1.36 GB
Fatal error: com.oracle.graal.pointsto.util.AnalysisError$ParsingError: Error encountered while parsing Main.doThing(java.lang.String)
Parsing context:
        parsing com.oracle.svm.reflect.Main_doThing_0a53730d28337e862e8ecdbb5a8053b0ef3e3d13_1.invoke(Unknown Source)
        parsing java.lang.reflect.Method.invoke(Method.java:566)
        parsing Main.main(Main.java:7)
        parsing com.oracle.svm.core.JavaMainWrapper.runCore(JavaMainWrapper.java:151)
        parsing com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:186)
        parsing com.oracle.svm.core.code.IsolateEnterStub.JavaMainWrapper_run_5087f5482cc9a6abc971913ece43acb471d2631b(generated:0)

        at com.oracle.graal.pointsto.util.AnalysisError.parsingError(AnalysisError.java:138)
        at com.oracle.graal.pointsto.flow.MethodTypeFlow.doParse(MethodTypeFlow.java:323)
        at com.oracle.graal.pointsto.flow.MethodTypeFlow.ensureParsed(MethodTypeFlow.java:300)
        at com.oracle.graal.pointsto.flow.MethodTypeFlow.addContext(MethodTypeFlow.java:107)
        at com.oracle.graal.pointsto.flow.StaticInvokeTypeFlow.update(InvokeTypeFlow.java:346)
        at com.oracle.graal.pointsto.BigBang$2.run(BigBang.java:511)
        at com.oracle.graal.pointsto.util.CompletionExecutor.lambda$execute$0(CompletionExecutor.java:171)
        at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1426)
        at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
        at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
        at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
        at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
        at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
Caused by: org.graalvm.compiler.java.BytecodeParser$BytecodeParserError: org.graalvm.compiler.debug.GraalError: java.lang.reflect.InvocationTargetException
        at parsing Main.doThing(Main.java:12)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.throwParserError(BytecodeParser.java:2582)
        at com.oracle.svm.hosted.phases.SharedGraphBuilderPhase$SharedBytecodeParser.throwParserError(SharedGraphBuilderPhase.java:94)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.iterateBytecodesForBlock(BytecodeParser.java:3402)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.processBlock(BytecodeParser.java:3204)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.build(BytecodeParser.java:1085)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.buildRootMethod(BytecodeParser.java:979)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.GraphBuilderPhase$Instance.run(GraphBuilderPhase.java:84)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.Phase.run(Phase.java:49)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.BasePhase.apply(BasePhase.java:197)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.Phase.apply(Phase.java:42)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.Phase.apply(Phase.java:38)
        at com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder.parse(MethodTypeFlowBuilder.java:221)
        at com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder.apply(MethodTypeFlowBuilder.java:340)
        at com.oracle.graal.pointsto.flow.MethodTypeFlow.doParse(MethodTypeFlow.java:310)
        ... 11 more
Caused by: org.graalvm.compiler.debug.GraalError: java.lang.reflect.InvocationTargetException
        at jdk.internal.vm.compiler/org.graalvm.compiler.debug.GraalError.shouldNotReachHere(GraalError.java:55)
        at com.oracle.graal.pointsto.meta.AnalysisMethod.<init>(AnalysisMethod.java:149)
        at com.oracle.graal.pointsto.meta.AnalysisUniverse.createMethod(AnalysisUniverse.java:412)
        at com.oracle.graal.pointsto.meta.AnalysisUniverse.lookupAllowUnresolved(AnalysisUniverse.java:400)
        at com.oracle.graal.pointsto.infrastructure.WrappedConstantPool.lookupMethod(WrappedConstantPool.java:116)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.lookupMethodInPool(BytecodeParser.java:4285)
        at com.oracle.svm.hosted.phases.SharedGraphBuilderPhase$SharedBytecodeParser.lookupMethodInPool(SharedGraphBuilderPhase.java:107)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.lookupMethod(BytecodeParser.java:4279)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.genInvokeStatic(BytecodeParser.java:1656)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.processBytecode(BytecodeParser.java:5288)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.iterateBytecodesForBlock(BytecodeParser.java:3397)
        ... 22 more
Caused by: java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at com.oracle.graal.pointsto.meta.AnalysisMethod.<init>(AnalysisMethod.java:147)
        ... 31 more
Caused by: java.lang.ExceptionInInitializerError
        at EnumClass.<init>(EnumClass.java:7)
        at EnumClass.<clinit>(EnumClass.java:2)
        at Main.$SWITCH_TABLE$EnumClass(Main.java:3)
        ... 36 more
Caused by: java.lang.NullPointerException
        at TestClass.<clinit>(TestClass.java:2)
        ... 39 more
Error: Image build request failed with exit status 1

For completeness, here is bytecode comparison: ecj: https://gist.github.com/Barteks2x/ba88cbcfb5f162d585f3312f556f1abd javac: https://gist.github.com/Barteks2x/1dcf3c7d279c76cf20ff966fff30a8a7 diff -u -r src_javac src_ecj https://gist.github.com/Barteks2x/9e1c8269d093de188b73bde0e225c8ee

christianwimmer commented 4 years ago

I think @vjovanov is currently working on a fix for this

oubidar-Abderrahim commented 4 years ago

@christianwimmer FYI: There is an Internal ticket (GR-24133) for this issue

igortat commented 1 year ago

Any progress with this issue?