Reproduce issue with not filtering out META-INF/version classes
Bug: b/281774632
Change-Id: I4ed8047a399831a057253a36a57657766baf62ea
diff --git a/src/main/java/com/android/tools/r8/utils/FileUtils.java b/src/main/java/com/android/tools/r8/utils/FileUtils.java
index a7caf57..5fc14e9 100644
--- a/src/main/java/com/android/tools/r8/utils/FileUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/FileUtils.java
@@ -109,12 +109,14 @@
return Files.readAllLines(file);
}
- public static void writeTextFile(Path file, List<String> lines) throws IOException {
+ public static Path writeTextFile(Path file, List<String> lines) throws IOException {
Files.write(file, lines);
+ return file;
}
- public static void writeTextFile(Path file, String... lines) throws IOException {
+ public static Path writeTextFile(Path file, String... lines) throws IOException {
Files.write(file, Arrays.asList(lines));
+ return file;
}
public static Path validateOutputFile(Path path, Reporter reporter) {
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
index 39c6354..82952de 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -262,6 +262,14 @@
return stream;
}
+ public ZipBuilder addFile(String name, Path file) throws IOException {
+ ZipEntry zipEntry = new ZipEntry(name);
+ stream.putNextEntry(zipEntry);
+ Files.copy(file, stream);
+ stream.closeEntry();
+ return this;
+ }
+
public ZipBuilder addFilesRelative(Path basePath, Collection<Path> filesToAdd)
throws IOException {
for (Path path : filesToAdd) {
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 8479714..9570f9d 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -1542,7 +1542,7 @@
public static ProcessResult forkR8WithJavaOptions(
Path dir, List<String> javaOptions, String... args) throws IOException {
String r8Jar = R8_JAR.toAbsolutePath().toString();
- return forkJavaWithJarAndJavaOptions(dir, r8Jar, Arrays.asList(args), javaOptions);
+ return forkJavaWithJarAndJavaOptions(dir, javaOptions, r8Jar, Arrays.asList(args));
}
public static ProcessResult forkR8Jar(Path dir, String... args) throws IOException {
@@ -1569,11 +1569,11 @@
private static ProcessResult forkJavaWithJar(Path dir, String jarPath, List<String> args)
throws IOException {
- return forkJavaWithJarAndJavaOptions(dir, jarPath, args, ImmutableList.of());
+ return forkJavaWithJarAndJavaOptions(dir, ImmutableList.of(), jarPath, args);
}
private static ProcessResult forkJavaWithJarAndJavaOptions(
- Path dir, String jarPath, List<String> args, List<String> javaOptions) throws IOException {
+ Path dir, List<String> javaOptions, String jarPath, List<String> args) throws IOException {
List<String> command =
new ImmutableList.Builder<String>()
.add(getJavaExecutable())
@@ -1585,6 +1585,19 @@
return runProcess(new ProcessBuilder(command).directory(dir.toFile()));
}
+ public static ProcessResult forkJavaWithJavaOptions(
+ Path dir, List<String> javaOptions, Class clazz, List<String> args) throws IOException {
+ List<String> command =
+ new ImmutableList.Builder<String>()
+ .add(getJavaExecutable())
+ .addAll(javaOptions)
+ .add("-cp")
+ .add(System.getProperty("java.class.path"))
+ .add(clazz.getCanonicalName())
+ .addAll(args)
+ .build();
+ return runProcess(new ProcessBuilder(command).directory(dir.toFile()));
+ }
private static ProcessResult forkJava(Path dir, Class clazz, List<String> args)
throws IOException {
diff --git a/src/test/java/com/android/tools/r8/locale/TurkishLocaleMultiReleaseJarTest.java b/src/test/java/com/android/tools/r8/locale/TurkishLocaleMultiReleaseJarTest.java
new file mode 100644
index 0000000..9dd0da6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/locale/TurkishLocaleMultiReleaseJarTest.java
@@ -0,0 +1,109 @@
+// Copyright (c) 2023, 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.locale;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.D8;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+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 TurkishLocaleMultiReleaseJarTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
+ Path workingDir = temp.getRoot().toPath();
+ ProcessResult result =
+ ToolHelper.forkJavaWithJavaOptions(
+ workingDir,
+ ImmutableList.of("-Duser.language=tr"),
+ D8.class,
+ ImmutableList.of(
+ "--min-api",
+ Integer.toString(parameters.getApiLevel().getLevel()),
+ "--lib",
+ ToolHelper.getAndroidJar(AndroidApiLevel.U).toAbsolutePath().toString(),
+ buildMultiReleaseJarWithUpperCaseMetaInf(workingDir).toString()));
+ checkResult(result);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ Path workingDir = temp.getRoot().toPath();
+ ImmutableList.Builder<String> builder = ImmutableList.builder();
+ builder.add(
+ "--lib",
+ ToolHelper.getAndroidJar(AndroidApiLevel.U).toAbsolutePath().toString(),
+ "--pg-conf",
+ FileUtils.writeTextFile(temp.newFile("test.pro").toPath(), "-keep class * { *; }")
+ .toString(),
+ buildMultiReleaseJarWithUpperCaseMetaInf(workingDir).toString());
+ if (parameters.isCfRuntime()) {
+ builder.add("--classfile");
+ } else {
+ builder.add("--min-api", Integer.toString(parameters.getApiLevel().getLevel()));
+ }
+
+ ProcessResult result =
+ ToolHelper.forkJavaWithJavaOptions(
+ workingDir, ImmutableList.of("-Duser.language=tr"), R8.class, builder.build());
+ checkResult(result);
+ }
+
+ private Path buildMultiReleaseJarWithUpperCaseMetaInf(Path dir) throws Exception {
+ // Compiler will to check String.toLowerCase() of zip entries.
+ Path jar = dir.resolve("test.jar");
+ ZipBuilder.builder(jar)
+ .addFilesRelative(
+ ToolHelper.getClassPathForTests(), ToolHelper.getClassFileForTestClass(TestClass.class))
+ .addFile(
+ Paths.get("META-INF/versions/9")
+ .resolve(
+ ToolHelper.getClassPathForTests()
+ .relativize(ToolHelper.getClassFileForTestClass(TestClass.class)))
+ .toString(),
+ ToolHelper.getClassFileForTestClass(TestClass.class))
+ .build();
+ return jar;
+ }
+
+ private void checkResult(ProcessResult result) {
+ assertEquals(1, result.exitCode);
+ assertThat(
+ result.stderr,
+ containsString("Type " + TestClass.class.getTypeName() + " is defined multiple times"));
+ }
+
+ static class TestClass {
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/locale/TurkishLocaleZipFileInputTest.java b/src/test/java/com/android/tools/r8/locale/TurkishLocaleZipFileInputTest.java
new file mode 100644
index 0000000..14a330e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/locale/TurkishLocaleZipFileInputTest.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2023, 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.locale;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.D8;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+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 TurkishLocaleZipFileInputTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
+ Path workingDir = temp.getRoot().toPath();
+ ProcessResult result =
+ ToolHelper.forkJavaWithJavaOptions(
+ workingDir,
+ // See b/281774632 for context.
+ ImmutableList.of("-Duser.language=tr"),
+ D8.class,
+ ImmutableList.of(
+ "--min-api",
+ Integer.toString(parameters.getApiLevel().getLevel()),
+ "--lib",
+ ToolHelper.getAndroidJar(AndroidApiLevel.U).toAbsolutePath().toString(),
+ buildZipWithUpperCaseExtension(workingDir).toString()));
+ checkResult(result);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ Path workingDir = temp.getRoot().toPath();
+ ImmutableList.Builder<String> builder = ImmutableList.builder();
+ builder.add(
+ "--lib",
+ ToolHelper.getAndroidJar(AndroidApiLevel.U).toAbsolutePath().toString(),
+ "--pg-conf",
+ FileUtils.writeTextFile(temp.newFile("test.pro").toPath(), "-keep class * { *; }")
+ .toString(),
+ buildZipWithUpperCaseExtension(workingDir).toString());
+ if (parameters.isCfRuntime()) {
+ builder.add("--classfile");
+ } else {
+ builder.add("--min-api", Integer.toString(parameters.getApiLevel().getLevel()));
+ }
+
+ ProcessResult result =
+ ToolHelper.forkJavaWithJavaOptions(
+ workingDir,
+ // See b/281774632 for context.
+ ImmutableList.of("-Duser.language=tr"),
+ R8.class,
+ builder.build());
+ checkResult(result);
+ }
+
+ private Path buildZipWithUpperCaseExtension(Path dir) throws Exception {
+ // Compiler will to check String.toLowerCase() of file extensions.
+ Path jar = dir.resolve("test.ZIP");
+ ZipBuilder.builder(jar)
+ .addFilesRelative(
+ ToolHelper.getClassPathForTests(), ToolHelper.getClassFileForTestClass(TestClass.class))
+ .build();
+ return jar;
+ }
+
+ private void checkResult(ProcessResult result) {
+ assertEquals(1, result.exitCode);
+ assertThat(result.stderr, containsString("Unsupported source file type"));
+ }
+
+ static class TestClass {
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+ }
+}