This library offers a fix for an issue with clojure.lang.Reflector
when used
together with GraalVM native-image
on java11.
Include this library in your leiningen profile or deps.edn alias so it's
available on the classpath for GraalVM native-image
.
Important:
graalvm-20.2.0
. The modifier must exactly match the version of GraalVM
native-image
you are using.Relevant config for project.cljc
:
(defproject foo "0.0.1-SNAPSHOT"
:profiles {:native-image {:dependencies [[borkdude/clj-reflector-graal-java11-fix "0.0.1-graalvm-20.2.0"]]}})
To produce an uberjar that is fed to native-image
you can:
$ lein with-profiles +native-image do clean, uberjar
and then:
$ native-image -jar target/foo-0.0.1-SNAPSHOT-standalone.jar
Relevant config for deps.edn
:
{:aliases
{:native-image {:extra-deps {borkdude/clj-reflector-graal-java11-fix
{:mvn/version "0.0.1-graalvm-20.2.0"
:exclusions [org.graalvm.nativeimage/svm]}}}}}
Notice the exclusion? This project depends on native dep org.graalvm.nativeimage/svm
.
While technically correct, tools deps does not support native deps and fails for this native dep.
Since the native dep is provided by the Graal installation anyway, it can be safely excluded for tools deps.
Your compile script would get the classspath for native-image
via:
clojure -A:native-image -Spath
JDK11 is supported since GraalVM 19.3.0. The class clojure.lang.Reflector
uses
a MethodHandle
to maintain compatibility with java8 and java11 at the same
time:
static {
MethodHandle pred = null;
try {
if (! isJava8())
pred = MethodHandles.lookup().findVirtual(Method.class, "canAccess", MethodType.methodType(boolean.class, Object.class));
} catch (Throwable t) {
Util.sneakyThrow(t);
}
CAN_ACCESS_PRED = pred;
}
GraalVM does not support MethodHandle
s that cannot be analyzed as a compile
time constant. It will complain when it analyzes clojure.lang.Reflector
on
JDK11:
Exception in thread "main" com.oracle.svm.core.jdk.UnsupportedFeatureError: Invoke with MethodHandle argument could not be reduced to at most a single call or single field access. The method handle must be a compile time constant, e.g., be loaded from a `static final` field. Method that contains the method handle invocation: java.lang.invoke.Invokers$Holder.invoke_MT(Object, Object, Object, Object)
at com.oracle.svm.core.util.VMError.unsupportedFeature(VMError.java:101)
at clojure.lang.Reflector.canAccess(Reflector.java:49)
...
See the issue on the GraalVM repo.
GraalVM supports substitutions. For JDK11 or later the method canAccess
is replaced as follows:
@TargetClass(className = "clojure.lang.Reflector")
final class Target_clojure_lang_Reflector {
@Substitute
@TargetElement(onlyWith = JDK11OrLater.class)
private static boolean canAccess(Method m, Object target) {
// JDK9+ use j.l.r.AccessibleObject::canAccess, which respects module rules
try {
return (boolean) m.canAccess(target);
} catch (Throwable t) {
throw Util.sneakyThrow(t);
}
}
}
Copyright © 2020 Michiel Borkent
Distributed under the EPL License. See LICENSE.
This project contains code from:
Can you improve this documentation? These fine people already did:
Michiel Borkent & Lee ReadEdit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close