Skip to content

Commit

Permalink
Extend ReflectiveClassBuildItem to support queryOnly option
Browse files Browse the repository at this point in the history
queryOnly registrations enables us to register only the metadata without
the actual code, essentially reducing the native executable size.  For
instance, if some code invokes getDeclaredMethods on a class to see if a
specific method is available we don't actually need all the methods
bundled in the native executable, so we could use queryAllMethods
instead of methods (which will also pull in the code of all methods).

Note that for the time being we only extend it to support
queryAllDeclaredConstructors and queryAllMethods since we don't have an
option to register only public methods.

Closes quarkusio#41999
  • Loading branch information
zakkak committed Jul 23, 2024
1 parent 1748f93 commit a087e59
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.List;

import io.quarkus.builder.item.MultiBuildItem;
import io.quarkus.logging.Log;

/**
* Used to register a class for reflection in native mode
Expand All @@ -15,8 +16,10 @@ public final class ReflectiveClassBuildItem extends MultiBuildItem {

private final List<String> className;
private final boolean methods;
private final boolean queryMethods;
private final boolean fields;
private final boolean constructors;
private final boolean queryConstructors;
private final boolean weak;
private final boolean serialization;
private final boolean unsafeAllocated;
Expand All @@ -38,7 +41,8 @@ public static Builder builder(String... classNames) {
return new Builder().className(classNames);
}

private ReflectiveClassBuildItem(boolean constructors, boolean methods, boolean fields, boolean weak, boolean serialization,
private ReflectiveClassBuildItem(boolean constructors, boolean queryConstructors, boolean methods, boolean queryMethods,
boolean fields, boolean weak, boolean serialization,
boolean unsafeAllocated, Class<?>... classes) {
List<String> names = new ArrayList<>();
for (Class<?> i : classes) {
Expand All @@ -49,8 +53,24 @@ private ReflectiveClassBuildItem(boolean constructors, boolean methods, boolean
}
this.className = names;
this.methods = methods;
if (methods && queryMethods) {
Log.warnf(
"Both methods and queryMethods are set to true for classes: %s. queryMethods is redundant and will be ignored",
String.join(", ", names));
this.queryMethods = false;
} else {
this.queryMethods = queryMethods;
}
this.fields = fields;
this.constructors = constructors;
if (methods && queryMethods) {
Log.warnf(
"Both constructors and queryConstructors are set to true for classes: %s. queryConstructors is redundant and will be ignored",
String.join(", ", names));
this.queryConstructors = false;
} else {
this.queryConstructors = queryConstructors;
}
this.weak = weak;
this.serialization = serialization;
this.unsafeAllocated = unsafeAllocated;
Expand All @@ -74,7 +94,7 @@ public ReflectiveClassBuildItem(boolean methods, boolean fields, Class<?>... cla
*/
@Deprecated(since = "3.0", forRemoval = true)
public ReflectiveClassBuildItem(boolean constructors, boolean methods, boolean fields, Class<?>... classes) {
this(constructors, methods, fields, false, false, false, classes);
this(constructors, false, methods, false, fields, false, false, false, classes);
}

/**
Expand All @@ -92,7 +112,7 @@ public ReflectiveClassBuildItem(boolean methods, boolean fields, String... class
*/
@Deprecated(since = "3.0", forRemoval = true)
public ReflectiveClassBuildItem(boolean constructors, boolean methods, boolean fields, String... classNames) {
this(constructors, methods, fields, false, false, false, classNames);
this(constructors, false, methods, false, fields, false, false, false, classNames);
}

