Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move native JNI configuration from Feature to json #31179

Merged
merged 1 commit into from
Feb 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
Comment on lines -74 to -76
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that this was useful only for GraalVM < 22.3, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
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 @@ -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";
Expand Down Expand Up @@ -124,8 +120,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 @@ -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());
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();
}
}

}