blob: 6a18bd6d5b85a2f8daaf7f3687be406975bdea08 [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.naming.overloadaggressively;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.android.tools.r8.jasmin.JasminTestBase;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FieldSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class ValidNameConflictTest extends JasminTestBase {
private static final List<String> EXPECTED_OUTPUT =
ImmutableList.of("null", "Expected to be seen at the end.");
private static final String REPEATED_NAME = "hopeTheresNoSuchNameInRuntimeLibraries";
@Parameter(0)
public TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final String CLASS_NAME = "Example";
private final String SUPER_CLASS = "Super";
private final String ANOTHER_CLASS = "Test";
private final String MSG = "Expected to be seen at the end.";
private Iterable<String> buildCodeForVisitingDeclaredMembers(
Iterable<String> prologue, Iterable<String> argumentLoadingAndCall) {
return Iterables.concat(
prologue,
ImmutableList.of(
" astore_0", // Member[]
" aload_0",
" arraylength",
" istore_1", // Member[].length
" iconst_0",
" istore_2", // counter
"loop:",
" iload_2",
" iload_1",
" if_icmpge end",
" aload_0",
" iload_2",
" aaload", // Member[counter]
" astore_3",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" aload_3"),
argumentLoadingAndCall,
ImmutableList.of(
" invokevirtual java/io/PrintStream/println(Ljava/lang/Object;)V",
" iinc 2 1", // counter++
" goto loop",
"end:",
" return"));
}
private List<byte[]> buildFieldNameConflictClassFile() throws Exception {
JasminBuilder builder = new JasminBuilder();
ClassBuilder classBuilder = builder.addClass(CLASS_NAME);
classBuilder.addStaticField(REPEATED_NAME, "Ljava/lang/Object;", null);
classBuilder.addStaticField(REPEATED_NAME, "Ljava/lang/String;", "\"" + MSG + "\"");
classBuilder.addMainMethod(
buildCodeForVisitingDeclaredMembers(
ImmutableList.of(
".limit stack 3",
".limit locals 4",
" ldc " + CLASS_NAME,
" invokevirtual java/lang/Class/getDeclaredFields()[Ljava/lang/reflect/Field;"),
ImmutableList.of(
" aconst_null",
" invokevirtual"
+ " java/lang/reflect/Field/get(Ljava/lang/Object;)Ljava/lang/Object;")));
return builder.buildClasses();
}
@Test
public void remainFieldNameConflict_keepRules() throws Exception {
List<byte[]> programClassFileData = buildFieldNameConflictClassFile();
if (parameters.isCfRuntime()) {
testForJvm(parameters)
.addProgramClassFileData(programClassFileData)
.noVerify()
.run(parameters.getRuntime(), CLASS_NAME)
.assertSuccessWithOutputLines(EXPECTED_OUTPUT);
}
R8TestRunResult runResult =
testForR8(parameters.getBackend())
.addProgramClassFileData(programClassFileData)
.addKeepMainRule(CLASS_NAME)
.addKeepRules("-keep class " + CLASS_NAME + " { static <fields>; }")
.addDontShrink()
.setMinApi(parameters)
.compile()
.applyIf(parameters.isCfRuntime(), TestCompileResult::noVerify)
.run(parameters.getRuntime(), CLASS_NAME)
.assertSuccessWithOutputLines(EXPECTED_OUTPUT);
CodeInspector codeInspector = runResult.inspector();
ClassSubject clazz = codeInspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
FieldSubject f1 = clazz.field("java.lang.String", REPEATED_NAME);
assertTrue(f1.isPresent());
assertFalse(f1.isRenamed());
FieldSubject f2 = clazz.field("java.lang.Object", REPEATED_NAME);
assertTrue(f2.isPresent());
assertFalse(f2.isRenamed());
assertEquals(f1.getFinalName(), f2.getFinalName());
}
@Test
public void resolveFieldNameConflict_no_options() throws Exception {
List<byte[]> programClassFileData = buildFieldNameConflictClassFile();
if (parameters.isCfRuntime()) {
testForJvm(parameters)
.addProgramClassFileData(programClassFileData)
.noVerify()
.run(parameters.getRuntime(), CLASS_NAME)
.assertSuccessWithOutputLines(EXPECTED_OUTPUT);
}
R8TestRunResult runResult =
testForR8(parameters.getBackend())
.addProgramClassFileData(programClassFileData)
.addKeepMainRule(CLASS_NAME)
.addDontShrink()
.setMinApi(parameters)
.compile()
.applyIf(parameters.isCfRuntime(), TestCompileResult::noVerify)
.run(parameters.getRuntime(), CLASS_NAME)
.assertSuccessWithOutputLines(EXPECTED_OUTPUT);
CodeInspector codeInspector = runResult.inspector();
ClassSubject clazz = codeInspector.clazz(CLASS_NAME);
assertTrue(clazz.isPresent());
FieldSubject f1 = clazz.field("java.lang.String", REPEATED_NAME);
assertTrue(f1.isPresent());
assertTrue(f1.isRenamed());
FieldSubject f2 = clazz.field("java.lang.Object", REPEATED_NAME);
assertTrue(f2.isPresent());
assertTrue(f2.isRenamed());
assertNotEquals(f1.getFinalName(), f2.getFinalName());
}
private List<byte[]> buildMethodNameConflictClassFile() throws Exception {
JasminBuilder builder = new JasminBuilder();
ClassBuilder classBuilder = builder.addClass(ANOTHER_CLASS);
classBuilder.addStaticMethod(
REPEATED_NAME, ImmutableList.of(), "Ljava/lang/Object;", "aconst_null", "areturn");
classBuilder.addStaticMethod(
REPEATED_NAME, ImmutableList.of(), "Ljava/lang/String;", "ldc \"" + MSG + "\"", "areturn");
classBuilder = builder.addClass(CLASS_NAME);
classBuilder.addMainMethod(
buildCodeForVisitingDeclaredMembers(
ImmutableList.of(
".limit stack 4",
".limit locals 4",
" ldc " + ANOTHER_CLASS,
" invokevirtual java/lang/Class/getDeclaredMethods()[Ljava/lang/reflect/Method;"),
ImmutableList.of(
" aconst_null",
" aconst_null",
" checkcast [Ljava/lang/Object;",
" invokevirtual java/lang/reflect/Method/invoke"
+ "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")));
return builder.buildClasses();
}
@Test
public void remainMethodNameConflict_keepRules() throws Exception {
List<byte[]> programClassFileData = buildMethodNameConflictClassFile();
if (parameters.isCfRuntime()) {
testForJvm(parameters)
.addProgramClassFileData(programClassFileData)
.noVerify()
.run(parameters.getRuntime(), CLASS_NAME)
.assertSuccessWithOutputLines(EXPECTED_OUTPUT);
}
R8TestRunResult runResult =
testForR8(parameters.getBackend())
.addProgramClassFileData(programClassFileData)
.addKeepMainRule(CLASS_NAME)
.addKeepRules("-keep class " + ANOTHER_CLASS + " { static <methods>; }")
.addDontShrink()
.setMinApi(parameters)
.compile()
.applyIf(parameters.isCfRuntime(), TestCompileResult::noVerify)
.run(parameters.getRuntime(), CLASS_NAME)
.assertSuccessWithOutputLines(EXPECTED_OUTPUT);
CodeInspector codeInspector = runResult.inspector();
ClassSubject clazz = codeInspector.clazz(ANOTHER_CLASS);
assertTrue(clazz.isPresent());
MethodSubject m1 = clazz.method("java.lang.String", REPEATED_NAME, ImmutableList.of());
assertTrue(m1.isPresent());
assertFalse(m1.isRenamed());
MethodSubject m2 = clazz.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
assertTrue(m2.isPresent());
assertFalse(m2.isRenamed());
assertEquals(m1.getFinalName(), m2.getFinalName());
}
@Test
public void resolveMethodNameConflict_no_options() throws Exception {
List<byte[]> programClassFileData = buildMethodNameConflictClassFile();
if (parameters.isCfRuntime()) {
testForJvm(parameters)
.addProgramClassFileData(programClassFileData)
.noVerify()
.run(parameters.getRuntime(), CLASS_NAME)
.assertSuccessWithOutputLines(EXPECTED_OUTPUT);
}
R8TestRunResult runResult =
testForR8(parameters.getBackend())
.addProgramClassFileData(programClassFileData)
.addKeepMainRule(CLASS_NAME)
.addDontShrink()
.setMinApi(parameters)
.compile()
.applyIf(parameters.isCfRuntime(), TestCompileResult::noVerify)
.run(parameters.getRuntime(), CLASS_NAME)
.applyIf(
parameters.isCfRuntime(),
r -> r.assertSuccessWithOutputLines(ListUtils.reverse(EXPECTED_OUTPUT)),
r -> r.assertSuccessWithOutputLines(EXPECTED_OUTPUT));
CodeInspector codeInspector = runResult.inspector();
ClassSubject clazz = codeInspector.clazz(ANOTHER_CLASS);
assertTrue(clazz.isPresent());
MethodSubject m1 = clazz.method("java.lang.String", REPEATED_NAME, ImmutableList.of());
assertTrue(m1.isPresent());
assertTrue(m1.isRenamed());
MethodSubject m2 = clazz.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
assertTrue(m2.isPresent());
assertTrue(m2.isRenamed());
assertNotEquals(m1.getFinalName(), m2.getFinalName());
}
private List<byte[]> buildMethodNameConflictInHierarchy() throws Exception {
JasminBuilder builder = new JasminBuilder();
ClassBuilder classBuilder = builder.addClass(SUPER_CLASS);
classBuilder.addVirtualMethod(
REPEATED_NAME, ImmutableList.of(), "Ljava/lang/Object;", "aconst_null", "areturn");
classBuilder.addVirtualMethod(
REPEATED_NAME, ImmutableList.of(), "Ljava/lang/String;", "ldc \"" + MSG + "\"", "areturn");
classBuilder = builder.addClass(ANOTHER_CLASS, SUPER_CLASS);
classBuilder.addVirtualMethod(
REPEATED_NAME,
ImmutableList.of(),
"Ljava/lang/Object;",
"aload_0",
"invokespecial " + SUPER_CLASS + "/" + REPEATED_NAME + "()Ljava/lang/Object;",
"areturn");
classBuilder.addVirtualMethod(
REPEATED_NAME,
ImmutableList.of(),
"Ljava/lang/String;",
"aload_0",
"invokespecial " + SUPER_CLASS + "/" + REPEATED_NAME + "()Ljava/lang/String;",
"areturn");
classBuilder = builder.addClass(CLASS_NAME);
classBuilder.addMainMethod(
buildCodeForVisitingDeclaredMembers(
ImmutableList.of(
".limit stack 4",
".limit locals 5",
" new " + ANOTHER_CLASS,
" dup",
" invokespecial " + ANOTHER_CLASS + "/<init>()V",
" astore 4",
" ldc " + ANOTHER_CLASS,
" invokevirtual java/lang/Class/getDeclaredMethods()[Ljava/lang/reflect/Method;"),
ImmutableList.of(
" aload 4", // instance
" aconst_null",
" checkcast [Ljava/lang/Object;",
" invokevirtual java/lang/reflect/Method/invoke"
+ "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")));
return builder.buildClasses();
}
@Test
public void remainMethodNameConflictInHierarchy_keepRules() throws Exception {
List<byte[]> programClassFileData = buildMethodNameConflictInHierarchy();
if (parameters.isCfRuntime()) {
testForJvm(parameters)
.addProgramClassFileData(programClassFileData)
.noVerify()
.run(parameters.getRuntime(), CLASS_NAME)
.assertSuccessWithOutputLines(EXPECTED_OUTPUT);
}
R8TestRunResult runResult =
testForR8(parameters.getBackend())
.addProgramClassFileData(programClassFileData)
.addKeepMainRule(CLASS_NAME)
.addKeepClassAndMembersRules(ANOTHER_CLASS)
.addDontShrink()
.setMinApi(parameters)
.compile()
.applyIf(parameters.isCfRuntime(), TestCompileResult::noVerify)
.run(parameters.getRuntime(), CLASS_NAME)
.assertSuccessWithOutputLines(EXPECTED_OUTPUT);
CodeInspector codeInspector = runResult.inspector();
ClassSubject sup = codeInspector.clazz(SUPER_CLASS);
assertTrue(sup.isPresent());
MethodSubject m1 = sup.method("java.lang.String", REPEATED_NAME, ImmutableList.of());
assertTrue(m1.isPresent());
assertFalse(m1.isRenamed());
MethodSubject m2 = sup.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
assertTrue(m2.isPresent());
assertFalse(m2.isRenamed());
assertEquals(m1.getFinalName(), m2.getFinalName());
ClassSubject sub = codeInspector.clazz(ANOTHER_CLASS);
assertTrue(sub.isPresent());
MethodSubject subM1 = sub.method("java.lang.String", REPEATED_NAME, ImmutableList.of());
assertTrue(subM1.isPresent());
assertFalse(subM1.isRenamed());
MethodSubject subM2 = sub.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
assertTrue(subM2.isPresent());
assertFalse(subM2.isRenamed());
assertEquals(subM1.getFinalName(), subM2.getFinalName());
// No matter what, overloading methods should be renamed to the same name.
assertEquals(m1.getFinalName(), subM1.getFinalName());
assertEquals(m2.getFinalName(), subM2.getFinalName());
}
@Test
public void resolveMethodNameConflictInHierarchy_no_options() throws Exception {
List<byte[]> programClassFileData = buildMethodNameConflictInHierarchy();
if (parameters.isCfRuntime()) {
testForJvm(parameters)
.addProgramClassFileData(programClassFileData)
.noVerify()
.run(parameters.getRuntime(), CLASS_NAME)
.assertSuccessWithOutputLines(EXPECTED_OUTPUT);
}
R8TestRunResult runResult =
testForR8(parameters.getBackend())
.addProgramClassFileData(programClassFileData)
.addKeepMainRule(CLASS_NAME)
.addDontShrink()
.setMinApi(parameters)
.compile()
.applyIf(parameters.isCfRuntime(), TestCompileResult::noVerify)
.run(parameters.getRuntime(), CLASS_NAME)
.applyIf(
parameters.isCfRuntime(),
r -> r.assertSuccessWithOutputLines(ListUtils.reverse(EXPECTED_OUTPUT)),
r -> r.assertSuccessWithOutputLines(EXPECTED_OUTPUT));
CodeInspector codeInspector = runResult.inspector();
ClassSubject sup = codeInspector.clazz(SUPER_CLASS);
assertTrue(sup.isPresent());
MethodSubject m1 = sup.method("java.lang.String", REPEATED_NAME, ImmutableList.of());
assertTrue(m1.isPresent());
assertTrue(m1.isRenamed());
MethodSubject m2 = sup.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
assertTrue(m2.isPresent());
assertTrue(m2.isRenamed());
assertNotEquals(m1.getFinalName(), m2.getFinalName());
ClassSubject sub = codeInspector.clazz(ANOTHER_CLASS);
assertTrue(sub.isPresent());
MethodSubject subM1 = sub.method("java.lang.String", REPEATED_NAME, ImmutableList.of());
assertTrue(subM1.isPresent());
assertTrue(subM1.isRenamed());
MethodSubject subM2 = sub.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
assertTrue(subM2.isPresent());
assertTrue(subM2.isRenamed());
assertNotEquals(subM1.getFinalName(), subM2.getFinalName());
// No matter what, overloading methods should be renamed to the same name.
assertEquals(m1.getFinalName(), subM1.getFinalName());
assertEquals(m2.getFinalName(), subM2.getFinalName());
}
}