Merge "Add a test for minified enum and use of valueOf"
diff --git a/build.gradle b/build.gradle
index a542ea4..e6432e4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1243,6 +1243,8 @@
}
dependsOn getJarsFromSupportLibs
+ // R8.jar is required for running bootstrap tests.
+ dependsOn R8
testLogging.exceptionFormat = 'full'
if (project.hasProperty('print_test_stdout')) {
testLogging.showStandardStreams = true
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 4e7f421..67b6e2d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1259,9 +1259,7 @@
Value argument = invoke.arguments().get(argumentIndex);
assert invoke.outType().verifyCompatible(argument.outType());
invoke.outValue().replaceUsers(argument);
- if (!options.isGeneratingClassFiles()) {
- invoke.setOutValue(null);
- }
+ invoke.setOutValue(null);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 6aa1853..1a23da1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -252,7 +252,7 @@
if (target != null) {
// Remove writes to dead (i.e. never read) fields.
if (!isFieldRead(target, true)) {
- iterator.remove();
+ iterator.removeOrReplaceByDebugLocalRead();
}
}
}
diff --git a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
index fe002ec..68d634e 100644
--- a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
+++ b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
@@ -1354,8 +1354,12 @@
"lang.Character.isWhitespaceI.Character_isWhitespace_A01",
match(runtimes(Runtime.ART_V4_0_4)))
.put("lang.Character.getDirectionalityI.Character_getDirectionality_A01", any())
- .put("lang.Character.UnicodeBlock.ofC.UnicodeBlock_of_A01", any())
- .put("lang.Character.UnicodeBlock.ofI.UnicodeBlock_of_A01", any())
+ .put(
+ "lang.Character.UnicodeBlock.ofC.UnicodeBlock_of_A01",
+ match(artRuntimesFromAndJava(Runtime.ART_V4_4_4)))
+ .put(
+ "lang.Character.UnicodeBlock.ofI.UnicodeBlock_of_A01",
+ match(artRuntimesFromAndJava(Runtime.ART_V4_4_4)))
.put("lang.Character.isLowerCaseI.Character_isLowerCase_A01", anyDexVm())
.put("lang.Process.waitFor.Process_waitFor_A01", anyDexVm())
.put("lang.System.getProperties.System_getProperties_A01", anyDexVm())
@@ -1527,7 +1531,7 @@
match(artRuntimesUpTo(Runtime.ART_V6_0_1)))
.put(
"lang.reflect.AccessibleObject.setAccessibleZ.AccessibleObject_setAccessible_A04",
- match(runtimes(Runtime.ART_V4_4_4, Runtime.JAVA)))
+ match(artRuntimesUpToAndJava(Runtime.ART_V4_4_4)))
.put(
"lang.reflect.AccessibleObject.setAccessible_Ljava_lang_reflect_AccessibleObjectZ.AccessibleObject_setAccessible_A04",
match(artRuntimesUpToAndJava(Runtime.ART_V6_0_1)))
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index e34282d..f5eaa25 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -93,7 +93,7 @@
public static final String SMALI_BUILD_DIR = TESTS_BUILD_DIR + "smali/";
public static final String LINE_SEPARATOR = StringUtils.LINE_SEPARATOR;
- public final static String PATH_SEPARATOR = File.pathSeparator;
+ public static final String PATH_SEPARATOR = File.pathSeparator;
public static final String DEFAULT_DEX_FILENAME = "classes.dex";
public static final String DEFAULT_PROGUARD_MAP_FILE = "proguard.map";
@@ -106,6 +106,8 @@
private static final String PROGUARD6_0_1 = "third_party/proguard/proguard6.0.1/bin/proguard";
private static final String PROGUARD = PROGUARD5_2_1;
+ public static final Path R8_JAR = Paths.get(LIBS_DIR, "r8.jar");
+
public enum DexVm {
ART_4_0_4_TARGET(Version.V4_0_4, Kind.TARGET),
ART_4_0_4_HOST(Version.V4_0_4, Kind.HOST),
@@ -997,7 +999,7 @@
public static ProcessResult forkR8Jar(Path dir, String... args)
throws IOException, InterruptedException {
- String r8Jar = Paths.get(LIBS_DIR, "r8.jar").toAbsolutePath().toString();
+ String r8Jar = R8_JAR.toAbsolutePath().toString();
return forkJavaWithJar(dir, r8Jar, Arrays.asList(args));
}
diff --git a/src/test/java/com/android/tools/r8/cf/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/BootstrapCurrentEqualityTest.java
new file mode 100644
index 0000000..02df3d1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/BootstrapCurrentEqualityTest.java
@@ -0,0 +1,205 @@
+// 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.cf;
+
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static com.google.common.io.ByteStreams.toByteArray;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ArchiveClassFileProvider;
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.codeinspector.ClassHierarchyVerifier;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.base.Charsets;
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class BootstrapCurrentEqualityTest extends TestBase {
+
+ private static final String R8_NAME = "com.android.tools.r8.R8";
+ private static final Path MAIN_KEEP = Paths.get("src/main/keep.txt");
+
+ private static final String HELLO_NAME = "hello.Hello";
+ private static final String[] KEEP_HELLO = {
+ "-keep class " + HELLO_NAME + " {", " public static void main(...);", "}",
+ };
+
+ private class R8Result {
+
+ final ProcessResult processResult;
+ final Path outputJar;
+ final String pgMap;
+
+ R8Result(ProcessResult processResult, Path outputJar, String pgMap) {
+ this.processResult = processResult;
+ this.outputJar = outputJar;
+ this.pgMap = pgMap;
+ }
+
+ @Override
+ public String toString() {
+ return processResult.toString() + "\n\n" + pgMap;
+ }
+ }
+
+ private static Path r8R8Debug;
+ private static Path r8R8Release;
+
+ @ClassRule public static TemporaryFolder testFolder = new TemporaryFolder();
+
+ @BeforeClass
+ public static void beforeAll() throws Exception {
+ r8R8Debug = compileR8(CompilationMode.DEBUG);
+ r8R8Release = compileR8(CompilationMode.RELEASE);
+ }
+
+ private static Path compileR8(CompilationMode mode) throws Exception {
+ // Run R8 on r8.jar.
+ Path output = runR8(ToolHelper.R8_JAR, testFolder.newFolder().toPath(), mode);
+ // Check that all non-abstract classes in the R8'd R8 implement all abstract/interface methods
+ // from their supertypes. This is a sanity check for the tree shaking and minification.
+ AndroidApp app = AndroidApp.builder().addProgramFile(output).build();
+ new ClassHierarchyVerifier(new CodeInspector(app)).run();
+ return output;
+ }
+
+ @Test
+ public void test() throws Exception {
+ Path helloJar = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "hello" + JAR_EXTENSION);
+ ProcessResult runResult = ToolHelper.runJava(helloJar, "hello.Hello");
+ assertEquals(0, runResult.exitCode);
+ compareR8(helloJar, runResult, KEEP_HELLO, "hello.Hello");
+ }
+
+ private void compareR8(Path program, ProcessResult runResult, String[] keep, String... args)
+ throws Exception {
+ R8Result runR8Debug =
+ runExternalR8(ToolHelper.R8_JAR, program, temp.newFolder().toPath(), keep, "--debug");
+ assertEquals(runResult.toString(), ToolHelper.runJava(runR8Debug.outputJar, args).toString());
+ R8Result runR8Release =
+ runExternalR8(ToolHelper.R8_JAR, program, temp.newFolder().toPath(), keep, "--release");
+ assertEquals(runResult.toString(), ToolHelper.runJava(runR8Release.outputJar, args).toString());
+ RunR8AndCheck(r8R8Debug, program, runR8Debug, keep, "--debug");
+ RunR8AndCheck(r8R8Debug, program, runR8Release, keep, "--release");
+ RunR8AndCheck(r8R8Release, program, runR8Debug, keep, "--debug");
+ RunR8AndCheck(r8R8Release, program, runR8Release, keep, "--release");
+ }
+
+ private void RunR8AndCheck(Path r8, Path program, R8Result result, String[] keep, String mode)
+ throws Exception {
+ R8Result runR8R8 = runExternalR8(r8, program, temp.newFolder().toPath(), keep, mode);
+ // Check that the process outputs (exit code, stdout, stderr) are the same.
+ assertEquals(result.toString(), runR8R8.toString());
+ // Check that the output jars are the same.
+ assertProgramsEqual(result.outputJar, runR8R8.outputJar);
+ }
+
+ private static Path runR8(Path inputJar, Path outputPath, CompilationMode mode) throws Exception {
+ Path outputJar = outputPath.resolve("output.jar");
+ ToolHelper.runR8(
+ R8Command.builder()
+ .setMode(mode)
+ .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
+ .setProgramConsumer(new ClassFileConsumer.ArchiveConsumer(outputJar, true))
+ .addProgramFiles(inputJar)
+ .addProguardConfigurationFiles(MAIN_KEEP)
+ .build());
+ return outputJar;
+ }
+
+ private R8Result runExternalR8(
+ Path r8Jar, Path inputJar, Path output, String[] keepRules, String mode) throws Exception {
+ Path pgConfigFile = output.resolve("keep.rules");
+ Path outputJar = output.resolve("output.jar");
+ Path pgMapFile = output.resolve("map.txt");
+ FileUtils.writeTextFile(pgConfigFile, keepRules);
+ ProcessResult processResult =
+ ToolHelper.runJava(
+ r8Jar,
+ R8_NAME,
+ "--lib",
+ ToolHelper.JAVA_8_RUNTIME,
+ "--classfile",
+ inputJar.toString(),
+ "--output",
+ outputJar.toString(),
+ "--pg-conf",
+ pgConfigFile.toString(),
+ mode,
+ "--pg-map-output",
+ pgMapFile.toString());
+ assertEquals(0, processResult.exitCode);
+ String pgMap = FileUtils.readTextFile(pgMapFile, Charsets.UTF_8);
+ return new R8Result(processResult, outputJar, pgMap);
+ }
+
+ private static void assertProgramsEqual(Path expectedJar, Path actualJar) throws Exception {
+ if (filesAreEqual(expectedJar, actualJar)) {
+ return;
+ }
+ ArchiveClassFileProvider expected = new ArchiveClassFileProvider(expectedJar);
+ ArchiveClassFileProvider actual = new ArchiveClassFileProvider(actualJar);
+ assertEquals(getSortedDescriptorList(expected), getSortedDescriptorList(actual));
+ for (String descriptor : expected.getClassDescriptors()) {
+ assertArrayEquals(
+ "Class " + descriptor + " differs",
+ getClassAsBytes(expected, descriptor),
+ getClassAsBytes(actual, descriptor));
+ }
+ }
+
+ private static boolean filesAreEqual(Path file1, Path file2) throws IOException {
+ long size = Files.size(file1);
+ long sizeOther = Files.size(file2);
+ if (size != sizeOther) {
+ return false;
+ }
+ if (size < 4096) {
+ return Arrays.equals(Files.readAllBytes(file1), Files.readAllBytes(file2));
+ }
+ int byteRead1 = 0;
+ int byteRead2 = 0;
+ try (FileInputStream fs1 = new FileInputStream(file1.toString());
+ FileInputStream fs2 = new FileInputStream(file2.toString())) {
+ BufferedInputStream bs1 = new BufferedInputStream(fs1);
+ BufferedInputStream bs2 = new BufferedInputStream(fs2);
+ while (byteRead1 == byteRead2 && byteRead1 != -1) {
+ byteRead1 = bs1.read();
+ byteRead2 = bs2.read();
+ }
+ }
+ return byteRead1 == byteRead2;
+ }
+
+ private static List<String> getSortedDescriptorList(ArchiveClassFileProvider inputJar) {
+ ArrayList<String> descriptorList = new ArrayList<>(inputJar.getClassDescriptors());
+ Collections.sort(descriptorList);
+ return descriptorList;
+ }
+
+ private static byte[] getClassAsBytes(ArchiveClassFileProvider inputJar, String descriptor)
+ throws Exception {
+ return toByteArray(inputJar.getProgramResource(descriptor).getByteStream());
+ }
+}