Skip to content

Commit

Permalink
New weak reflection implementation
Browse files Browse the repository at this point in the history
This implementation uses a reachability handler to only register the
classes if they are reachable.
  • Loading branch information
stuartwdouglas committed Jul 30, 2021
1 parent 972f178 commit 758e8cb
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ private ReflectiveClassBuildItem(boolean constructors, boolean methods, boolean
this.finalFieldsWritable = finalFieldsWritable;
this.weak = weak;
this.serialization = serialization;
if (weak) {
if (serialization) {
throw new RuntimeException("Weak reflection not supported with serialization");
}
if (finalFieldsWritable) {
throw new RuntimeException("Weak reflection not supported with finalFieldsWritable");
}
}
}

public ReflectiveClassBuildItem(boolean methods, boolean fields, String... className) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import io.quarkus.gizmo.TryBlock;
import io.quarkus.runtime.ResourceHelper;
import io.quarkus.runtime.graal.ResourcesFeature;
import io.quarkus.runtime.graal.WeakReflection;

public class NativeImageAutoFeatureStep {

Expand Down Expand Up @@ -83,6 +84,9 @@ public class NativeImageAutoFeatureStep {
static final String DYNAMIC_PROXY_REGISTRY = "com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry";
static final String LEGACY_LOCALIZATION_FEATURE = "com.oracle.svm.core.jdk.LocalizationFeature";
static final String LOCALIZATION_FEATURE = "com.oracle.svm.core.jdk.localization.LocalizationFeature";
public static final MethodDescriptor WEAK_REFLECTION_REGISTRATION = MethodDescriptor.ofMethod(WeakReflection.class,
"register", void.class, Feature.BeforeAnalysisAccess.class, Class.class, boolean.class, boolean.class,
boolean.class);

@BuildStep
GeneratedResourceBuildItem generateNativeResourcesList(List<NativeImageResourceBuildItem> resources,
Expand Down Expand Up @@ -314,9 +318,9 @@ public void write(String s, byte[] bytes) {
MethodDescriptor registerSerializationMethod = null;

for (Map.Entry<String, ReflectionInfo> entry : reflectiveClasses.entrySet()) {
MethodCreator mv = file.getMethodCreator("registerClass" + count++, "V");
MethodCreator mv = file.getMethodCreator("registerClass" + count++, void.class, Feature.BeforeAnalysisAccess.class);
mv.setModifiers(Modifier.PRIVATE | Modifier.STATIC);
overallCatch.invokeStaticMethod(mv.getMethodDescriptor());
overallCatch.invokeStaticMethod(mv.getMethodDescriptor(), overallCatch.getMethodParam(0));

TryBlock tc = mv.tryBlock();

Expand All @@ -339,73 +343,77 @@ public void write(String s, byte[] bytes) {
tc.writeArrayValue(carray, 0, clazz);
tc.invokeStaticMethod(ofMethod(RUNTIME_REFLECTION, "register", void.class, Class[].class),
carray);
}

if (entry.getValue().constructors) {
tc.invokeStaticMethod(
ofMethod(RUNTIME_REFLECTION, "register", void.class, Executable[].class),
constructors);
} else if (!entry.getValue().ctorSet.isEmpty()) {
ResultHandle farray = tc.newArray(Constructor.class, tc.load(1));
for (ReflectiveMethodBuildItem ctor : entry.getValue().ctorSet) {
ResultHandle paramArray = tc.newArray(Class.class, tc.load(ctor.getParams().length));
for (int i = 0; i < ctor.getParams().length; ++i) {
String type = ctor.getParams()[i];
tc.writeArrayValue(paramArray, i, tc.loadClass(type));
}
ResultHandle fhandle = tc.invokeVirtualMethod(
ofMethod(Class.class, "getDeclaredConstructor", Constructor.class, Class[].class), clazz,
paramArray);
tc.writeArrayValue(farray, 0, fhandle);
if (entry.getValue().constructors) {
tc.invokeStaticMethod(
ofMethod(RUNTIME_REFLECTION, "register", void.class, Executable[].class),
farray);
}
}
if (entry.getValue().methods) {
tc.invokeStaticMethod(
ofMethod(RUNTIME_REFLECTION, "register", void.class, Executable[].class),
methods);
} else if (!entry.getValue().methodSet.isEmpty()) {
ResultHandle farray = tc.newArray(Method.class, tc.load(1));
for (ReflectiveMethodBuildItem method : entry.getValue().methodSet) {
ResultHandle paramArray = tc.newArray(Class.class, tc.load(method.getParams().length));
for (int i = 0; i < method.getParams().length; ++i) {
String type = method.getParams()[i];
tc.writeArrayValue(paramArray, i, tc.loadClass(type));
constructors);
} else if (!entry.getValue().ctorSet.isEmpty()) {
ResultHandle farray = tc.newArray(Constructor.class, tc.load(1));
for (ReflectiveMethodBuildItem ctor : entry.getValue().ctorSet) {
ResultHandle paramArray = tc.newArray(Class.class, tc.load(ctor.getParams().length));
for (int i = 0; i < ctor.getParams().length; ++i) {
String type = ctor.getParams()[i];
tc.writeArrayValue(paramArray, i, tc.loadClass(type));
}
ResultHandle fhandle = tc.invokeVirtualMethod(
ofMethod(Class.class, "getDeclaredConstructor", Constructor.class, Class[].class), clazz,
paramArray);
tc.writeArrayValue(farray, 0, fhandle);
tc.invokeStaticMethod(
ofMethod(RUNTIME_REFLECTION, "register", void.class, Executable[].class),
farray);
}
ResultHandle fhandle = tc.invokeVirtualMethod(
ofMethod(Class.class, "getDeclaredMethod", Method.class, String.class, Class[].class), clazz,
tc.load(method.getName()), paramArray);
tc.writeArrayValue(farray, 0, fhandle);
}
if (entry.getValue().methods) {
tc.invokeStaticMethod(
ofMethod(RUNTIME_REFLECTION, "register", void.class, Executable[].class),
farray);
methods);
} else if (!entry.getValue().methodSet.isEmpty()) {
ResultHandle farray = tc.newArray(Method.class, tc.load(1));
for (ReflectiveMethodBuildItem method : entry.getValue().methodSet) {
ResultHandle paramArray = tc.newArray(Class.class, tc.load(method.getParams().length));
for (int i = 0; i < method.getParams().length; ++i) {
String type = method.getParams()[i];
tc.writeArrayValue(paramArray, i, tc.loadClass(type));
}
ResultHandle fhandle = tc.invokeVirtualMethod(
ofMethod(Class.class, "getDeclaredMethod", Method.class, String.class, Class[].class), clazz,
tc.load(method.getName()), paramArray);
tc.writeArrayValue(farray, 0, fhandle);
tc.invokeStaticMethod(
ofMethod(RUNTIME_REFLECTION, "register", void.class, Executable[].class),
farray);
}
}
}
if (entry.getValue().fields) {
BranchResult graalVm21Test = tc.ifGreaterEqualZero(
tc.invokeVirtualMethod(VERSION_COMPARE_TO,
tc.invokeStaticMethod(VERSION_CURRENT),
tc.marshalAsArray(int.class, tc.load(21))));
graalVm21Test.trueBranch().invokeStaticMethod(
ofMethod(RUNTIME_REFLECTION, "register", void.class,
boolean.class, boolean.class, Field[].class),
tc.load(entry.getValue().finalFieldsWritable), tc.load(entry.getValue().serialization), fields);
graalVm21Test.falseBranch().invokeStaticMethod(
ofMethod(RUNTIME_REFLECTION, "register", void.class,
boolean.class, Field[].class),
tc.load(entry.getValue().finalFieldsWritable), fields);
} else if (!entry.getValue().fieldSet.isEmpty()) {
ResultHandle farray = tc.newArray(Field.class, tc.load(1));
for (String field : entry.getValue().fieldSet) {
ResultHandle fhandle = tc.invokeVirtualMethod(
ofMethod(Class.class, "getDeclaredField", Field.class, String.class), clazz, tc.load(field));
tc.writeArrayValue(farray, 0, fhandle);
tc.invokeStaticMethod(
ofMethod(RUNTIME_REFLECTION, "register", void.class, Field[].class),
farray);
if (entry.getValue().fields) {
BranchResult graalVm21Test = tc.ifGreaterEqualZero(
tc.invokeVirtualMethod(VERSION_COMPARE_TO,
tc.invokeStaticMethod(VERSION_CURRENT),
tc.marshalAsArray(int.class, tc.load(21))));
graalVm21Test.trueBranch().invokeStaticMethod(
ofMethod(RUNTIME_REFLECTION, "register", void.class,
boolean.class, boolean.class, Field[].class),
tc.load(entry.getValue().finalFieldsWritable), tc.load(entry.getValue().serialization), fields);
graalVm21Test.falseBranch().invokeStaticMethod(
ofMethod(RUNTIME_REFLECTION, "register", void.class,
boolean.class, Field[].class),
tc.load(entry.getValue().finalFieldsWritable), fields);
} else if (!entry.getValue().fieldSet.isEmpty()) {
ResultHandle farray = tc.newArray(Field.class, tc.load(1));
for (String field : entry.getValue().fieldSet) {
ResultHandle fhandle = tc.invokeVirtualMethod(
ofMethod(Class.class, "getDeclaredField", Field.class, String.class), clazz, tc.load(field));
tc.writeArrayValue(farray, 0, fhandle);
tc.invokeStaticMethod(
ofMethod(RUNTIME_REFLECTION, "register", void.class, Field[].class),
farray);
}
}
} else {
tc.invokeStaticMethod(WEAK_REFLECTION_REGISTRATION, tc.getMethodParam(0), clazz,
tc.load(entry.getValue().constructors), tc.load(entry.getValue().methods),
tc.load(entry.getValue().fields));
}

if (entry.getValue().serialization) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.quarkus.runtime.graal;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.hosted.RuntimeReflection;
import org.jboss.logging.Logger;

/**
* Weak reflection implementation called from generated bytecode
*/
public class WeakReflection {

static final Logger log = Logger.getLogger(WeakReflection.class);

public static void register(Feature.BeforeAnalysisAccess analysisAccess, Class<?> clazz, boolean constructors,
boolean methods, boolean fields) {
analysisAccess.registerReachabilityHandler(new Callback(clazz, constructors, methods, fields), clazz);
}

public static class Callback implements Consumer<Feature.DuringAnalysisAccess> {
final AtomicBoolean onlyOnce = new AtomicBoolean(false);
final Class<?> clazz;
final boolean constructors;
final boolean methods;
final boolean fields;

public Callback(Class<?> clazz, boolean constructors, boolean methods, boolean fields) {
this.clazz = clazz;
this.constructors = constructors;
this.methods = methods;
this.fields = fields;
}

@Override
public void accept(Feature.DuringAnalysisAccess duringAnalysisAccess) {
if (!onlyOnce.compareAndSet(false, true)) {
return;
}
log.debugf("Registering %s for reflection as it is reachable", clazz);
RuntimeReflection.register(clazz);
if (fields) {
RuntimeReflection.register(clazz.getDeclaredFields());
}
if (constructors) {
RuntimeReflection.register(clazz.getDeclaredConstructors());
}
if (methods) {
RuntimeReflection.register(clazz.getDeclaredMethods());
}
}
}

}

0 comments on commit 758e8cb

Please sign in to comment.