Version 1.2.28.

Merge: Do not use Paths.get on ZipEntry names.
CL: https://r8-review.googlesource.com/c/r8/+/22466

Merge: Fix FilteredClassPathTest on Windows to not assume the use of Path objects.
CL: https://r8-review.googlesource.com/c/r8/+/22466

R=sgjesse@google.com

Bug: 109992855
Change-Id: Ie7f08bcfc21085856c564d57d2ca256c74b6ffac
diff --git a/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java b/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
index af83158..a6389da 100644
--- a/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
@@ -12,7 +12,7 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ZipUtils;
 import com.google.common.io.ByteStreams;
 import java.io.Closeable;
 import java.io.IOException;
@@ -21,7 +21,6 @@
 import java.nio.file.Files;
 import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashSet;
@@ -73,7 +72,7 @@
     while (entries.hasMoreElements()) {
       ZipEntry entry = entries.nextElement();
       String name = entry.getName();
-      if (FileUtils.isClassFile(Paths.get(name)) && include.test(name)) {
+      if (ZipUtils.isClassFile(name) && include.test(name)) {
         descriptors.add(DescriptorUtils.guessTypeDescriptor(name));
       }
     }
diff --git a/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java b/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java
index 425b3c3..1d6e678 100644
--- a/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/ArchiveProgramResourceProvider.java
@@ -3,22 +3,18 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
-import static com.android.tools.r8.utils.FileUtils.isClassFile;
-import static com.android.tools.r8.utils.FileUtils.isDexFile;
-
 import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.origin.ArchiveEntryOrigin;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ZipUtils;
 import com.google.common.io.ByteStreams;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -39,16 +35,15 @@
   }
 
   public static boolean includeClassFileEntries(String entry) {
-    return isClassFile(Paths.get(entry));
+    return ZipUtils.isClassFile(entry);
   }
 
   public static boolean includeDexEntries(String entry) {
-    return isDexFile(Paths.get(entry));
+    return ZipUtils.isDexFile(entry);
   }
 
   public static boolean includeClassFileOrDexEntries(String entry) {
-    Path path = Paths.get(entry);
-    return isClassFile(path) || isDexFile(path);
+    return ZipUtils.isClassFile(entry) || ZipUtils.isDexFile(entry);
   }
 
   private final Origin origin;
