blob: 8a35389970a2a62a252c89e3013654144079dbbe [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.resolution.singletarget;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertNull;
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.DexEncodedMethod;
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.ResolutionResult;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.Sets;
import java.util.ArrayList;
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 InstantiatedLowerBoundTest extends TestBase {
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withNoneRuntime().build();
}
public InstantiatedLowerBoundTest(TestParameters parameters) {
// Empty to satisfy construction of none-runtime.
}
@Test
public void testSingleTargetLowerBoundInstantiated() throws Exception {
AppView<AppInfoWithLiveness> appView =
computeAppViewWithLiveness(
buildClasses(A.class, B.class, Main.class).build(),
factory -> new ArrayList<>(buildKeepRuleForClassAndMethods(Main.class, factory)));
AppInfoWithLiveness appInfo = appView.appInfo();
DexType typeA = buildType(A.class, appInfo.dexItemFactory());
DexType typeB = buildType(B.class, appInfo.dexItemFactory());
DexType typeMain = buildType(Main.class, appInfo.dexItemFactory());
DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
ClassTypeElement latticeB =
ClassTypeElement.create(typeB, Nullability.definitelyNotNull(), appView);
DexEncodedMethod singleTarget =
appInfo.lookupSingleVirtualTarget(fooA, typeMain, false, t -> false, typeA, latticeB);
assertNotNull(singleTarget);
DexMethod fooB = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
assertEquals(fooB, singleTarget.method);
}
@Test
public void testSingleTargetLowerBoundInMiddleInstantiated() throws Exception {
AppView<AppInfoWithLiveness> appView =
computeAppViewWithLiveness(
buildClasses(A.class, B.class, C.class, Main.class).build(),
factory -> new ArrayList<>(buildKeepRuleForClassAndMethods(Main.class, factory)));
AppInfoWithLiveness appInfo = appView.appInfo();
DexType typeA = buildType(A.class, appInfo.dexItemFactory());
DexType typeB = buildType(B.class, appInfo.dexItemFactory());
DexType typeMain = buildType(Main.class, appInfo.dexItemFactory());
DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
ClassTypeElement latticeB =
ClassTypeElement.create(typeB, Nullability.definitelyNotNull(), appView);
DexEncodedMethod singleTarget =
appInfo.lookupSingleVirtualTarget(fooA, typeMain, false, t -> false, typeA, latticeB);
assertNotNull(singleTarget);
DexMethod fooB = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
assertEquals(fooB, singleTarget.method);
}
@Test
public void testSingleTargetLowerAllInstantiated() throws Exception {
AppView<AppInfoWithLiveness> appView =
computeAppViewWithLiveness(
buildClasses(A.class, B.class, C.class, MainAllInstantiated.class).build(),
factory ->
new ArrayList<>(
buildKeepRuleForClassAndMethods(MainAllInstantiated.class, factory)));
AppInfoWithLiveness appInfo = appView.appInfo();
DexType typeA = buildType(A.class, appInfo.dexItemFactory());
DexType typeC = buildType(C.class, appInfo.dexItemFactory());
DexType typeMain = buildType(MainAllInstantiated.class, appInfo.dexItemFactory());
DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
DexMethod fooB = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
DexMethod fooC = buildNullaryVoidMethod(C.class, "foo", appInfo.dexItemFactory());
ResolutionResult resolution = appInfo.resolveMethod(typeA, fooA);
DexProgramClass context = appView.definitionForProgramType(typeMain);
DexProgramClass upperBound = appView.definitionForProgramType(typeA);
DexProgramClass lowerBound = appView.definitionForProgramType(typeC);
LookupResult lookupResult =
resolution.lookupVirtualDispatchTargets(context, appInfo, upperBound, lowerBound);
Set<DexMethod> expected = Sets.newIdentityHashSet();
expected.add(fooA);
expected.add(fooB);
expected.add(fooC);
assertTrue(lookupResult.isLookupResultSuccess());
Set<DexMethod> actual = Sets.newIdentityHashSet();
lookupResult
.asLookupResultSuccess()
.forEach(
clazzAndMethod -> actual.add(clazzAndMethod.getMethod().method),
lambdaTarget -> {
assert false;
});
assertEquals(expected, actual);
ClassTypeElement latticeC =
ClassTypeElement.create(typeC, Nullability.definitelyNotNull(), appView);
DexEncodedMethod singleTarget =
appInfo.lookupSingleVirtualTarget(fooA, typeMain, false, t -> false, typeA, latticeC);
assertNull(singleTarget);
}
public static class A {
public void foo() {
System.out.println("A.foo");
}
}
public static class B extends A {
@Override
public void foo() {
System.out.println("B.foo");
}
}
public static class C extends B {
@Override
public void foo() {
System.out.println("C.foo");
}
}
public static class Main {
public static void main(String[] args) {
new B();
}
}
public static class MainAllInstantiated {
public static void main(String[] args) {
new A();
new B();
new C();
}
}
}