blob: 3d9645de4be8dbb841c8b14d22258c1574a66730 [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.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
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.AndroidApp;
import com.android.tools.r8.utils.StringUtils;
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.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class ValidNameConflictTest extends JasminTestBase {
private static final String REPEATED_NAME = "hopeTheresNoSuchNameInRuntimeLibraries";
private final Backend backend;
@Parameterized.Parameters(name = "Backend: {0}")
public static Backend[] data() {
return ToolHelper.getBackends();
}
public ValidNameConflictTest(Backend backend) {
this.backend = backend;
}
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 JasminBuilder buildFieldNameConflictClassFile() {
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;
}
private ProcessResult runRaw(AndroidApp app, String main) throws IOException {
if (backend == Backend.DEX) {
return runOnArtRaw(app, main);
} else {
assert backend == Backend.CF;
return runOnJavaRawNoVerify(app, main, Collections.emptyList());
}
}
@Test
public void remainFieldNameConflict_keepRules() throws Exception {
JasminBuilder builder = buildFieldNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
List<String> pgConfigs = ImmutableList.of(
"-keep public class " + CLASS_NAME + " {\n"
+ " public static void main(java.lang.String[]);\n"
+ " static <fields>;"
+ "}\n"
+ "-printmapping\n",
"-dontshrink");
AndroidApp app = compileWithR8(builder, pgConfigs, null, backend);
CodeInspector codeInspector = new CodeInspector(app);
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());
ProcessResult output = runRaw(app, CLASS_NAME);
assertEquals(0, output.exitCode);
assertEquals(javaOutput.stdout, output.stdout);
}
@Test
public void resolveFieldNameConflict_no_options() throws Exception {
JasminBuilder builder = buildFieldNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
List<String> pgConfigs = ImmutableList.of(
keepMainProguardConfiguration(CLASS_NAME),
"-dontshrink");
AndroidApp app = compileWithR8(builder, pgConfigs, null, backend);
CodeInspector codeInspector = new CodeInspector(app);
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());
ProcessResult output = runRaw(app, CLASS_NAME);
assertEquals(0, output.exitCode);
assertEquals(javaOutput.stdout, output.stdout);
}
@Test
public void remainFieldNameConflict_overloadaggressively() throws Exception {
JasminBuilder builder = buildFieldNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
List<String> pgConfigs = ImmutableList.of(
keepMainProguardConfiguration(CLASS_NAME),
"-overloadaggressively",
"-dontshrink");
AndroidApp app = compileWithR8(builder, pgConfigs, null, backend);
CodeInspector codeInspector = new CodeInspector(app);
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());
assertEquals(f1.getFinalName(), f2.getFinalName());
ProcessResult output = runRaw(app, CLASS_NAME);
assertEquals(0, output.exitCode);
assertEquals(javaOutput.stdout, output.stdout);
}
private JasminBuilder buildMethodNameConflictClassFile() {
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 3",
".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;
}
@Test
public void remainMethodNameConflict_keepRules() throws Exception {
JasminBuilder builder = buildMethodNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
List<String> pgConfigs = ImmutableList.of(
"-keep class " + ANOTHER_CLASS + " {\n"
+ " static <methods>;"
+ "}\n",
keepMainProguardConfiguration(CLASS_NAME),
"-dontshrink");
AndroidApp app = compileWithR8(builder, pgConfigs, null, backend);
CodeInspector codeInspector = new CodeInspector(app);
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());
ProcessResult output = runRaw(app, CLASS_NAME);
assertEquals(0, output.exitCode);
assertEquals(javaOutput.stdout, output.stdout);
}
@Test
public void resolveMethodNameConflict_no_options() throws Exception {
JasminBuilder builder = buildMethodNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
List<String> pgConfigs = ImmutableList.of(
keepMainProguardConfiguration(CLASS_NAME),
"-dontshrink");
AndroidApp app = compileWithR8(builder, pgConfigs, null, backend);
CodeInspector codeInspector = new CodeInspector(app);
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());
ProcessResult output = runRaw(app, CLASS_NAME);
assertEquals(0, output.exitCode);
assertEquals(
new HashSet<>(StringUtils.splitLines(javaOutput.stdout)),
new HashSet<>(StringUtils.splitLines(output.stdout)));
}
@Test
public void remainMethodNameConflict_overloadaggressively() throws Exception {
JasminBuilder builder = buildMethodNameConflictClassFile();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
List<String> pgConfigs = ImmutableList.of(
keepMainProguardConfiguration(CLASS_NAME),
"-overloadaggressively",
"-dontshrink");
AndroidApp app = compileWithR8(builder, pgConfigs, null, backend);
CodeInspector codeInspector = new CodeInspector(app);
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());
if (backend == Backend.DEX) {
assertNotEquals(m1.getFinalName(), m2.getFinalName());
} else {
assertEquals(m1.getFinalName(), m2.getFinalName());
}
ProcessResult output = runRaw(app, CLASS_NAME);
assertEquals(0, output.exitCode);
assertEquals(javaOutput.stdout, output.stdout);
}
private JasminBuilder buildMethodNameConflictInHierarchy() {
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 3",
".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;
}
@Test
public void remainMethodNameConflictInHierarchy_keepRules() throws Exception {
JasminBuilder builder = buildMethodNameConflictInHierarchy();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
List<String> pgConfigs = ImmutableList.of(
"-keep class " + ANOTHER_CLASS + " {\n"
+ " <methods>;"
+ "}\n",
keepMainProguardConfiguration(CLASS_NAME),
"-dontshrink");
AndroidApp app = compileWithR8(builder, pgConfigs, null, backend);
CodeInspector codeInspector = new CodeInspector(app);
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());
ProcessResult output = runRaw(app, CLASS_NAME);
assertEquals(0, output.exitCode);
assertEquals(javaOutput.stdout, output.stdout);
}
@Test
public void resolveMethodNameConflictInHierarchy_no_options() throws Exception {
JasminBuilder builder = buildMethodNameConflictInHierarchy();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
List<String> pgConfigs = ImmutableList.of(
keepMainProguardConfiguration(CLASS_NAME),
"-dontshrink");
AndroidApp app = compileWithR8(builder, pgConfigs, null, backend);
CodeInspector codeInspector = new CodeInspector(app);
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());
ProcessResult output = runRaw(app, CLASS_NAME);
assertEquals(0, output.exitCode);
assertEquals(
new HashSet<>(StringUtils.splitLines(javaOutput.stdout)),
new HashSet<>(StringUtils.splitLines(output.stdout)));
}
@Test
public void remainMethodNameConflictInHierarchy_overloadaggressively() throws Exception {
JasminBuilder builder = buildMethodNameConflictInHierarchy();
ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
assertEquals(0, javaOutput.exitCode);
List<String> pgConfigs = ImmutableList.of(
keepMainProguardConfiguration(CLASS_NAME),
"-overloadaggressively",
"-dontshrink");
AndroidApp app = compileWithR8(builder, pgConfigs, null, backend);
CodeInspector codeInspector = new CodeInspector(app);
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());
if (backend == Backend.DEX) {
assertNotEquals(m1.getFinalName(), m2.getFinalName());
} else {
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());
assertTrue(subM1.isRenamed());
MethodSubject subM2 = sub.method("java.lang.Object", REPEATED_NAME, ImmutableList.of());
assertTrue(subM2.isPresent());
assertTrue(subM2.isRenamed());
if (backend == Backend.DEX) {
assertNotEquals(subM1.getFinalName(), subM2.getFinalName());
} else {
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());
ProcessResult output = runRaw(app, CLASS_NAME);
assertEquals(0, output.exitCode);
assertEquals(javaOutput.stdout, output.stdout);
}
}