Skip to content

Commit

Permalink
[GR-32865] Automatic initialization of classes must use devirtualized…
Browse files Browse the repository at this point in the history
… callee.

PullRequest: graal/9452
  • Loading branch information
Christian Wimmer committed Jul 29, 2021
2 parents 48c4ec2 + 7d950f0 commit 153141d
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,19 @@ private boolean updateMethodSafety(AnalysisMethod m) {
* Invoke becomes unsafe if it calls other unsafe methods.
*/
private boolean isInvokeUnsafeIterative(InvokeTypeFlow i) {
assert i.getTargetMethod() != null : "All methods can be statically bound.";
return methodSafety.get(i.getTargetMethod()) == Safety.UNSAFE;
/*
* Note that even though (for now) we only process invokes that can be statically bound, we
* cannot just take the target method of the type flow: the static analysis can
* de-virtualize the target method to a method overridden in a subclass. So we must look at
* the actual callees of the type flow, even though we know that there is at most one callee
* returned.
*/
for (AnalysisMethod callee : i.getCallees()) {
if (methodSafety.get(callee) == Safety.UNSAFE) {
return true;
}
}
return false;
}

private void addInitializer(AnalysisType t) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,47 @@ class ForNameMustBeDelayed {
int field;
}

class DevirtualizedCallMustBeDelayed {
static {
System.out.println("Delaying " + DevirtualizedCallMustBeDelayed.class);
}

static final Object value = 42;
}

class DevirtualizedCallSuperMustBeSafeEarly {
Object foo() {
return -1;
}
}

class DevirtualizedCallSubMustBeSafeEarly extends DevirtualizedCallSuperMustBeSafeEarly {
@Override
Object foo() {
return DevirtualizedCallMustBeDelayed.value;
}
}

class DevirtualizedCallUsageMustBeDelayed {
static final Object value = computeValue();

private static Object computeValue() {
DevirtualizedCallSuperMustBeSafeEarly provider = createProvider();

/*
* The static analysis can prove that DevirtualizedCallSubMustBeDelayed.foo is the only
* callee and de-virtualize this call. So the original target method of the call site and
* the actually invoked method are different - and the analysis that automatically
* initializes classes must properly pick up this dependency.
*/
return provider.foo();
}

private static DevirtualizedCallSuperMustBeSafeEarly createProvider() {
return new DevirtualizedCallSubMustBeSafeEarly();
}
}

class TestClassInitializationMustBeSafeEarlyFeature implements Feature {

static final Class<?>[] checkedClasses = new Class<?>[]{
Expand Down Expand Up @@ -435,7 +476,8 @@ class TestClassInitializationMustBeSafeEarlyFeature implements Feature {
NativeMethodMustBeDelayed.class,
ReferencesOtherPureClassMustBeSafeEarly.class, HelperClassMustBeSafeEarly.class,
CycleMustBeSafeLate.class, HelperClassMustBeSafeLate.class,
ReflectionMustBeSafeEarly.class, ForNameMustBeSafeEarly.class, ForNameMustBeDelayed.class
ReflectionMustBeSafeEarly.class, ForNameMustBeSafeEarly.class, ForNameMustBeDelayed.class,
DevirtualizedCallMustBeDelayed.class, DevirtualizedCallSuperMustBeSafeEarly.class, DevirtualizedCallSubMustBeSafeEarly.class, DevirtualizedCallUsageMustBeDelayed.class
};

private static void checkClasses(boolean checkSafeEarly, boolean checkSafeLate) {
Expand Down Expand Up @@ -611,6 +653,8 @@ public static void main(String[] args) {
assertSame(ForNameMustBeDelayed.class, ReflectionMustBeSafeEarly.c2);
assertSame("foo", ReflectionMustBeSafeEarly.m1.getName());
assertSame("field", ReflectionMustBeSafeEarly.f2.getName());

System.out.println(DevirtualizedCallUsageMustBeDelayed.value);
}

private static void assertSame(Object expected, Object actual) {
Expand Down

0 comments on commit 153141d

Please sign in to comment.