Skip to content

Commit

Permalink
Merge pull request #31179 from zakkak/2023-02-15-jni-config-json
Browse files Browse the repository at this point in the history
Move native JNI configuration from Feature to json
  • Loading branch information
gsmet authored Feb 17, 2023
2 parents 35d2750 + 9937825 commit 7abe8ae
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> names = new ArrayList<>();
for (Class<?> i : classes) {
Expand All @@ -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) {
Expand All @@ -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<String> getClassNames() {
Expand All @@ -71,7 +60,4 @@ public boolean isFields() {
return fields;
}

public boolean isFinalFieldsWriteable() {
return finalFieldsWriteable;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -77,8 +75,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 LOCALIZATION_FEATURE = "com.oracle.svm.core.jdk.localization.LocalizationFeature";
static final String RUNTIME_RESOURCE_SUPPORT = "org.graalvm.nativeimage.impl.RuntimeResourceSupport";
Expand Down Expand Up @@ -121,8 +117,7 @@ void generateFeature(BuildProducer<GeneratedNativeImageClassBuildItem> nativeIma
List<NativeImageResourcePatternsBuildItem> resourcePatterns,
List<NativeImageResourceBundleBuildItem> resourceBundles,
List<ServiceProviderBuildItem> serviceProviderBuildItems,
List<UnsafeAccessedFieldBuildItem> unsafeAccessedFields,
List<JniRuntimeAccessBuildItem> jniRuntimeAccessibleClasses) {
List<UnsafeAccessedFieldBuildItem> unsafeAccessedFields) {
ClassCreator file = new ClassCreator(new ClassOutput() {
@Override
public void write(String s, byte[] bytes) {
Expand Down Expand Up @@ -387,82 +382,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());
Expand Down
Original file line number Diff line number Diff line change
@@ -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<GeneratedResourceBuildItem> jniConfig,
List<JniRuntimeAccessBuildItem> jniRuntimeAccessibleClasses) {
final Map<String, JniInfo> jniClasses = new LinkedHashMap<>();

for (JniRuntimeAccessBuildItem jniAccessible : jniRuntimeAccessibleClasses) {
addJniClass(jniClasses, jniAccessible);
}

JsonArrayBuilder root = Json.array();
for (Map.Entry<String, JniInfo> 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<String, JniInfo> 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();
}
}

}

0 comments on commit 7abe8ae

Please sign in to comment.