@@ -97,14 +92,13 @@
         ZipEntry entry = entries.nextElement();
         try (InputStream stream = zipFile.getInputStream(entry)) {
           String name = entry.getName();
-          Path path = Paths.get(name);
-          Origin entryOrigin = new ArchiveEntryOrigin(entry.getName(), origin);
+          Origin entryOrigin = new ArchiveEntryOrigin(name, origin);
           if (include.test(name)) {
-            if (FileUtils.isDexFile(path)) {
+            if (ZipUtils.isDexFile(name)) {
               dexResources.add(
                   ProgramResource.fromBytes(
                       entryOrigin, Kind.DEX, ByteStreams.toByteArray(stream), null));
-            } else if (isClassFile(path)) {
+            } else if (ZipUtils.isClassFile(name)) {
               String descriptor = DescriptorUtils.guessTypeDescriptor(name);
               classResources.add(
                   ProgramResource.fromBytes(
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 02c5f8e..9271ea6 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.2.27";
+  public static final String LABEL = "1.2.28";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java b/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
index 1336f95..807509a 100644
--- a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
+++ b/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
@@ -22,7 +22,6 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.ZipUtils;
 import com.google.common.collect.ImmutableMap;
@@ -61,7 +60,7 @@
           archive.toString(),
           (entry, stream) -> {
             String name = entry.getName();
-            if (FileUtils.isClassFile(Paths.get(name))) {
+            if (ZipUtils.isClassFile(name)) {
               String descriptor = DescriptorUtils.guessTypeDescriptor(name);
               builder.put(descriptor, ByteStreams.toByteArray(stream));
             }
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
index 871f6d8..7d2907a 100644
--- a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
@@ -3,13 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.compatdx;
 
-import static com.android.tools.r8.utils.FileUtils.isApkFile;
-import static com.android.tools.r8.utils.FileUtils.isArchive;
-import static com.android.tools.r8.utils.FileUtils.isClassFile;
-import static com.android.tools.r8.utils.FileUtils.isDexFile;
-import static com.android.tools.r8.utils.FileUtils.isJarFile;
-import static com.android.tools.r8.utils.FileUtils.isZipFile;
-
 import com.android.tools.r8.CompatDxHelper;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
@@ -28,6 +21,7 @@
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.ZipUtils;
 import com.google.common.collect.ImmutableList;
 import com.google.common.io.ByteStreams;
 import java.io.File;
@@ -475,7 +469,7 @@
     }
     if (singleDexFile) {
       return new SingleDexFileConsumer(
-          isDexFile(output)
+          FileUtils.isDexFile(output)
               ? new NamedDexFileConsumer(output)
               : createDexConsumer(output, inputs, keepClasses));
     }
@@ -486,13 +480,13 @@
       Path output, List<Path> inputs, boolean keepClasses)
       throws DxUsageMessage {
     if (keepClasses) {
-      if (!isArchive(output)) {
+      if (!FileUtils.isArchive(output)) {
         throw new DxCompatOptions.DxUsageMessage(
             "Output must be an archive when --keep-classes is set.");
       }
       return new DexKeepClassesConsumer(output, inputs);
     }
-    return isArchive(output)
+    return FileUtils.isArchive(output)
         ? new DexIndexedConsumer.ArchiveConsumer(output)
         : new DexIndexedConsumer.DirectoryConsumer(output);
   }
@@ -572,12 +566,12 @@
     private void writeZipWithClasses(DiagnosticsHandler handler) throws IOException {
       // For each input archive file, add all class files within.
       for (Path input : inputs) {
-        if (isArchive(input)) {
+        if (FileUtils.isArchive(input)) {
           try (ZipFile zipFile = new ZipFile(input.toFile(), StandardCharsets.UTF_8)) {
             final Enumeration<? extends ZipEntry> entries = zipFile.entries();
             while (entries.hasMoreElements()) {
               ZipEntry entry = entries.nextElement();
-              if (isClassFile(Paths.get(entry.getName()))) {
+              if (ZipUtils.isClassFile(entry.getName())) {
                 try (InputStream entryStream = zipFile.getInputStream(entry)) {
                   outputBuilder.addFile(
                       entry.getName(), ByteStreams.toByteArray(entryStream), handler);
@@ -604,11 +598,11 @@
       return;
     }
     Path path = file.toPath();
-    if (isZipFile(path) || isJarFile(path) || isClassFile(path)) {
+    if (FileUtils.isZipFile(path) || FileUtils.isJarFile(path) || FileUtils.isClassFile(path)) {
       files.add(path);
       return;
     }
-    if (isApkFile(path)) {
+    if (FileUtils.isApkFile(path)) {
       throw new Unimplemented("apk files not yet supported");
     }
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/FilteredClassPath.java b/src/main/java/com/android/tools/r8/shaking/FilteredClassPath.java
index 1eb11e9..c67af00 100644
--- a/src/main/java/com/android/tools/r8/shaking/FilteredClassPath.java
+++ b/src/main/java/com/android/tools/r8/shaking/FilteredClassPath.java
@@ -46,14 +46,14 @@
     return path;
   }
 
-  public boolean matchesFile(Path file) {
+  public boolean matchesFile(String name) {
     if (isUnfiltered()) {
       return true;
     }
     boolean isNegated = false;
     for (String pattern : pattern) {
       isNegated = pattern.charAt(0) == '!';
-      boolean matches = matchAgainstFileName(file.toString(), 0, pattern, isNegated ? 1 : 0);
+      boolean matches = matchAgainstFileName(name, 0, pattern, isNegated ? 1 : 0);
       if (matches) {
         return !isNegated;
       }
@@ -63,7 +63,7 @@
   }
 
   private boolean containsFileSeparator(String string) {
-    return string.indexOf(File.separatorChar) != -1;
+    return string.indexOf('/') != -1;
   }
 
   private boolean matchAgainstFileName(String fileName, int namePos, String pattern,
@@ -95,7 +95,7 @@
         }
       } else {
         for (int i = namePos; i < fileName.length(); i++) {
-          if (!includeFileSeparators && fileName.charAt(i) == File.separatorChar) {
+          if (!includeFileSeparators && fileName.charAt(i) == '/') {
             return false;
           }
           if (matchAgainstFileName(fileName, i, pattern, patternPos + 1)) {
diff --git a/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java b/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java
index 0f06ca8..a611105 100644
--- a/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java
@@ -4,8 +4,6 @@
 package com.android.tools.r8.utils;
 
 import static com.android.tools.r8.utils.FileUtils.isArchive;
-import static com.android.tools.r8.utils.FileUtils.isClassFile;
-import static com.android.tools.r8.utils.FileUtils.isDexFile;
 
 import com.android.tools.r8.DataDirectoryResource;
 import com.android.tools.r8.DataEntryResource;
@@ -23,8 +21,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
-import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -56,17 +52,16 @@
         ZipEntry entry = entries.nextElement();
         try (InputStream stream = zipFile.getInputStream(entry)) {
           String name = entry.getName();
-          Path path = Paths.get(name);
           Origin entryOrigin = new ArchiveEntryOrigin(name, origin);
-          if (archive.matchesFile(path)) {
-            if (isDexFile(path)) {
+          if (archive.matchesFile(name)) {
+            if (ZipUtils.isDexFile(name)) {
               if (!ignoreDexInArchive) {
                 ProgramResource resource =
                     OneShotByteResource.create(
                         Kind.DEX, entryOrigin, ByteStreams.toByteArray(stream), null);
                 dexResources.add(resource);
               }
-            } else if (isClassFile(path)) {
+            } else if (ZipUtils.isClassFile(name)) {
               String descriptor = DescriptorUtils.guessTypeDescriptor(name);
               ProgramResource resource =
                   OneShotByteResource.create(
@@ -111,7 +106,7 @@
       final Enumeration<? extends ZipEntry> entries = zipFile.entries();
       while (entries.hasMoreElements()) {
         ZipEntry entry = entries.nextElement();
-        Path name = Paths.get(entry.getName());
+        String name = entry.getName();
         if (archive.matchesFile(name) && !isProgramResourceName(name)) {
           if (entry.isDirectory()) {
             resourceBrowser.visit(DataDirectoryResource.fromZip(zipFile, entry));
@@ -129,7 +124,7 @@
     }
   }
 
-  private boolean isProgramResourceName(Path name) {
-    return isClassFile(name) || (isDexFile(name) && !ignoreDexInArchive);
+  private boolean isProgramResourceName(String name) {
+    return ZipUtils.isClassFile(name) || (ZipUtils.isDexFile(name) && !ignoreDexInArchive);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/FilteredArchiveClassFileProvider.java b/src/main/java/com/android/tools/r8/utils/FilteredArchiveClassFileProvider.java
index f933b42..0c6cc14 100644
--- a/src/main/java/com/android/tools/r8/utils/FilteredArchiveClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/FilteredArchiveClassFileProvider.java
@@ -6,12 +6,11 @@
 import com.android.tools.r8.ArchiveClassFileProvider;
 import com.android.tools.r8.shaking.FilteredClassPath;
 import java.io.IOException;
-import java.nio.file.Paths;
 
 // Internal filtered class-file provider.
 class FilteredArchiveClassFileProvider extends ArchiveClassFileProvider {
 
   FilteredArchiveClassFileProvider(FilteredClassPath archive) throws IOException {
-    super(archive.getPath(), entry -> archive.matchesFile(Paths.get(entry)));
+    super(archive.getPath(), entry -> archive.matchesFile(entry));
   }
 }
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 849377b..fd50583 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -3,6 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
+import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
+import static com.android.tools.r8.utils.FileUtils.DEX_EXTENSION;
+import static com.android.tools.r8.utils.FileUtils.MODULE_INFO_CLASS;
+
 import com.android.tools.r8.errors.CompilationError;
 import com.google.common.io.ByteStreams;
 import java.io.File;
@@ -92,4 +96,17 @@
     stream.write(content);
     stream.closeEntry();
   }
+
+  public static boolean isDexFile(String entry) {
+    String name = entry.toLowerCase();
+    return name.endsWith(DEX_EXTENSION);
+  }
+
+  public static boolean isClassFile(String entry) {
+    String name = entry.toLowerCase();
+    if (name.endsWith(MODULE_INFO_CLASS)) {
+      return false;
+    }
+    return name.endsWith(CLASS_EXTENSION);
+  }
 }
diff --git a/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java b/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java
index 2ef0123..23dc7e0 100644
--- a/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java
+++ b/src/test/apiUsageSample/com/android/tools/apiusagesample/D8ApiUsageSample.java
@@ -391,8 +391,9 @@
         ZipInputStream zip = new ZipInputStream(Files.newInputStream(file), StandardCharsets.UTF_8);
         ZipEntry entry;
         while (null != (entry = zip.getNextEntry())) {
-          if (isClassFile(Paths.get(entry.getName()))) {
-            Origin origin = new ArchiveEntryOrigin(entry.getName(), zipOrigin);
+          String name = entry.getName();
+          if (isClassFile(name)) {
+            Origin origin = new ArchiveEntryOrigin(name, zipOrigin);
             classfiles.add(new ClassFileContent(origin, readBytes(zip)));
           }
         }
diff --git a/src/test/apiUsageSample/com/android/tools/apiusagesample/R8ApiUsageSample.java b/src/test/apiUsageSample/com/android/tools/apiusagesample/R8ApiUsageSample.java
index 2c2e8d7..7bce10a 100644
--- a/src/test/apiUsageSample/com/android/tools/apiusagesample/R8ApiUsageSample.java
+++ b/src/test/apiUsageSample/com/android/tools/apiusagesample/R8ApiUsageSample.java
@@ -382,8 +382,9 @@
         ZipInputStream zip = new ZipInputStream(Files.newInputStream(file), StandardCharsets.UTF_8);
         ZipEntry entry;
         while (null != (entry = zip.getNextEntry())) {
-          if (isClassFile(Paths.get(entry.getName()))) {
-            Origin origin = new ArchiveEntryOrigin(entry.getName(), zipOrigin);
+          String name = entry.getName();
+          if (isClassFile(name)) {
+            Origin origin = new ArchiveEntryOrigin(name, zipOrigin);
             classfiles.add(new ClassFileContent(origin, readBytes(zip)));
           }
         }
diff --git a/src/test/java/com/android/tools/r8/ArchiveClassFileProviderTest.java b/src/test/java/com/android/tools/r8/ArchiveClassFileProviderTest.java
new file mode 100644
index 0000000..6ceb0a2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ArchiveClassFileProviderTest.java
@@ -0,0 +1,43 @@
+// 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;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class ArchiveClassFileProviderTest {
+
+  @Rule
+  public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+  public Path createZip() throws IOException {
+    Path tempRoot = temporaryFolder.getRoot().toPath();
+    Path zipFile = tempRoot.resolve("zipfile.zip");
+    ZipOutputStream zipStream =
+        new ZipOutputStream(new FileOutputStream(zipFile.toFile()), StandardCharsets.UTF_8);
+    ZipEntry entry = new ZipEntry("non-ascii:$\u02CF");
+    zipStream.putNextEntry(entry);
+    zipStream.write(10);
+    zipStream.close();
+    return zipFile;
+  }
+
+  @Test
+  public void testSystemLocale() throws IOException, ResourceException {
+    // Set the locale used for Paths to ASCII which will make Path creation fail
+    // for non-ascii names.
+    System.setProperty("sun.jnu.encoding", "ASCII");
+    Path zipFile = createZip();
+    new ArchiveClassFileProvider(zipFile);
+    ArchiveProgramResourceProvider.fromArchive(zipFile).getProgramResources();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/FilteredClassPathTest.java b/src/test/java/com/android/tools/r8/shaking/FilteredClassPathTest.java
index f27813e..7c243ae 100644
--- a/src/test/java/com/android/tools/r8/shaking/FilteredClassPathTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/FilteredClassPathTest.java
@@ -34,16 +34,8 @@
 
   private void testPath(List<String> filters, List<String> positives, List<String> negatives) {
     FilteredClassPath path = makeFilteredClassPath(filters);
-    Assert.assertTrue(
-        positives.stream().map(FilteredClassPathTest::adaptFileSeparator).map(Paths::get)
-            .allMatch(path::matchesFile));
-    Assert.assertFalse(
-        negatives.stream().map(FilteredClassPathTest::adaptFileSeparator).map(Paths::get)
-            .allMatch(path::matchesFile));
-  }
-
-  private static String adaptFileSeparator(String s) {
-    return s.replace('/', File.separatorChar);
+    Assert.assertTrue(positives.stream().allMatch(path::matchesFile));
+    Assert.assertFalse(negatives.stream().allMatch(path::matchesFile));
   }
 
   private static FilteredClassPath makeFilteredClassPath(List<String> filters) {
@@ -51,9 +43,7 @@
   }
 
   private static FilteredClassPath makeFilteredClassPath(Path path, List<String> filters) {
-    return new FilteredClassPath(path,
-        filters.stream().map(FilteredClassPathTest::adaptFileSeparator)
-            .collect(ImmutableList.toImmutableList()));
+    return new FilteredClassPath(path, ImmutableList.copyOf(filters));
   }
 
   @Test