Skip to content

Commit 19009b6

Browse files
committed
Introduce a typeReached check for Class.forName
The core implementation of #7480 This PR applies only for `Class.forName`. The other cases will be covered in the consecutive PRs. A type is reached if the type is initialized (successfuly, or unsuccesfully), or any of the type's subtypes are reached. Type is also reached if any of the subtypes is marked as `initialize-at-build-time`. The JSON elements `typeReached` and `typeReachable` are currently distinguished as different elements and can not be merged. This is due to the possible restriction in semantics if we merge different reflection descriptors with a condition that is different only in a runtime check. The agent still outputs `typeReachable` as the implementation is not finished for all elements. Implementation notes: * The `ClassInitializationInfo` is contains the extra field `typeReached`. * The `ClassInitializationInfo` is not a singleton for build-time initialized classes that require tracking for being reached. This is required as the field `reached` can be mutated at runtime. Review entry points: 1) For the runtime computation of reached and build-time metadata construction: substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/classinitialization/ClassInitializationInfo.java 2) For the usage of the mechanism in `Class.forName`: substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java 3) For parsing the JSON files: substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationParser.java 4) For interactions with optimizations related to class initalization follow the `EnsureClassInitializedNode`: substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/classinitialization/EnsureClassInitializedNode.java.
1 parent 224a292 commit 19009b6

File tree

40 files changed

+744
-225
lines changed

40 files changed

+744
-225
lines changed

