1

I started a new Kotlin project and i want to use the arrow-kt core Lib in combination with Quarkus (1.12.2). I want to use the native compilation feature of Quarkus with the GraalVM. My first thought was that arrow is a simple lib without reflection but then i read that. Since GraalVm has a problem with reflection in native executables at runtime, will that be a problem with Arrow? If it is a problem, can i bypass the problem by simply avoiding some features of Arrow?

I know that i can mark classes for reflection in Quarkus/GraalVM. Which classes are inspected by reflection? Can i simply add reflection information for a few classes or do i need to that for the whole lib or my whole code?

Alex
  • 80
  • 6

2 Answers2

3

Starting in 0.12.0, which is about to release, Arrow does not use reflection. Previously it did in monad comprehensions for all inheritors of MonadContinuation in their bind operation accessing the ContinuationUtils class. In this class, we used reflection to read and write private fields related to the continuation stack labels.

3

As another answer states a newer release might not use reflection, making the question about the particular library not that important. However, for completeness, here are some answers to these questions in general.

Since GraalVm has a problem with reflection in native executables at runtime, will that be a problem with Arrow?

GraalVM native image uses static analysis during building the executable out of your program. This means that dynamic features of the langauge require explicit configuration to help the analysis to include the necessary classes / methods into the binary. For example, static analysis cannot predict which classes will be accessed through reflection or proxied when these are referenced through strings only which can sometimes are constructed only at runtime.

Can i simply add reflection information for a few classes or do i need to that for the whole lib or my whole code?

You do need to configure all the accesses through the reflection API. The libraries can provide the config for their use of reflection, resources, etc. But if they need refletive access to your application classes then they cannot do that.

The configuration required is in the form of json files, for example a reflection configuration to include a class might look like:

[
  {
    "name" : "java.lang.String",
    "fields" : [
      { "name" : "value", "allowWrite" : true },
      { "name" : "hash" }
    ],
    "methods" : [
      { "name" : "<init>", "parameterTypes" : [] },
      { "name" : "<init>", "parameterTypes" : ["char[]"] },
      { "name" : "charAt" },
      { "name" : "format", "parameterTypes" : ["java.lang.String", "java.lang.Object[]"] }
    ]
  }
]

The example above specifies that the program would like to be able to use java.lang.String reflectively, have access to the fields value and hash and the methods listed.

It might be a bit tedious, however rather straightforward to create config like that. Some frameworks help you by providing annotations to mark classes with and then generate the config themselves.

But if you want to create the config for the library that you don't know, so it's hard to manually create the config, you can use and it's recommended to use the assisted configuration agent.

This means you execute your program enabling a javaagent, which will trace and write down config for all necessary features: resource access, serialization/deserialization, proxies, JNI, reflection, etc.

So you run the application like this and execute the codepaths you're interested in (maybe through your tests) and the output dir will contain the config.

java -agentlib:native-image-agent=config-output-dir=/path/to/config-dir/ -jar myjar.jar

You can then edit the config if needed manually to, for example, extrapolate to the code paths you didn't run with the tracing agent.

Then you run the native image build process passing the config options, for example, for the reflection file config specify: -H:ReflectionConfigurationFiles=/path/to/reflectconfig.

You can also use the fact that META-INF/native-image directory is the default location for the configuration files, so you don't have to specify the options. For example if you generate the config in the config/META-INF/native-image directory, then you can place it on the classpath for the native image and the files will be picked up automatically:

native-image -cp config -jar myjar.jar
Oleg Šelajev
  • 3,530
  • 1
  • 17
  • 25