blob: 7bd9cc9f74d0379c24fd890eb82569b2896cb1df [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.assertFalse;
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.AppView;
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.LookupResult;
import com.android.tools.r8.graph.ResolutionResult;
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.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
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 AppView<AppInfoWithLiveness> appView;
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> virtualTargetHolders) {
this.methodName = methodName;
this.invokeReceiver = invokeReceiver;
this.singleTargetHolderOrNull = singleTargetHolderOrNull;
this.virtualTargetHolders = virtualTargetHolders;
}
@BeforeClass
public static void computeAppInfo() throws Exception {
appView = computeAppViewWithLiveness(readClassesAndAsmDump(CLASSES, ASM_CLASSES), Main.class);
appInfo = appView.appInfo();
}
private static Object[] noVirtualSingleTarget(String name, Class<?> receiverAndTarget) {
return new Object[] {name, receiverAndTarget, null, Collections.emptyList()};
}
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),
manyTargets(
"abstractTargetAtTop",
AbstractTopClass.class,
SubSubClassOne.class,
SubSubClassTwo.class),
singleTargetWithAbstracts(
"overridenInAbstractClassOnly", AbstractTopClass.class, AbstractTopClass.class),
onlyUnreachableTargets("overridenInAbstractClassOnly", SubSubClassThree.class),
manyTargets(
"overriddenInTwoSubTypes",
AbstractTopClass.class,
SubSubClassOne.class,
SubSubClassTwo.class),
manyTargets(
"definedInTwoSubTypes",
AbstractTopClass.class,
SubSubClassOne.class,
SubSubClassTwo.class),
onlyUnreachableTargets("staticMethod", AbstractTopClass.class),
manyTargets("overriddenInTwoSubTypes", OtherAbstractTopClass.class),
manyTargets("abstractOverriddenInTwoSubTypes", OtherAbstractTopClass.class),
manyTargets("overridesOnDifferentLevels", OtherAbstractTopClass.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),
singleTarget(
"overriddenInOtherInterface", AbstractTopClass.class, InterfaceWithDefault.class),
manyTargets("abstractMethod", ThirdAbstractTopClass.class),
noVirtualSingleTarget("instanceMethod", ThirdAbstractTopClass.class),
noVirtualSingleTarget("otherInstanceMethod", ThirdAbstractTopClass.class),
});
}
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> virtualTargetHolders;
@Test
public void lookupSingleTarget() {
DexMethod method = buildNullaryVoidMethod(invokeReceiver, methodName, appInfo.dexItemFactory());
Assert.assertNotNull(
appInfo.resolveMethod(toType(invokeReceiver, appInfo), method).getSingleTarget());
DexEncodedMethod singleVirtualTarget =
appInfo.lookupSingleVirtualTarget(method, method.holder, false);
if (singleTargetHolderOrNull == null) {
Assert.assertNull(singleVirtualTarget);
} else {
Assert.assertNotNull(singleVirtualTarget);
Assert.assertEquals(toType(singleTargetHolderOrNull, appInfo), singleVirtualTarget.holder());
}
}
@Test
public void lookupVirtualTargets() throws IOException {
DexMethod method = buildNullaryVoidMethod(invokeReceiver, methodName, appInfo.dexItemFactory());
Assert.assertNotNull(
appInfo.resolveMethod(toType(invokeReceiver, appInfo), method).getSingleTarget());
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
if (resolutionResult.isVirtualTarget()) {
LookupResult lookupResult =
resolutionResult.lookupVirtualDispatchTargets(
appView.definitionForProgramType(buildType(Main.class, appView.dexItemFactory())),
appInfo);
assertTrue(lookupResult.isLookupResultSuccess());
assertFalse(lookupResult.asLookupResultSuccess().hasLambdaTargets());
Set<DexType> targetHolders = new HashSet<>();
lookupResult
.asLookupResultSuccess()
.forEach(
methodTarget -> targetHolders.add(methodTarget.getHolder().type),
lambdaTarget -> {
assert false;
});
Assert.assertEquals(virtualTargetHolders.size(), targetHolders.size());
assertTrue(
virtualTargetHolders.stream()
.map(t -> toType(t, appInfo))
.allMatch(targetHolders::contains));
} else {
assertTrue(virtualTargetHolders.isEmpty());
}
}
}