docs/reference-manual/native-image/assets/config-condition-schema-v1.0.0.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,18 @@
44
"title": "JSON schema for the conditions used in GraalVM Native Image configuration files",
55
"properties": {
66
"typeReachable": {
7-
"type": "string",
7+
"deprecated": true,
8+
"$ref": "config-type-schema-v1.0.0.json",
89
"title": "Fully qualified name of a class that must be reachable in order to register the type <type> for reflection"
10+
},
11+
"typeReached": {
12+
"$ref": "config-type-schema-v1.0.0.json",
13+
"title": "Fully qualified name of a class that must be reached in order to register the type <type> for reflection"
914
}
1015
},
11-
"required": [
12-
"typeReachable"
16+
"oneOf": [
17+
"typeReachable",
18+
"typeReached"
1319
],
1420
"additionalProperties": false,
1521
"type": "object"

sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ConfigurationCondition.java

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,43 +43,51 @@
4343
import java.util.Objects;
4444

4545
/**
46-
* A condition that describes if a reflectively accessed element in Native Image is visible by the
46+
* A condition that describes if a reflectively-accessed element in Native Image is visible by the
4747
* user.
4848
* <p>
49-
* Currently, there is only one type of condition (<code>typeReached</code>) so this is a single
49+
* Currently, there is only one type of condition (<code>typeReached</code>) so this is a final
5050
* class instead of the class hierarchy. The {@link ConfigurationCondition#type} represents the
5151
* {@link Class<>} that needs to be reached by analysis in order for an element to be visible.
5252
*/
5353
public final class ConfigurationCondition {
5454

5555
/* Cached to save space: it is used as a marker for all non-conditional elements */
56-
private static final ConfigurationCondition JAVA_LANG_OBJECT_REACHED = new ConfigurationCondition(Object.class);
56+
private static final ConfigurationCondition JAVA_LANG_OBJECT_REACHED = new ConfigurationCondition(Object.class, true);
5757

5858
public static ConfigurationCondition alwaysTrue() {
5959
return JAVA_LANG_OBJECT_REACHED;
6060
}
6161

6262
private final Class<?> type;
6363

64-
public static ConfigurationCondition create(Class<?> type) {
64+
private final boolean runtimeChecked;
65+
66+
public static ConfigurationCondition create(Class<?> type, boolean runtimeChecked) {
67+
Objects.requireNonNull(type);
6568
if (JAVA_LANG_OBJECT_REACHED.getType().equals(type)) {
6669
return JAVA_LANG_OBJECT_REACHED;
6770
}
68-
return new ConfigurationCondition(type);
71+
return new ConfigurationCondition(type, runtimeChecked);
6972
}
7073

7174
public boolean isAlwaysTrue() {
7275
return ConfigurationCondition.alwaysTrue().equals(this);
7376
}
7477

75-
private ConfigurationCondition(Class<?> type) {
78+
private ConfigurationCondition(Class<?> type, boolean runtimeChecked) {
79+
this.runtimeChecked = runtimeChecked;
7680
this.type = type;
7781
}
7882

7983
public Class<?> getType() {
8084
return type;
8185
}
8286

87+
public boolean isRuntimeChecked() {
88+
return runtimeChecked;
89+
}
90+
8391
@Override
8492
public boolean equals(Object o) {
8593
if (this == o) {
@@ -88,13 +96,13 @@ public boolean equals(Object o) {
8896
if (o == null || getClass() != o.getClass()) {
8997
return false;
9098
}
91-
ConfigurationCondition condition = (ConfigurationCondition) o;
92-
return Objects.equals(type, condition.type);
99+
ConfigurationCondition that = (ConfigurationCondition) o;
100+
return runtimeChecked == that.runtimeChecked && Objects.equals(type, that.type);
93101
}
94102

95103
@Override
96104
public int hashCode() {
97-
return Objects.hash(type);
105+
return Objects.hash(type, runtimeChecked);
98106
}
99107

100108
}

sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/UnresolvedConfigurationCondition.java

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,22 +43,27 @@
4343
import java.util.Objects;
4444

4545
/**
46-
* This is an unresolved version of the {@link ConfigurationCondition} used only during parsing.
46+
* Represents a {@link ConfigurationCondition} during parsing before it is resolved in a context of
47+
* the classpath.
4748
*/
48-
public class UnresolvedConfigurationCondition implements Comparable<UnresolvedConfigurationCondition> {
49+
public final class UnresolvedConfigurationCondition implements Comparable<UnresolvedConfigurationCondition> {
50+
private static final UnresolvedConfigurationCondition JAVA_LANG_OBJECT_REACHED = new UnresolvedConfigurationCondition(Object.class.getTypeName(), true);
51+
public static final String TYPE_REACHED_KEY = "typeReached";
52+
public static final String TYPE_REACHABLE_KEY = "typeReachable";
4953
private final String typeName;
50-
private static final UnresolvedConfigurationCondition JAVA_LANG_OBJECT_REACHED = new UnresolvedConfigurationCondition(Object.class.getTypeName());
54+
private final boolean runtimeChecked;
5155

52-
public static UnresolvedConfigurationCondition create(String typeName) {
56+
public static UnresolvedConfigurationCondition create(String typeName, boolean runtimeChecked) {
5357
Objects.requireNonNull(typeName);
5458
if (JAVA_LANG_OBJECT_REACHED.getTypeName().equals(typeName)) {
5559
return JAVA_LANG_OBJECT_REACHED;
5660
}
57-
return new UnresolvedConfigurationCondition(typeName);
61+
return new UnresolvedConfigurationCondition(typeName, runtimeChecked);
5862
}
5963

60-
protected UnresolvedConfigurationCondition(String typeName) {
64+
private UnresolvedConfigurationCondition(String typeName, boolean runtimeChecked) {
6165
this.typeName = typeName;
66+
this.runtimeChecked = runtimeChecked;
6267
}
6368

6469
public static UnresolvedConfigurationCondition alwaysTrue() {
@@ -69,6 +74,14 @@ public String getTypeName() {
6974
return typeName;
7075
}
7176

77+
public boolean isRuntimeChecked() {
78+
return runtimeChecked;
79+
}
80+
81+
public boolean isAlwaysTrue() {
82+
return typeName.equals(JAVA_LANG_OBJECT_REACHED.getTypeName());
83+
}
84+
7285
@Override
7386
public boolean equals(Object o) {
7487
if (this == o) {
@@ -77,26 +90,28 @@ public boolean equals(Object o) {
7790
if (o == null || getClass() != o.getClass()) {
7891
return false;
7992
}
80-
UnresolvedConfigurationCondition condition = (UnresolvedConfigurationCondition) o;
81-
return Objects.equals(typeName, condition.typeName);
93+
UnresolvedConfigurationCondition that = (UnresolvedConfigurationCondition) o;
94+
return runtimeChecked == that.runtimeChecked && Objects.equals(typeName, that.typeName);
8295
}
8396

8497
@Override
8598
public int hashCode() {
86-
return Objects.hash(typeName);
99+
return Objects.hash(typeName, runtimeChecked);
87100
}
88101

89102
@Override
90-
public String toString() {
91-
return "[\"typeReachable\": \"" + typeName + "\"" + "]";
103+
public int compareTo(UnresolvedConfigurationCondition o) {
104+
int res = Boolean.compare(runtimeChecked, o.runtimeChecked);
105+
if (res != 0) {
106+
return res;
107+
}
108+
return typeName.compareTo(o.typeName);
92109
}
93110

94111
@Override
95-
public int compareTo(UnresolvedConfigurationCondition c) {
96-
return this.typeName.compareTo(c.typeName);
112+
public String toString() {
113+
var field = runtimeChecked ? TYPE_REACHED_KEY : TYPE_REACHABLE_KEY;
114+
return "[" + field + ": \"" + typeName + "\"" + "]";
97115
}
98116

99-
public boolean isAlwaysTrue() {
100-
return typeName.equals(JAVA_LANG_OBJECT_REACHED.getTypeName());
101-
}
102117
}

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationConditionPrintable.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
package com.oracle.svm.configure.config;
2626

2727
import static com.oracle.svm.core.configure.ConfigurationParser.CONDITIONAL_KEY;
28-
import static com.oracle.svm.core.configure.ConfigurationParser.TYPE_REACHABLE_KEY;
28+
import static org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition.TYPE_REACHABLE_KEY;
2929

3030
import java.io.IOException;
3131

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/ConditionalConfigurationComputer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ private ConfigurationSet deduceConditionalConfiguration(Map<MethodInfo, List<Met
171171
for (List<MethodCallNode> value : methodCallNodes.values()) {
172172
for (MethodCallNode node : value) {
173173
String className = node.methodInfo.getJavaDeclaringClassName();
174-
UnresolvedConfigurationCondition condition = UnresolvedConfigurationCondition.create(className);
174+
UnresolvedConfigurationCondition condition = UnresolvedConfigurationCondition.create(className, false);
175175
var resolveCondition = ConfigurationConditionResolver.identityResolver().resolveCondition(condition);
176176
addConfigurationWithCondition(configurationSet, node.configuration, resolveCondition.get());
177177
}

0 commit comments

Comments
 (0)