blob: d1c3ac168e265e7b44eeac6a09c3b683077b102f [file] [log] [blame]
// Copyright (c) 2017, 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.debug;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.debug.DebugTestConfig.RuntimeKind;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
/** Tests renaming of class and method names and corresponding proguard map output. */
@RunWith(Parameterized.class)
public class MinificationTest extends DebugTestBase {
private static final String SOURCE_FILE = "Minified.java";
@Parameterized.Parameters(name = "backend:{0} minification:{1} proguardMap:{2}")
public static Collection minificationControl() {
ImmutableList.Builder<Object> builder = ImmutableList.builder();
for (RuntimeKind kind : RuntimeKind.values()) {
for (MinifyMode mode : MinifyMode.values()) {
builder.add((Object) new Object[] {kind, mode, false});
if (mode.isMinify()) {
builder.add((Object) new Object[] {kind, mode, true});
}
}
}
return builder.build();
}
@Rule
public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
private final RuntimeKind runtimeKind;
private final MinifyMode minificationMode;
private final boolean writeProguardMap;
public MinificationTest(
RuntimeKind runtimeKind, MinifyMode minificationMode, boolean writeProguardMap) {
this.runtimeKind = runtimeKind;
this.minificationMode = minificationMode;
this.writeProguardMap = writeProguardMap;
}
private boolean minifiedNames() {
return minificationMode.isMinify() && !writeProguardMap;
}
private DebugTestConfig getTestConfig() throws Throwable {
List<String> proguardConfigurations = Collections.emptyList();
ImmutableList.Builder<String> proguardConfigurationBuilder = ImmutableList.builder();
if (!minificationMode.isMinify()) {
proguardConfigurationBuilder.add("-dontobfuscate");
} else if (minificationMode.isAggressive()) {
proguardConfigurationBuilder.add("-overloadaggressively");
}
proguardConfigurationBuilder.add(
"-keep public class Minified { public static void main(java.lang.String[]); }");
proguardConfigurationBuilder.add("-keepattributes SourceFile");
proguardConfigurationBuilder.add("-keepattributes LineNumberTable");
proguardConfigurations = proguardConfigurationBuilder.build();
Path outputPath = temp.getRoot().toPath().resolve("classes.zip");
Path proguardMap = writeProguardMap ? temp.getRoot().toPath().resolve("proguard.map") : null;
OutputMode outputMode =
runtimeKind == RuntimeKind.CF ? OutputMode.ClassFile : OutputMode.DexIndexed;
R8Command.Builder builder =
R8Command.builder()
.addProgramFiles(DEBUGGEE_JAR)
.setOutput(outputPath, outputMode)
.setMode(CompilationMode.DEBUG);
if (runtimeKind == RuntimeKind.DEX) {
AndroidApiLevel minSdk = ToolHelper.getMinApiLevelForDexVm();
builder.setMinApiLevel(minSdk.getLevel()).addLibraryFiles(ToolHelper.getAndroidJar(minSdk));
} else if (runtimeKind == RuntimeKind.CF) {
builder.addLibraryFiles(ToolHelper.getJava8RuntimeJar());
}
if (proguardMap != null) {
builder.setProguardMapOutputPath(proguardMap);
}
if (!proguardConfigurations.isEmpty()) {
builder.addProguardConfiguration(proguardConfigurations, Origin.unknown());
}
// Disable line number optimization if we're not using a Proguard map.
ToolHelper.runR8(
builder.build(),
proguardMap == null
? (oc -> oc.lineNumberOptimization = LineNumberOptimization.OFF)
: null);
switch (runtimeKind) {
case CF:
{
CfDebugTestConfig config = new CfDebugTestConfig(outputPath);
config.setProguardMap(proguardMap);
return config;
}
case DEX:
{
DexDebugTestConfig config = new DexDebugTestConfig(outputPath);
config.setProguardMap(proguardMap);
return config;
}
default:
throw new Unreachable();
}
}
@Test
public void testBreakInMainClass() throws Throwable {
final String className = "Minified";
final String methodName = minifiedNames() ? "a" : "test";
final String signature = "()V";
final String innerClassName = minifiedNames() ? "a" : "Minified$Inner";
final String innerMethodName = minifiedNames() ? "a" : "innerTest";
final String innerSignature = "()I";
DebugTestConfig config = getTestConfig();
checkStructure(
config,
className,
MethodSignature.fromSignature(methodName, signature),
innerClassName,
MethodSignature.fromSignature(innerMethodName, innerSignature));
runDebugTest(
config,
className,
breakpoint(className, methodName, signature),
run(),
checkMethod(className, methodName, signature),
checkLine(SOURCE_FILE, 14),
stepOver(INTELLIJ_FILTER),
checkMethod(className, methodName, signature),
checkLine(SOURCE_FILE, 15),
stepInto(INTELLIJ_FILTER),
checkMethod(innerClassName, innerMethodName, innerSignature),
checkLine(SOURCE_FILE, 8),
run());
}
@Test
public void testBreakInPossiblyRenamedClass() throws Throwable {
final String className = "Minified";
final String innerClassName = minifiedNames() ? "a" : "Minified$Inner";
final String innerMethodName = minifiedNames() ? "a" : "innerTest";
final String innerSignature = "()I";
DebugTestConfig config = getTestConfig();
checkStructure(
config,
className,
innerClassName,
MethodSignature.fromSignature(innerMethodName, innerSignature));
runDebugTest(
config,
className,
breakpoint(innerClassName, innerMethodName, innerSignature),
run(),
checkMethod(innerClassName, innerMethodName, innerSignature),
checkLine(SOURCE_FILE, 8),
run());
}
private void checkStructure(
DebugTestConfig config,
String className,
MethodSignature method,
String innerClassName,
MethodSignature innerMethod)
throws Throwable {
Path proguardMap = config.getProguardMap();
String mappingFile = proguardMap == null ? null : proguardMap.toString();
CodeInspector inspector = new CodeInspector(config.getPaths(), mappingFile, null);
ClassSubject clazz = inspector.clazz(className);
assertTrue(clazz.isPresent());
if (method != null) {
assertTrue(clazz.method(method).isPresent());
}
ClassSubject innerClass = inspector.clazz(innerClassName);
assertTrue(innerClass.isPresent());
assertTrue(innerClass.method(innerMethod).isPresent());
}
private void checkStructure(
DebugTestConfig config, String className, String innerClassName, MethodSignature innerMethod)
throws Throwable {
checkStructure(config, className, null, innerClassName, innerMethod);
}
}