/**
Expand All @@ -102,7 +122,7 @@ public ReflectiveClassBuildItem(boolean constructors, boolean methods, boolean f
@Deprecated(since = "3.0", forRemoval = true)
public ReflectiveClassBuildItem(boolean constructors, boolean methods, boolean fields, boolean serialization,
String... classNames) {
this(constructors, methods, fields, false, serialization, false, classNames);
this(constructors, false, methods, false, fields, false, serialization, false, classNames);
}

public static ReflectiveClassBuildItem weakClass(String... classNames) {
Expand All @@ -123,7 +143,8 @@ public static ReflectiveClassBuildItem serializationClass(String... classNames)
return ReflectiveClassBuildItem.builder(classNames).serialization().build();
}

ReflectiveClassBuildItem(boolean constructors, boolean methods, boolean fields, boolean weak, boolean serialization,
ReflectiveClassBuildItem(boolean constructors, boolean queryConstructors, boolean methods, boolean queryMethods,
boolean fields, boolean weak, boolean serialization,
boolean unsafeAllocated, String... className) {
for (String i : className) {
if (i == null) {
Expand All @@ -132,8 +153,24 @@ public static ReflectiveClassBuildItem serializationClass(String... classNames)
}
this.className = Arrays.asList(className);
this.methods = methods;
if (methods && queryMethods) {
Log.warnf(
"Both methods and queryMethods are set to true for classes: %s. queryMethods is redundant and will be ignored",
String.join(", ", className));
this.queryMethods = false;
} else {
this.queryMethods = queryMethods;
}
this.fields = fields;
this.constructors = constructors;
if (methods && queryMethods) {
Log.warnf(
"Both constructors and queryConstructors are set to true for classes: %s. queryConstructors is redundant and will be ignored",
String.join(", ", className));
this.queryConstructors = false;
} else {
this.queryConstructors = queryConstructors;
}
this.weak = weak;
this.serialization = serialization;
this.unsafeAllocated = unsafeAllocated;
Expand All @@ -147,6 +184,10 @@ public boolean isMethods() {
return methods;
}

public boolean isQueryMethods() {
return queryMethods;
}

public boolean isFields() {
return fields;
}
Expand All @@ -155,6 +196,10 @@ public boolean isConstructors() {
return constructors;
}

public boolean isQueryConstructors() {
return queryConstructors;
}

/**
* @deprecated As of GraalVM 21.2 finalFieldsWritable is no longer needed when registering fields for reflection. This will
* be removed in a future verion of Quarkus.
Expand All @@ -179,7 +224,9 @@ public boolean isUnsafeAllocated() {
public static class Builder {
private String[] className;
private boolean constructors = true;
private boolean queryConstructors;
private boolean methods;
private boolean queryMethods;
private boolean fields;
private boolean weak;
private boolean serialization;
Expand All @@ -202,6 +249,15 @@ public Builder constructors() {
return constructors(true);
}

public Builder queryConstructors(boolean queryConstructors) {
this.queryConstructors = queryConstructors;
return this;
}

public Builder queryConstructors() {
return queryConstructors(true);
}

public Builder methods(boolean methods) {
this.methods = methods;
return this;
Expand All @@ -211,6 +267,15 @@ public Builder methods() {
return methods(true);
}

public Builder queryMethods(boolean queryMethods) {
this.queryMethods = queryMethods;
return this;
}

public Builder queryMethods() {
return queryMethods(true);
}

public Builder fields(boolean fields) {
this.fields = fields;
return this;
Expand Down Expand Up @@ -257,7 +322,8 @@ public Builder unsafeAllocated() {
}

public ReflectiveClassBuildItem build() {
return new ReflectiveClassBuildItem(constructors, methods, fields, weak, serialization, unsafeAllocated, className);
return new ReflectiveClassBuildItem(constructors, queryConstructors, methods, queryMethods, fields, weak,
serialization, unsafeAllocated, className);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ void generateReflectConfig(BuildProducer<GeneratedResourceBuildItem> reflectConf
forcedNonWeakClasses.add(nonWeakReflectiveClassBuildItem.getClassName());
}
for (ReflectiveClassBuildItem i : reflectiveClassBuildItems) {
addReflectiveClass(reflectiveClasses, forcedNonWeakClasses, i.isConstructors(), i.isMethods(), i.isFields(),
addReflectiveClass(reflectiveClasses, forcedNonWeakClasses, i.isConstructors(), i.isQueryConstructors(),
i.isMethods(), i.isQueryMethods(), i.isFields(),
i.isWeak(), i.isSerialization(), i.isUnsafeAllocated(), i.getClassNames().toArray(new String[0]));
}
for (ReflectiveFieldBuildItem i : reflectiveFields) {
Expand All @@ -51,7 +52,7 @@ void generateReflectConfig(BuildProducer<GeneratedResourceBuildItem> reflectConf
}

for (ServiceProviderBuildItem i : serviceProviderBuildItems) {
addReflectiveClass(reflectiveClasses, forcedNonWeakClasses, true, false, false, false, false, false,
addReflectiveClass(reflectiveClasses, forcedNonWeakClasses, true, false, false, false, false, false, false, false,
i.providers().toArray(new String[] {}));
}

Expand All @@ -76,30 +77,40 @@ void generateReflectConfig(BuildProducer<GeneratedResourceBuildItem> reflectConf
}
if (info.constructors) {
json.put("allDeclaredConstructors", true);
} else if (!info.ctorSet.isEmpty()) {
for (ReflectiveMethodBuildItem ctor : info.ctorSet) {
JsonObjectBuilder methodObject = Json.object();
methodObject.put("name", ctor.getName());
JsonArrayBuilder paramsArray = Json.array();
for (int i = 0; i < ctor.getParams().length; ++i) {
paramsArray.add(ctor.getParams()[i]);
} else {
if (info.queryConstructors) {
json.put("queryAllDeclaredConstructors", true);
}
if (!info.ctorSet.isEmpty()) {
for (ReflectiveMethodBuildItem ctor : info.ctorSet) {
JsonObjectBuilder methodObject = Json.object();
methodObject.put("name", ctor.getName());
JsonArrayBuilder paramsArray = Json.array();
for (int i = 0; i < ctor.getParams().length; ++i) {
paramsArray.add(ctor.getParams()[i]);
}
methodObject.put("parameterTypes", paramsArray);
methodsArray.add(methodObject);
}
methodObject.put("parameterTypes", paramsArray);
methodsArray.add(methodObject);
}
}
if (info.methods) {
json.put("allDeclaredMethods", true);
} else if (!info.methodSet.isEmpty()) {
for (ReflectiveMethodBuildItem method : info.methodSet) {
JsonObjectBuilder methodObject = Json.object();
methodObject.put("name", method.getName());
JsonArrayBuilder paramsArray = Json.array();
for (int i = 0; i < method.getParams().length; ++i) {
paramsArray.add(method.getParams()[i]);
} else {
if (info.queryMethods) {
json.put("queryAllDeclaredMethods", true);
}
if (!info.methodSet.isEmpty()) {
for (ReflectiveMethodBuildItem method : info.methodSet) {
JsonObjectBuilder methodObject = Json.object();
methodObject.put("name", method.getName());
JsonArrayBuilder paramsArray = Json.array();
for (int i = 0; i < method.getParams().length; ++i) {
paramsArray.add(method.getParams()[i]);
}
methodObject.put("parameterTypes", paramsArray);
methodsArray.add(methodObject);
}
methodObject.put("parameterTypes", paramsArray);
methodsArray.add(methodObject);
}
}
if (!methodsArray.isEmpty()) {
Expand Down Expand Up @@ -145,22 +156,28 @@ public void addReflectiveMethod(Map<String, ReflectionInfo> reflectiveClasses, R
}

public void addReflectiveClass(Map<String, ReflectionInfo> reflectiveClasses, Set<String> forcedNonWeakClasses,
boolean constructors, boolean method,
boolean fields, boolean weak, boolean serialization, boolean unsafeAllocated,
boolean constructors, boolean queryConstructors, boolean method,
boolean queryMethods, boolean fields, boolean weak, boolean serialization, boolean unsafeAllocated,
String... className) {
for (String cl : className) {
ReflectionInfo existing = reflectiveClasses.get(cl);
if (existing == null) {
String typeReachable = (!forcedNonWeakClasses.contains(cl) && weak) ? cl : null;
reflectiveClasses.put(cl, new ReflectionInfo(constructors, method, fields,
reflectiveClasses.put(cl, new ReflectionInfo(constructors, queryConstructors, method, queryMethods, fields,
typeReachable, serialization, unsafeAllocated));
} else {
if (constructors) {
existing.constructors = true;
}
if (queryConstructors) {
existing.queryConstructors = true;
}
if (method) {
existing.methods = true;
}
if (queryMethods) {
existing.queryMethods = true;
}
if (fields) {
existing.fields = true;
}
Expand All @@ -185,7 +202,9 @@ public void addReflectiveField(Map<String, ReflectionInfo> reflectiveClasses, Re

static final class ReflectionInfo {
boolean constructors;
boolean queryConstructors;
boolean methods;
boolean queryMethods;
boolean fields;
boolean serialization;
boolean unsafeAllocated;
Expand All @@ -195,15 +214,18 @@ static final class ReflectionInfo {
Set<ReflectiveMethodBuildItem> ctorSet = new HashSet<>();

private ReflectionInfo() {
this(false, false, false, null, false, false);
this(false, false, false, false, false, null, false, false);
}

private ReflectionInfo(boolean constructors, boolean methods, boolean fields, String typeReachable,
private ReflectionInfo(boolean constructors, boolean queryConstructors, boolean methods, boolean queryMethods,
boolean fields, String typeReachable,
boolean serialization, boolean unsafeAllocated) {
this.methods = methods;
this.queryMethods = queryMethods;
this.fields = fields;
this.typeReachable = typeReachable;
this.constructors = constructors;
this.queryConstructors = queryConstructors;
this.serialization = serialization;
this.unsafeAllocated = unsafeAllocated;
}
Expand Down

0 comments on commit a087e59

Please sign in to comment.