blob: c27b55b537ffacfcf5403ab8300e1ac556352e11 [file] [log] [blame]
// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.classmerging;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.LookupResult;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class KeptTargetsIncompleteDiamondTest extends TestBase {
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withNoneRuntime().build();
}
public KeptTargetsIncompleteDiamondTest(TestParameters parameters) {
// Empty to satisfy construction of none-runtime.
}
private AppView<AppInfoWithLiveness> computeAppViewWithLiveness(
Class<?> methodToBeKept, Class<?> classToBeKept) throws Exception {
return computeAppViewWithLiveness(
buildClasses(I.class, J.class, K.class, L.class, A.class, Main.class).build(),
factory -> {
List<ProguardConfigurationRule> rules = new ArrayList<>();
rules.addAll(buildKeepRuleForClassAndMethods(methodToBeKept, factory));
rules.addAll(buildKeepRuleForClass(classToBeKept, factory));
rules.addAll(buildKeepRuleForClassAndMethods(Main.class, factory));
return rules;
});
}
@Test
public void testRefinedReceiverOnStaticTarget() throws Exception {
// I { <-- kept
// foo() <-- kept, initial
// }
//
// J {
// default foo() { ... } <-- kept, resolved
// }
//
// K { }
//
// L extends I, K { } <-- upperbound
//
// A implements L { <-- lowerbound
//
// }
AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(I.class, I.class);
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
ResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
DexType typeI = buildType(I.class, appView.dexItemFactory());
DexType typeL = buildType(L.class, appView.dexItemFactory());
DexType typeA = buildType(A.class, appView.dexItemFactory());
DexProgramClass classI = appView.definitionForProgramType(typeI);
DexProgramClass classL = appView.definitionForProgramType(typeL);
DexProgramClass classA = appView.definitionForProgramType(typeA);
LookupResult lookupResult =
resolutionResult.lookupVirtualDispatchTargets(classI, appView.appInfo(), classL, classA);
assertTrue(lookupResult.isLookupResultSuccess());
LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
Set<String> targets = new HashSet<>();
lookupResultSuccess.forEach(
methodTarget -> targets.add(methodTarget.asMethodTarget().getDefinition().qualifiedName()),
lambdaTarget -> {
assert false;
});
Set<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
assertEquals(expected, targets);
assertTrue(lookupResultSuccess.isComplete());
}
@Test
public void testRefinedReceiverOnStaticTargetInRange() throws Exception {
// I { <-- kept
// foo() <-- kept, initial, upperbound
// }
//
// J {
// default foo() { ... } <-- kept, resolved
// }
//
// K { }
//
// L extends I, K { }
//
// A implements L { <-- lowerbound
//
// }
AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(I.class, I.class);
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
ResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
DexType typeI = buildType(I.class, appView.dexItemFactory());
DexType typeL = buildType(L.class, appView.dexItemFactory());
DexType typeA = buildType(A.class, appView.dexItemFactory());
DexProgramClass classI = appView.definitionForProgramType(typeI);
DexProgramClass classA = appView.definitionForProgramType(typeA);
LookupResult lookupResult =
resolutionResult.lookupVirtualDispatchTargets(classI, appView.appInfo(), classI, classA);
assertTrue(lookupResult.isLookupResultSuccess());
LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
Set<String> targets = new HashSet<>();
lookupResultSuccess.forEach(
methodTarget -> targets.add(methodTarget.asMethodTarget().getDefinition().qualifiedName()),
lambdaTarget -> {
assert false;
});
Set<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
assertEquals(expected, targets);
assertTrue(lookupResultSuccess.isComplete());
}
@Test
public void testRefinedReceiverWithKeptDispatchTarget() throws Exception {
// I {
// foo() <-- initial
// }
//
// J { <-- kept
// default foo() { ... } <-- kept, resolved
// }
//
// K { }
//
// L extends I, K { }
//
// A implements L { <-- lowerbound, upperbound
//
// }
AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(J.class, J.class);
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
ResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
DexType typeI = buildType(I.class, appView.dexItemFactory());
DexType typeB = buildType(A.class, appView.dexItemFactory());
DexProgramClass classI = appView.definitionForProgramType(typeI);
DexProgramClass classA = appView.definitionForProgramType(typeB);
LookupResult lookupResult =
resolutionResult.lookupVirtualDispatchTargets(classI, appView.appInfo(), classA, classA);
assertTrue(lookupResult.isLookupResultSuccess());
LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
Set<String> targets = new HashSet<>();
lookupResultSuccess.forEach(
methodTarget -> targets.add(methodTarget.asMethodTarget().getDefinition().qualifiedName()),
lambdaTarget -> {
assert false;
});
Set<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
assertEquals(expected, targets);
assertTrue(lookupResultSuccess.isIncomplete());
}
@Test
public void testRefinedReceiverWithKeptRefinedReceiver() throws Exception {
// I {
// foo() <-- initial
// }
//
// J {
// default foo() { ... } <-- kept, resolved
// }
//
// K { }
//
// A implements I, K { <-- kept, lowerbound, upperbound
//
// }
AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(J.class, A.class);
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
ResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
DexType typeI = buildType(I.class, appView.dexItemFactory());
DexType typeB = buildType(A.class, appView.dexItemFactory());
DexProgramClass classI = appView.definitionForProgramType(typeI);
DexProgramClass classA = appView.definitionForProgramType(typeB);
LookupResult lookupResult =
resolutionResult.lookupVirtualDispatchTargets(classI, appView.appInfo(), classA, classA);
assertTrue(lookupResult.isLookupResultSuccess());
LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
Set<String> targets = new HashSet<>();
lookupResultSuccess.forEach(
methodTarget -> targets.add(methodTarget.asMethodTarget().getDefinition().qualifiedName()),
lambdaTarget -> {
assert false;
});
Set<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
assertEquals(expected, targets);
assertTrue(lookupResultSuccess.isIncomplete());
}
@Test
public void testRefinedReceiverWithKeptClassOnInitial() throws Exception {
// I { <-- kept
// foo() <-- kept, initial
// }
//
// J {
// default foo() { ... } <-- resolved
// }
//
// K { }
//
// L extends I, K { }
//
// A implements L { <-- lowerbound, upperbound
//
// }
AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(I.class, I.class);
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
ResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
DexType typeI = buildType(I.class, appView.dexItemFactory());
DexType typeB = buildType(A.class, appView.dexItemFactory());
DexProgramClass classI = appView.definitionForProgramType(typeI);
DexProgramClass classA = appView.definitionForProgramType(typeB);
LookupResult lookupResult =
resolutionResult.lookupVirtualDispatchTargets(classI, appView.appInfo(), classA, classA);
assertTrue(lookupResult.isLookupResultSuccess());
LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
Set<String> targets = new HashSet<>();
lookupResultSuccess.forEach(
methodTarget -> targets.add(methodTarget.asMethodTarget().getDefinition().qualifiedName()),
lambdaTarget -> {
assert false;
});
Set<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
assertEquals(expected, targets);
assertTrue(lookupResultSuccess.isComplete());
}
public interface I {
void foo();
}
public interface J extends I {
@Override
default void foo() {
System.out.println("K.foo");
}
}
public interface K extends J {}
public interface L extends I, K {}
public static class A implements L {}
public static class Main {
public static void main(String[] args) {
new A();
}
}
}