Skip to content

Commit 09fc585

Browse files
committed
Introduce a typeReached check for Class.forName
1 parent 241d5b4 commit 09fc585

28 files changed

+346
-114
lines changed

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

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,33 +53,40 @@
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) {
6567
if (JAVA_LANG_OBJECT_REACHED.getType().equals(type)) {
6668
return JAVA_LANG_OBJECT_REACHED;
6769
}
68-
return new ConfigurationCondition(type);
70+
return new ConfigurationCondition(type, runtimeChecked);
6971
}
7072

7173
public boolean isAlwaysTrue() {
7274
return ConfigurationCondition.alwaysTrue().equals(this);
7375
}
7476

75-
private ConfigurationCondition(Class<?> type) {
77+
private ConfigurationCondition(Class<?> type, boolean runtimeChecked) {
78+
this.runtimeChecked = runtimeChecked;
7679
this.type = type;
7780
}
7881

7982
public Class<?> getType() {
8083
return type;
8184
}
8285

86+
public boolean isRuntimeChecked() {
87+
return runtimeChecked;
88+
}
89+
8390
@Override
8491
public boolean equals(Object o) {
8592
if (this == o) {
@@ -88,13 +95,12 @@ public boolean equals(Object o) {
8895
if (o == null || getClass() != o.getClass()) {
8996
return false;
9097
}
91-
ConfigurationCondition condition = (ConfigurationCondition) o;
92-
return Objects.equals(type, condition.type);
98+
ConfigurationCondition that = (ConfigurationCondition) o;
99+
return runtimeChecked == that.runtimeChecked && Objects.equals(type, that.type);
93100
}
94101

95102
@Override
96103
public int hashCode() {
97-
return Objects.hash(type);
104+
return Objects.hash(type, runtimeChecked);
98105
}
99-
100106
}

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

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,20 @@
4747
*/
4848
public class UnresolvedConfigurationCondition implements Comparable<UnresolvedConfigurationCondition> {
4949
private final String typeName;
50-
private static final UnresolvedConfigurationCondition JAVA_LANG_OBJECT_REACHED = new UnresolvedConfigurationCondition(Object.class.getTypeName());
50+
private final boolean runtimeChecked;
51+
private static final UnresolvedConfigurationCondition JAVA_LANG_OBJECT_REACHED = new UnresolvedConfigurationCondition(Object.class.getTypeName(), true);
5152

52-
public static UnresolvedConfigurationCondition create(String typeName) {
53+
public static UnresolvedConfigurationCondition create(String typeName, boolean runtimeChecked) {
5354
Objects.requireNonNull(typeName);
5455
if (JAVA_LANG_OBJECT_REACHED.getTypeName().equals(typeName)) {
5556
return JAVA_LANG_OBJECT_REACHED;
5657
}
57-
return new UnresolvedConfigurationCondition(typeName);
58+
return new UnresolvedConfigurationCondition(typeName, runtimeChecked);
5859
}
5960

60-
protected UnresolvedConfigurationCondition(String typeName) {
61+
protected UnresolvedConfigurationCondition(String typeName, boolean runtimeChecked) {
6162
this.typeName = typeName;
63+
this.runtimeChecked = runtimeChecked;
6264
}
6365

6466
public static UnresolvedConfigurationCondition alwaysTrue() {
@@ -69,6 +71,14 @@ public String getTypeName() {
6971
return typeName;
7072
}
7173

74+
public boolean isRuntimeChecked() {
75+
return runtimeChecked;
76+
}
77+
78+
public boolean isAlwaysTrue() {
79+
return typeName.equals(JAVA_LANG_OBJECT_REACHED.getTypeName());
80+
}
81+
7282
@Override
7383
public boolean equals(Object o) {
7484
if (this == o) {
@@ -77,26 +87,28 @@ public boolean equals(Object o) {
7787
if (o == null || getClass() != o.getClass()) {
7888
return false;
7989
}
80-
UnresolvedConfigurationCondition condition = (UnresolvedConfigurationCondition) o;
81-
return Objects.equals(typeName, condition.typeName);
90+
UnresolvedConfigurationCondition that = (UnresolvedConfigurationCondition) o;
91+
return runtimeChecked == that.runtimeChecked && Objects.equals(typeName, that.typeName);
8292
}
8393

8494
@Override
8595
public int hashCode() {
86-
return Objects.hash(typeName);
96+
return Objects.hash(typeName, runtimeChecked);
8797
}
8898

8999
@Override
90-
public String toString() {
91-
return "[\"typeReachable\": \"" + typeName + "\"" + "]";
100+
public int compareTo(UnresolvedConfigurationCondition o) {
101+
int res = Boolean.compare(runtimeChecked, o.runtimeChecked);
102+
if (res != 0) {
103+
return res;
104+
}
105+
return typeName.compareTo(o.typeName);
92106
}
93107

94108
@Override
95-
public int compareTo(UnresolvedConfigurationCondition c) {
96-
return this.typeName.compareTo(c.typeName);
109+
public String toString() {
110+
var field = runtimeChecked ? "typeReached" : "typeReachable";
111+
return "[" + field + ": \"" + typeName + "\"" + "]";
97112
}
98113

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

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
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/classinitialization/ClassInitializationInfo.java

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,20 +60,26 @@
6060
@InternalVMMethod
6161
public final class ClassInitializationInfo {
6262

63-
/**
64-
* Singleton for classes that are already initialized during image building and do not need
65-
* class initialization at runtime, but have {@code <clinit>} methods.
66-
*/
67-
public static final ClassInitializationInfo INITIALIZED_INFO_SINGLETON = new ClassInitializationInfo(InitState.FullyInitialized, true);
63+
/** Singleton for classes that failed to link during image building. */
64+
public static ClassInitializationInfo createFailedInfo() {
65+
return new ClassInitializationInfo(InitState.InitializationError);
66+
}
6867

6968
/**
7069
* Singleton for classes that are already initialized during image building and do not need
7170
* class initialization at runtime, and don't have {@code <clinit>} methods.
7271
*/
73-
public static final ClassInitializationInfo NO_INITIALIZER_INFO_SINGLETON = new ClassInitializationInfo(InitState.FullyInitialized, false);
72+
public static ClassInitializationInfo createNoInitializerInfo() {
73+
return new ClassInitializationInfo(InitState.FullyInitialized, false, true);
74+
}
7475

75-
/** Singleton for classes that failed to link during image building. */
76-
public static final ClassInitializationInfo FAILED_INFO_SINGLETON = new ClassInitializationInfo(InitState.InitializationError);
76+
/**
77+
* For classes that are already initialized during image building and do not need class
78+
* initialization at runtime, but have {@code <clinit>} methods.
79+
*/
80+
public static ClassInitializationInfo createInitializedInfo() {
81+
return new ClassInitializationInfo(InitState.FullyInitialized, true, true);
82+
}
7783

7884
enum InitState {
7985
/**
@@ -133,11 +139,20 @@ interface ClassInitializerFunctionPointer extends CFunctionPointer {
133139
* at native image's build time or run time.
134140
*/
135141
private boolean hasInitializer;
142+
private boolean buildTimeInitialized;
143+
144+
private boolean reached;
145+
146+
public boolean isReached() {
147+
return reached;
148+
}
136149

137150
@Platforms(Platform.HOSTED_ONLY.class)
138-
private ClassInitializationInfo(InitState initState, boolean hasInitializer) {
151+
private ClassInitializationInfo(InitState initState, boolean hasInitializer, boolean buildTime) {
139152
this(initState);
140153
this.hasInitializer = hasInitializer;
154+
this.buildTimeInitialized = buildTime;
155+
this.reached = false;
141156
}
142157

143158
@Platforms(Platform.HOSTED_ONLY.class)
@@ -164,6 +179,14 @@ public boolean isInitialized() {
164179
return initState == InitState.FullyInitialized;
165180
}
166181

182+
public boolean isInitializationError() {
183+
return initState == InitState.InitializationError;
184+
}
185+
186+
public boolean isBuildTimeInitialized() {
187+
return buildTimeInitialized;
188+
}
189+
167190
private boolean isBeingInitialized() {
168191
return initState == InitState.BeingInitialized;
169192
}
@@ -176,6 +199,23 @@ private boolean isReentrantInitialization(IsolateThread thread) {
176199
return thread.equal(initThread);
177200
}
178201

202+
private static void reach(DynamicHub hub) {
203+
var current = hub;
204+
do {
205+
current.getClassInitializationInfo().reached = true;
206+
current = current.getSuperHub();
207+
} while (current != null);
208+
209+
reachInterfaces(hub);
210+
}
211+
212+
private static void reachInterfaces(DynamicHub hub) {
213+
for (DynamicHub superInterface : hub.getInterfaces()) {
214+
superInterface.getClassInitializationInfo().reached = true;
215+
reachInterfaces(superInterface);
216+
}
217+
}
218+
179219
/**
180220
* Perform class initialization. This is the slow-path that should only be called after checking
181221
* {@link #isInitialized}.
@@ -187,6 +227,12 @@ private boolean isReentrantInitialization(IsolateThread thread) {
187227
private static void initialize(ClassInitializationInfo info, DynamicHub hub) {
188228
IsolateThread self = CurrentIsolate.getCurrentThread();
189229

230+
reach(hub);
231+
232+
if (info.isInitialized()) {
233+
return;
234+
}
235+
190236
/*
191237
* GR-43118: If a predefined class is not loaded, and the caller class is loaded, set the
192238
* classloader of the initialized class to the class loader of the caller class.

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/classinitialization/EnsureClassInitializedNode.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
*/
2525
package com.oracle.svm.core.classinitialization;
2626

27+
import org.graalvm.word.LocationIdentity;
28+
2729
import jdk.graal.compiler.core.common.type.StampFactory;
2830
import jdk.graal.compiler.graph.Node;
2931
import jdk.graal.compiler.graph.Node.NodeIntrinsicFactory;
@@ -42,8 +44,6 @@
4244
import jdk.graal.compiler.nodes.spi.CanonicalizerTool;
4345
import jdk.graal.compiler.nodes.spi.Lowerable;
4446
import jdk.graal.compiler.nodes.type.StampTool;
45-
import org.graalvm.word.LocationIdentity;
46-
4747
import jdk.vm.ci.meta.ConstantReflectionProvider;
4848
import jdk.vm.ci.meta.ResolvedJavaType;
4949

@@ -54,22 +54,24 @@ public class EnsureClassInitializedNode extends WithExceptionNode implements Can
5454
public static final NodeClass<EnsureClassInitializedNode> TYPE = NodeClass.create(EnsureClassInitializedNode.class);
5555

5656
@Input private ValueNode hub;
57+
private final boolean requiredForTypeReached;
5758
@Input(InputType.State) private FrameState stateAfter;
5859

5960
public static boolean intrinsify(GraphBuilderContext b, ValueNode hub) {
6061
b.add(new EnsureClassInitializedNode(b.nullCheckedValue(hub)));
6162
return true;
6263
}
6364

64-
public EnsureClassInitializedNode(ValueNode hub, FrameState stateAfter) {
65+
public EnsureClassInitializedNode(ValueNode hub, FrameState stateAfter, boolean requiredForTypeReached) {
6566
super(TYPE, StampFactory.forVoid());
6667
this.hub = hub;
68+
this.requiredForTypeReached = requiredForTypeReached;
6769
assert StampTool.isPointerNonNull(hub) : "Hub must already be null-checked";
6870
this.stateAfter = stateAfter;
6971
}
7072

7173
public EnsureClassInitializedNode(ValueNode hub) {
72-
this(hub, null);
74+
this(hub, null, false);
7375
}
7476

7577
public ValueNode getHub() {
@@ -108,7 +110,7 @@ public ResolvedJavaType constantTypeOrNull(ConstantReflectionProvider constantRe
108110
@Override
109111
public Node canonical(CanonicalizerTool tool) {
110112
ResolvedJavaType type = constantTypeOrNull(tool.getConstantReflection());
111-
if (type != null) {
113+
if (type != null && !requiredForTypeReached) {
112114
for (FrameState cur = stateAfter; cur != null; cur = cur.outerFrameState()) {
113115
if (!needsRuntimeInitialization(cur.getMethod().getDeclaringClass(), type)) {
114116
return null;

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/classinitialization/EnsureClassInitializedSnippets.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@
2828

2929
import java.util.Map;
3030

31+
import org.graalvm.word.LocationIdentity;
32+
33+
import com.oracle.svm.core.graal.nodes.ForeignCallWithExceptionNode;
34+
import com.oracle.svm.core.graal.snippets.NodeLoweringProvider;
35+
import com.oracle.svm.core.graal.snippets.SubstrateTemplates;
36+
import com.oracle.svm.core.hub.DynamicHub;
37+
import com.oracle.svm.core.snippets.SnippetRuntime;
38+
import com.oracle.svm.core.snippets.SnippetRuntime.SubstrateForeignCallDescriptor;
39+
3140
import jdk.graal.compiler.api.replacements.Snippet;
3241
import jdk.graal.compiler.core.common.spi.ForeignCallDescriptor;
3342
import jdk.graal.compiler.graph.Node;
@@ -43,14 +52,6 @@
4352
import jdk.graal.compiler.replacements.SnippetTemplate.Arguments;
4453
import jdk.graal.compiler.replacements.SnippetTemplate.SnippetInfo;
4554
import jdk.graal.compiler.replacements.Snippets;
46-
import org.graalvm.word.LocationIdentity;
47-
48-
import com.oracle.svm.core.graal.nodes.ForeignCallWithExceptionNode;
49-
import com.oracle.svm.core.graal.snippets.NodeLoweringProvider;
50-
import com.oracle.svm.core.graal.snippets.SubstrateTemplates;
51-
import com.oracle.svm.core.hub.DynamicHub;
52-
import com.oracle.svm.core.snippets.SnippetRuntime;
53-
import com.oracle.svm.core.snippets.SnippetRuntime.SubstrateForeignCallDescriptor;
5455

5556
public final class EnsureClassInitializedSnippets extends SubstrateTemplates implements Snippets {
5657
private static final SubstrateForeignCallDescriptor INITIALIZE = SnippetRuntime.findForeignCall(ClassInitializationInfo.class, "initialize", HAS_SIDE_EFFECT, LocationIdentity.any());
@@ -72,13 +73,13 @@ private static void ensureClassIsInitializedSnippet(@Snippet.NonNullParameter Dy
7273
*/
7374
ClassInitializationInfo infoNonNull = (ClassInitializationInfo) PiNode.piCastNonNull(info, SnippetAnchorNode.anchor());
7475

75-
if (BranchProbabilityNode.probability(BranchProbabilityNode.EXTREMELY_SLOW_PATH_PROBABILITY, !infoNonNull.isInitialized())) {
76-
callInitialize(INITIALIZE, infoNonNull, DynamicHub.toClass(hub));
76+
if (!BranchProbabilityNode.probability(BranchProbabilityNode.EXTREMELY_SLOW_PATH_PROBABILITY, infoNonNull.isInitialized() & infoNonNull.isReached())) {
77+
callInitializationRoutine(INITIALIZE, infoNonNull, DynamicHub.toClass(hub));
7778
}
7879
}
7980

8081
@NodeIntrinsic(value = ForeignCallWithExceptionNode.class)
81-
private static native void callInitialize(@ConstantNodeParameter ForeignCallDescriptor descriptor, ClassInitializationInfo info, Class<?> clazz);
82+
private static native void callInitializationRoutine(@ConstantNodeParameter ForeignCallDescriptor descriptor, ClassInitializationInfo info, Class<?> clazz);
8283

8384
@SuppressWarnings("unused")
8485
public static void registerLowerings(OptionValues options, Providers providers, Map<Class<? extends Node>, NodeLoweringProvider<?>> lowerings) {

0 commit comments

Comments
 (0)