Skip to content

Commit 153141d

Browse files
author
Christian Wimmer
committed
[GR-32865] Automatic initialization of classes must use devirtualized callee.
PullRequest: graal/9452
2 parents 48c4ec2 + 7d950f0 commit 153141d

File tree

2 files changed

+58
-3
lines changed

2 files changed

+58
-3
lines changed

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/TypeInitializerGraph.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,19 @@ private boolean updateMethodSafety(AnalysisMethod m) {
217217
* Invoke becomes unsafe if it calls other unsafe methods.
218218
*/
219219
private boolean isInvokeUnsafeIterative(InvokeTypeFlow i) {
220-
assert i.getTargetMethod() != null : "All methods can be statically bound.";
221-
return methodSafety.get(i.getTargetMethod()) == Safety.UNSAFE;
220+
/*
221+
* Note that even though (for now) we only process invokes that can be statically bound, we
222+
* cannot just take the target method of the type flow: the static analysis can
223+
* de-virtualize the target method to a method overridden in a subclass. So we must look at
224+
* the actual callees of the type flow, even though we know that there is at most one callee
225+
* returned.
226+
*/
227+
for (AnalysisMethod callee : i.getCallees()) {
228+
if (methodSafety.get(callee) == Safety.UNSAFE) {
229+
return true;
230+
}
231+
}
232+
return false;
222233
}
223234

224235
private void addInitializer(AnalysisType t) {

substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/clinit/TestClassInitializationMustBeSafeEarly.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,47 @@ class ForNameMustBeDelayed {
404404
int field;
405405
}
406406

407+
class DevirtualizedCallMustBeDelayed {
408+
static {
409+
System.out.println("Delaying " + DevirtualizedCallMustBeDelayed.class);
410+
}
411+
412+
static final Object value = 42;
413+
}
414+
415+
class DevirtualizedCallSuperMustBeSafeEarly {
416+
Object foo() {
417+
return -1;
418+
}
419+
}
420+
421+
class DevirtualizedCallSubMustBeSafeEarly extends DevirtualizedCallSuperMustBeSafeEarly {
422+
@Override
423+
Object foo() {
424+
return DevirtualizedCallMustBeDelayed.value;
425+
}
426+
}
427+
428+
class DevirtualizedCallUsageMustBeDelayed {
429+
static final Object value = computeValue();
430+
431+
private static Object computeValue() {
432+
DevirtualizedCallSuperMustBeSafeEarly provider = createProvider();
433+
434+
/*
435+
* The static analysis can prove that DevirtualizedCallSubMustBeDelayed.foo is the only
436+
* callee and de-virtualize this call. So the original target method of the call site and
437+
* the actually invoked method are different - and the analysis that automatically
438+
* initializes classes must properly pick up this dependency.
439+
*/
440+
return provider.foo();
441+
}
442+
443+
private static DevirtualizedCallSuperMustBeSafeEarly createProvider() {
444+
return new DevirtualizedCallSubMustBeSafeEarly();
445+
}
446+
}
447+
407448
class TestClassInitializationMustBeSafeEarlyFeature implements Feature {
408449

409450
static final Class<?>[] checkedClasses = new Class<?>[]{
@@ -435,7 +476,8 @@ class TestClassInitializationMustBeSafeEarlyFeature implements Feature {
435476
NativeMethodMustBeDelayed.class,
436477
ReferencesOtherPureClassMustBeSafeEarly.class, HelperClassMustBeSafeEarly.class,
437478
CycleMustBeSafeLate.class, HelperClassMustBeSafeLate.class,
438-
ReflectionMustBeSafeEarly.class, ForNameMustBeSafeEarly.class, ForNameMustBeDelayed.class
479+
ReflectionMustBeSafeEarly.class, ForNameMustBeSafeEarly.class, ForNameMustBeDelayed.class,
480+
DevirtualizedCallMustBeDelayed.class, DevirtualizedCallSuperMustBeSafeEarly.class, DevirtualizedCallSubMustBeSafeEarly.class, DevirtualizedCallUsageMustBeDelayed.class
439481
};
440482

441483
private static void checkClasses(boolean checkSafeEarly, boolean checkSafeLate) {
@@ -611,6 +653,8 @@ public static void main(String[] args) {
611653
assertSame(ForNameMustBeDelayed.class, ReflectionMustBeSafeEarly.c2);
612654
assertSame("foo", ReflectionMustBeSafeEarly.m1.getName());
613655
assertSame("field", ReflectionMustBeSafeEarly.f2.getName());
656+
657+
System.out.println(DevirtualizedCallUsageMustBeDelayed.value);
614658
}
615659

616660
private static void assertSame(Object expected, Object actual) {

0 commit comments

Comments
 (0)