Uploaded image for project: 'Commons BCEL'
  1. Commons BCEL
  2. BCEL-373

Add support for GraalVM

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Open
    • Major
    • Resolution: Unresolved
    • 6.10.0
    • None
    • Main
    • None

    Description

      I'm working on a small tool called "jarbloat" (written in Clojure but should work on any JVM language that produces jar files) [1] for inspecting JAR files. I'm using BCEL to analyze class dependencies and package names. Although I have alternative code for computing package names based on paths, sadly, I don't have any alternative for fetching class dependencies outside of BCEL.

      Now, jarbloat works fine when compiled usual way (uberjar/fatjar), but when I compile it with GraalVM, BCEL code will throw NullPointerException:

      java.lang.ExceptionInInitializerError
      	at org.apache.bcel.util.SyntheticRepository.getInstance(SyntheticRepository.java:38)
      	at org.apache.bcel.classfile.JavaClass.<init>(JavaClass.java:145)
      	at org.apache.bcel.classfile.ClassParser.parse(ClassParser.java:179)
      	at jarbloat.class_analyzer.BCELAnalyzer.load_class(class_analyzer.clj:64)
      	at jarbloat.analyzer$analyze_entry_deps.invokeStatic(analyzer.clj:128)
      	at jarbloat.analyzer$analyze_entry_deps.invoke(analyzer.clj:118)
      	at jarbloat.analyzer$analyze_jar$fn__706.invoke(analyzer.clj:156)
      	at clojure.core$map$fn__4785.invoke(core.clj:2646)
      	at clojure.lang.LazySeq.sval(LazySeq.java:40)
      	at clojure.lang.LazySeq.seq(LazySeq.java:49)
      	at clojure.lang.RT.seq(RT.java:521)
      	at clojure.core$seq__4357.invokeStatic(core.clj:137)
      	at clojure.core$filter$fn__4812.invoke(core.clj:2700)
      	at clojure.lang.LazySeq.sval(LazySeq.java:40)
      	at clojure.lang.LazySeq.seq(LazySeq.java:56)
      	at clojure.lang.RT.seq(RT.java:521)
      	at clojure.core$seq__4357.invokeStatic(core.clj:137)
      	at jarbloat.printer$do_print_dot.invokeStatic(printer.clj:13)
      	at jarbloat.analyzer$analyze_jar.invokeStatic(analyzer.clj:160)
      	at jarbloat.core$handle_args.invokeStatic(core.clj:91)
      	at jarbloat.core$_main.invokeStatic(core.clj:105)
      	at jarbloat.core$_main.doInvoke(core.clj:99)
      	at clojure.lang.RestFn.applyTo(RestFn.java:137)
      	at jarbloat.core.main(Unknown Source)
      Caused by: java.lang.NullPointerException
      	at java.base@17.0.12/java.util.Objects.requireNonNull(Objects.java:208)
      	at java.base@17.0.12/sun.nio.fs.UnixFileSystem.getPath(UnixFileSystem.java:263)
      	at java.base@17.0.12/java.nio.file.Path.of(Path.java:147)
      	at java.base@17.0.12/java.nio.file.Paths.get(Paths.java:69)
      	at org.apache.bcel.util.ClassPath.getClassPath(ClassPath.java:481)
      	at org.apache.bcel.util.ClassPath.<clinit>(ClassPath.java:443)
      

      This happens when I set GraalVM's native-image to use --initialize-at-run-time=org.apache.bcel.util.ClassPath argument. Without it, GraalVM will complain with the following error:

      Error: Detected a ZipFile object in the image heap. A ZipFile object contains pointers to unmanaged C memory and file descriptors, and these resources are no longer available at image runtime.  To see how this object got instantiated use --trace-object-instantiation=java.util.zip.ZipFile. The object was probably created by a class initializer and is reachable from a static field. You can request class initialization at image runtime by using the option --initialize-at-run-time=<class-name>. Or you can write your own initialization methods and call them explicitly from your main entry point.
      Trace: Object was reached by
        reading field org.apache.bcel.util.ClassPath$AbstractZip.zipFile of constant 
          org.apache.bcel.util.ClassPath$Module@3956260a: /opt/graalvm-jdk-17.0.12+8.1/jmods/java.se.jmod
        indexing into array java.lang.Object[]@761be02f: [Ljava.lang.Object;@761be02f
        reading field java.util.ArrayList.elementData of constant 
          java.util.ArrayList@3eb61e52: [[/modules/jdk.internal.vm.compiler, /modules/com.oracle.graal.graal_enterprise,...
        reading field org.apache.bcel.util.ClassPath.paths of constant 
          org.apache.bcel.util.ClassPath@2f5aa92f: /opt/graalvm-jdk-17.0.12+8.1/lib/modules:/opt/graalvm-jdk-17.0.12+8.1/jmods/java...
        scanning root org.apache.bcel.util.ClassPath@2f5aa92f: /opt/graalvm-jdk-17.0.12+8.1/lib/modules:/opt/graalvm-jdk-17.0.12+8.1/jmods/java... embedded in 
          org.apache.bcel.util.SyntheticRepository.getInstance(SyntheticRepository.java:38)
        parsing method org.apache.bcel.util.SyntheticRepository.getInstance(SyntheticRepository.java:38) reachable via the parsing context
          at static root method.(Unknown Source)
      

      After some digging through the code, I found that BCEL has some variables initialized when the class is initialized (e.g. [2], [3], and [4]). I'm getting the impression that the problem starts in ClassPath.getClassPath() [5]. Some values, like SystemProperties.getJavaHome() [6], will be null on GraalVM.

      I'm not very familiar with BCEL internals, but after some rough checking things, maybe making this [2] repository initialization lazy could solve and allowing repository to be null, could solve the issue. Any thoughts?

      Tested with these GraalVM native-image versions:

      $ /opt/graalvm/bin/native-image --version
      GraalVM 22.0.0.2 Java 17 CE (Java Version 17.0.2+8-jvmci-22.0-b05)
      
      $ /opt/graalvm-jdk-17.0.12+8.1/bin/native-image --version
      native-image 17.0.12 2024-07-16
      GraalVM Runtime Environment Oracle GraalVM 17.0.12+8.1 (build 17.0.12+8-LTS-jvmci-23.0-b41)
      Substrate VM Oracle GraalVM 17.0.12+8.1 (build 17.0.12+8-LTS, serial gc, compressed references)
      

      [1] https://github.com/sanel/jarbloat
      [2] https://github.com/apache/commons-bcel/blob/master/src/main/java/org/apache/bcel/classfile/JavaClass.java#L145
      [3] https://github.com/apache/commons-bcel/blob/master/src/main/java/org/apache/bcel/util/SyntheticRepository.java#L38
      [4] https://github.com/apache/commons-bcel/blob/master/src/main/java/org/apache/bcel/util/ClassPath.java#L443
      [5] https://github.com/apache/commons-bcel/blob/master/src/main/java/org/apache/bcel/util/ClassPath.java#L470
      [6] https://github.com/apache/commons-bcel/blob/master/src/main/java/org/apache/bcel/util/ClassPath.java#L481

      Attachments

        Activity

          People

            Unassigned Unassigned
            sanelz Sanel Zukan
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated: