From 9937825d4ebbb42170c67c94471ed4d6a4901b0a Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Wed, 15 Feb 2023 11:35:24 +0200 Subject: [PATCH] Move native JNI configuration from Feature to json Follow up to #29886 --- .../JniRuntimeAccessBuildItem.java | 18 +--- .../steps/NativeImageFeatureStep.java | 83 +---------------- .../steps/NativeImageJNIConfigStep.java | 90 +++++++++++++++++++ 3 files changed, 93 insertions(+), 98 deletions(-) create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageJNIConfigStep.java diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/JniRuntimeAccessBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/JniRuntimeAccessBuildItem.java index 189e1e6068908..08854f53231f9 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/JniRuntimeAccessBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/JniRuntimeAccessBuildItem.java @@ -15,17 +15,8 @@ public final class JniRuntimeAccessBuildItem extends MultiBuildItem { private final boolean constructors; private final boolean methods; private final boolean fields; - private final boolean finalFieldsWriteable; - public JniRuntimeAccessBuildItem(boolean constructors, boolean methods, boolean fields, Class... classes) { - this(constructors, methods, fields, false, classes); - } - - public JniRuntimeAccessBuildItem(boolean constructors, boolean methods, boolean fields, String... className) { - this(constructors, methods, fields, false, className); - } - - public JniRuntimeAccessBuildItem(boolean constructors, boolean methods, boolean fields, boolean finalFieldsWriteable, + public JniRuntimeAccessBuildItem(boolean constructors, boolean methods, boolean fields, Class... classes) { List names = new ArrayList<>(); for (Class i : classes) { @@ -38,10 +29,9 @@ public JniRuntimeAccessBuildItem(boolean constructors, boolean methods, boolean this.constructors = constructors; this.methods = methods; this.fields = fields; - this.finalFieldsWriteable = finalFieldsWriteable; } - public JniRuntimeAccessBuildItem(boolean constructors, boolean methods, boolean fields, boolean finalFieldsWriteable, + public JniRuntimeAccessBuildItem(boolean constructors, boolean methods, boolean fields, String... className) { for (String i : className) { if (i == null) { @@ -52,7 +42,6 @@ public JniRuntimeAccessBuildItem(boolean constructors, boolean methods, boolean this.constructors = constructors; this.methods = methods; this.fields = fields; - this.finalFieldsWriteable = finalFieldsWriteable; } public List getClassNames() { @@ -71,7 +60,4 @@ public boolean isFields() { return fields; } - public boolean isFinalFieldsWriteable() { - return finalFieldsWriteable; - } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java index 63d0a21cc3c57..b6d7903299a9d 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageFeatureStep.java @@ -2,8 +2,6 @@ import static io.quarkus.gizmo.MethodDescriptor.ofMethod; -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -78,8 +76,6 @@ public class NativeImageFeatureStep { "findModule", Module.class, String.class); private static final MethodDescriptor INVOKE = ofMethod( Method.class, "invoke", Object.class, Object.class, Object[].class); - static final String LEGACY_JNI_RUNTIME_ACCESS = "com.oracle.svm.core.jni.JNIRuntimeAccess"; - static final String JNI_RUNTIME_ACCESS = "org.graalvm.nativeimage.hosted.RuntimeJNIAccess"; static final String BEFORE_ANALYSIS_ACCESS = Feature.BeforeAnalysisAccess.class.getName(); static final String DYNAMIC_PROXY_REGISTRY = "com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry"; static final String LOCALIZATION_FEATURE = "com.oracle.svm.core.jdk.localization.LocalizationFeature"; @@ -124,8 +120,7 @@ void generateFeature(BuildProducer nativeIma List resourcePatterns, List resourceBundles, List serviceProviderBuildItems, - List unsafeAccessedFields, - List jniRuntimeAccessibleClasses) { + List unsafeAccessedFields) { ClassCreator file = new ClassCreator(new ClassOutput() { @Override public void write(String s, byte[] bytes) { @@ -437,82 +432,6 @@ public void write(String s, byte[] bytes) { registerResourceBundles.returnVoid(); overallCatch.invokeStaticMethod(registerResourceBundles.getMethodDescriptor()); } - int count = 0; - - for (JniRuntimeAccessBuildItem jniAccessible : jniRuntimeAccessibleClasses) { - for (String className : jniAccessible.getClassNames()) { - MethodCreator mv = file.getMethodCreator("registerJniAccessibleClass" + count++, "V"); - mv.setModifiers(Modifier.PRIVATE | Modifier.STATIC); - overallCatch.invokeStaticMethod(mv.getMethodDescriptor()); - - TryBlock tc = mv.tryBlock(); - - ResultHandle clazz = tc.loadClassFromTCCL(className); - //we call these methods first, so if they are going to throw an exception it happens before anything has been registered - ResultHandle constructors = tc - .invokeVirtualMethod(ofMethod(Class.class, "getDeclaredConstructors", Constructor[].class), clazz); - ResultHandle methods = tc.invokeVirtualMethod(ofMethod(Class.class, "getDeclaredMethods", Method[].class), - clazz); - ResultHandle fields = tc.invokeVirtualMethod(ofMethod(Class.class, "getDeclaredFields", Field[].class), clazz); - - ResultHandle carray = tc.newArray(Class.class, tc.load(1)); - tc.writeArrayValue(carray, 0, clazz); - - BranchResult graalVm22_3Test = tc.ifGreaterEqualZero(tc.invokeVirtualMethod(VERSION_COMPARE_TO, - tc.invokeStaticMethod(VERSION_CURRENT), tc.marshalAsArray(int.class, tc.load(22), tc.load(3)))); - /* GraalVM >= 22.3 */ - try (BytecodeCreator greaterThan22_2 = graalVm22_3Test.trueBranch()) { - greaterThan22_2.invokeStaticMethod(ofMethod(JNI_RUNTIME_ACCESS, "register", void.class, Class[].class), - carray); - - if (jniAccessible.isConstructors()) { - greaterThan22_2.invokeStaticMethod( - ofMethod(JNI_RUNTIME_ACCESS, "register", void.class, Executable[].class), - constructors); - } - - if (jniAccessible.isMethods()) { - greaterThan22_2.invokeStaticMethod( - ofMethod(JNI_RUNTIME_ACCESS, "register", void.class, Executable[].class), - methods); - } - - if (jniAccessible.isFields()) { - greaterThan22_2.invokeStaticMethod( - ofMethod(JNI_RUNTIME_ACCESS, "register", void.class, Field[].class), - fields); - } - } - /* GraalVM < 22.3 */ - try (BytecodeCreator smallerThan22_3 = graalVm22_3Test.falseBranch()) { - smallerThan22_3.invokeStaticMethod( - ofMethod(LEGACY_JNI_RUNTIME_ACCESS, "register", void.class, Class[].class), - carray); - - if (jniAccessible.isConstructors()) { - smallerThan22_3.invokeStaticMethod( - ofMethod(LEGACY_JNI_RUNTIME_ACCESS, "register", void.class, Executable[].class), - constructors); - } - - if (jniAccessible.isMethods()) { - smallerThan22_3.invokeStaticMethod( - ofMethod(LEGACY_JNI_RUNTIME_ACCESS, "register", void.class, Executable[].class), - methods); - } - - if (jniAccessible.isFields()) { - smallerThan22_3.invokeStaticMethod( - ofMethod(LEGACY_JNI_RUNTIME_ACCESS, "register", void.class, boolean.class, Field[].class), - smallerThan22_3.load(jniAccessible.isFinalFieldsWriteable()), fields); - } - } - - CatchBlockCreator cc = tc.addCatch(Throwable.class); - //cc.invokeVirtualMethod(ofMethod(Throwable.class, "printStackTrace", void.class), cc.getCaughtException()); - mv.returnValue(null); - } - } CatchBlockCreator print = overallCatch.addCatch(Throwable.class); print.invokeVirtualMethod(ofMethod(Throwable.class, "printStackTrace", void.class), print.getCaughtException()); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageJNIConfigStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageJNIConfigStep.java new file mode 100644 index 0000000000000..883b9218801dd --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/NativeImageJNIConfigStep.java @@ -0,0 +1,90 @@ +package io.quarkus.deployment.steps; + +import java.io.IOException; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import io.quarkus.builder.Json; +import io.quarkus.builder.Json.JsonArrayBuilder; +import io.quarkus.builder.Json.JsonObjectBuilder; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.GeneratedResourceBuildItem; +import io.quarkus.deployment.builditem.nativeimage.JniRuntimeAccessBuildItem; +import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild; + +public class NativeImageJNIConfigStep { + + @BuildStep(onlyIf = NativeOrNativeSourcesBuild.class) + void generateJniConfig(BuildProducer jniConfig, + List jniRuntimeAccessibleClasses) { + final Map jniClasses = new LinkedHashMap<>(); + + for (JniRuntimeAccessBuildItem jniAccessible : jniRuntimeAccessibleClasses) { + addJniClass(jniClasses, jniAccessible); + } + + JsonArrayBuilder root = Json.array(); + for (Map.Entry entry : jniClasses.entrySet()) { + JsonObjectBuilder json = Json.object(); + + json.put("name", entry.getKey()); + + JniInfo info = entry.getValue(); + if (info.constructors) { + json.put("allDeclaredConstructors", true); + } + if (info.methods) { + json.put("allDeclaredMethods", true); + } + if (info.fields) { + json.put("allDeclaredFields", true); + } + + root.add(json); + } + + try (StringWriter writer = new StringWriter()) { + root.appendTo(writer); + jniConfig.produce(new GeneratedResourceBuildItem("META-INF/native-image/jni-config.json", + writer.toString().getBytes(StandardCharsets.UTF_8))); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void addJniClass(Map jniClasses, JniRuntimeAccessBuildItem jniRuntimeAccessBuildItem) { + for (String cl : jniRuntimeAccessBuildItem.getClassNames()) { + JniInfo existing = jniClasses.get(cl); + if (existing == null) { + jniClasses.put(cl, new JniInfo(jniRuntimeAccessBuildItem)); + } else { + if (jniRuntimeAccessBuildItem.isConstructors()) { + existing.constructors = true; + } + if (jniRuntimeAccessBuildItem.isMethods()) { + existing.methods = true; + } + if (jniRuntimeAccessBuildItem.isFields()) { + existing.fields = true; + } + } + } + } + + static final class JniInfo { + boolean constructors; + boolean methods; + boolean fields; + + private JniInfo(JniRuntimeAccessBuildItem jniRuntimeAccessBuildItem) { + this.methods = jniRuntimeAccessBuildItem.isMethods(); + this.fields = jniRuntimeAccessBuildItem.isFields(); + this.constructors = jniRuntimeAccessBuildItem.isConstructors(); + } + } + +}