blob: e290d518b89497592e318a00686e0d8d5bbaa8b4 [file] [log] [blame]
// Copyright (c) 2018, 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;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.AsmTestBase;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.resolution.singletarget.Main;
import com.android.tools.r8.resolution.singletarget.one.AbstractSubClass;
import com.android.tools.r8.resolution.singletarget.one.AbstractTopClass;
import com.android.tools.r8.resolution.singletarget.one.InterfaceWithDefault;
import com.android.tools.r8.resolution.singletarget.one.IrrelevantInterfaceWithDefaultDump;
import com.android.tools.r8.resolution.singletarget.one.SubSubClassOne;
import com.android.tools.r8.resolution.singletarget.one.SubSubClassThree;
import com.android.tools.r8.resolution.singletarget.one.SubSubClassTwo;
import com.android.tools.r8.resolution.singletarget.three.ThirdAbstractTopClass;
import com.android.tools.r8.resolution.singletarget.three.ThirdSubClassOne;
import com.android.tools.r8.resolution.singletarget.three.ThirdSubClassTwoDump;
import com.android.tools.r8.resolution.singletarget.two.OtherAbstractSubClassOne;
import com.android.tools.r8.resolution.singletarget.two.OtherAbstractSubClassTwo;
import com.android.tools.r8.resolution.singletarget.two.OtherAbstractTopClass;
import com.android.tools.r8.resolution.singletarget.two.OtherSubSubClassOne;
import com.android.tools.r8.resolution.singletarget.two.OtherSubSubClassTwo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.Assert;
import org.junit.BeforeClass;
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 SingleTargetLookupTest extends AsmTestBase {
/**
* Initialized in @Before rule.
*/
public static AppInfoWithLiveness appInfo;
public static List<Class<?>> CLASSES =
ImmutableList.of(
InterfaceWithDefault.class,
AbstractTopClass.class,
AbstractSubClass.class,
SubSubClassOne.class,
SubSubClassTwo.class,
SubSubClassThree.class,
OtherAbstractTopClass.class,
OtherAbstractSubClassOne.class,
OtherAbstractSubClassTwo.class,
OtherSubSubClassOne.class,
OtherSubSubClassTwo.class,
ThirdAbstractTopClass.class,
ThirdSubClassOne.class,
Main.class);
public static List<byte[]> ASM_CLASSES = ImmutableList.of(
getBytesFromAsmClass(IrrelevantInterfaceWithDefaultDump::dump),
getBytesFromAsmClass(ThirdSubClassTwoDump::dump)
);
public SingleTargetLookupTest(
String methodName,
Class invokeReceiver,
Class singleTargetHolderOrNull,
List<Class> allTargetHolders) {
this.methodName = methodName;
this.invokeReceiver = invokeReceiver;
this.singleTargetHolderOrNull = singleTargetHolderOrNull;
this.allTargetHolders = allTargetHolders;
}
@BeforeClass
public static void computeAppInfo() throws Exception {
appInfo =
computeAppViewWithLiveness(readClassesAndAsmDump(CLASSES, ASM_CLASSES), Main.class)
.appInfo();
}
private static Object[] singleTarget(String name, Class<?> receiverAndTarget) {
return new Object[]{name, receiverAndTarget, receiverAndTarget,
Collections.singletonList(receiverAndTarget)};
}
private static Object[] singleTarget(String name, Class<?> receiver, Class<?> target) {
return new Object[]{name, receiver, target, Collections.singletonList(target)};
}
private static Object[] singleTargetWithAbstracts(String name, Class<?> receiver,
Class<?> target, Class<?>... extras) {
return new Object[]{name, receiver, target,
ImmutableList.builder().add(target).add((Object[]) extras).build()};
}
private static Object[] manyTargets(String name, Class<?> receiver, Class<?>... targets) {
return new Object[]{name, receiver, null, ImmutableList.copyOf(targets)};
}
private static Object[] onlyUnreachableTargets(String name, Class<?> receiver,
Class<?>... targets) {
return new Object[]{name, receiver, null, ImmutableList.copyOf(targets)};
}
@Parameters(name = "{1}.{0} -> {2}")
public static List<Object[]> getData() {
return ImmutableList.copyOf(
new Object[][] {
singleTarget("singleTargetAtTop", AbstractTopClass.class),
singleTargetWithAbstracts(
"singleShadowingOverride",
AbstractTopClass.class,
AbstractSubClass.class,
AbstractTopClass.class),
manyTargets(
"abstractTargetAtTop",
AbstractTopClass.class,
AbstractTopClass.class,
SubSubClassOne.class,
SubSubClassTwo.class),
singleTargetWithAbstracts(
"overridenInAbstractClassOnly",
AbstractTopClass.class,
AbstractTopClass.class,
SubSubClassThree.class),
onlyUnreachableTargets(
"overridenInAbstractClassOnly", SubSubClassThree.class, SubSubClassThree.class),
manyTargets(
"overriddenInTwoSubTypes",
AbstractTopClass.class,
AbstractTopClass.class,
SubSubClassOne.class,
SubSubClassTwo.class),
manyTargets(
"definedInTwoSubTypes",
AbstractTopClass.class,
AbstractTopClass.class,
SubSubClassOne.class,
SubSubClassTwo.class),
onlyUnreachableTargets("staticMethod", AbstractTopClass.class),
manyTargets(
"overriddenInTwoSubTypes",
OtherAbstractTopClass.class,
OtherAbstractTopClass.class,
OtherSubSubClassOne.class,
OtherSubSubClassTwo.class),
manyTargets(
"abstractOverriddenInTwoSubTypes",
OtherAbstractTopClass.class,
OtherAbstractTopClass.class,
OtherSubSubClassOne.class,
OtherSubSubClassTwo.class),
manyTargets(
"overridesOnDifferentLevels",
OtherAbstractTopClass.class,
OtherAbstractTopClass.class,
OtherSubSubClassOne.class,
OtherAbstractSubClassTwo.class),
singleTarget("defaultMethod", AbstractTopClass.class, InterfaceWithDefault.class),
manyTargets(
"overriddenDefault",
AbstractTopClass.class,
InterfaceWithDefault.class,
SubSubClassTwo.class),
singleTarget("overriddenDefault", SubSubClassTwo.class),
singleTarget("overriddenByIrrelevantInterface", AbstractTopClass.class),
singleTarget(
"overriddenByIrrelevantInterface", SubSubClassOne.class, AbstractTopClass.class),
manyTargets(
"overriddenInOtherInterface",
AbstractTopClass.class,
InterfaceWithDefault.class),
manyTargets(
"abstractMethod",
ThirdAbstractTopClass.class,
ThirdAbstractTopClass.class,
ThirdSubClassOne.class),
singleTarget("instanceMethod", ThirdAbstractTopClass.class, ThirdAbstractTopClass.class),
singleTarget(
"otherInstanceMethod", ThirdAbstractTopClass.class, ThirdAbstractTopClass.class),
});
}
public static DexMethod buildNullaryVoidMethod(Class clazz, String name, AppInfo appInfo) {
return buildMethod(
Reference.method(Reference.classFromClass(clazz), name, Collections.emptyList(), null),
appInfo.dexItemFactory());
}
private static DexType toType(Class clazz, AppInfo appInfo) {
return buildType(clazz, appInfo.dexItemFactory());
}
private final String methodName;
private final Class invokeReceiver;
private final Class singleTargetHolderOrNull;
private final List<Class> allTargetHolders;
@Test
public void lookupSingleTarget() {
DexMethod method = buildNullaryVoidMethod(invokeReceiver, methodName, appInfo);
Assert.assertNotNull(
appInfo.resolveMethod(toType(invokeReceiver, appInfo), method).getSingleTarget());
DexEncodedMethod singleVirtualTarget = appInfo.lookupSingleVirtualTarget(method, method.holder);
if (singleTargetHolderOrNull == null) {
Assert.assertNull(singleVirtualTarget);
} else {
Assert.assertNotNull(singleVirtualTarget);
Assert.assertEquals(
toType(singleTargetHolderOrNull, appInfo), singleVirtualTarget.method.holder);
}
}
@Test
public void lookupVirtualTargets() {
DexMethod method = buildNullaryVoidMethod(invokeReceiver, methodName, appInfo);
Assert.assertNotNull(
appInfo.resolveMethod(toType(invokeReceiver, appInfo), method).getSingleTarget());
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
if (resolutionResult.isVirtualTarget()) {
Set<DexEncodedMethod> targets = resolutionResult.lookupVirtualTargets(appInfo);
Set<DexType> targetHolders =
targets.stream().map(m -> m.method.holder).collect(Collectors.toSet());
Assert.assertEquals(allTargetHolders.size(), targetHolders.size());
assertTrue(
allTargetHolders.stream().map(t -> toType(t, appInfo)).allMatch(targetHolders::contains));
} else {
assertTrue(allTargetHolders.isEmpty());
}
}
}