Version 2.0.41

Cherry pick: Check for any line separator when reading service files
CL: https://r8-review.googlesource.com/49222

Bug: 144437165
Change-Id: Ifc07dbd62f1c94f59f24c6175b705521e6ec2e0a
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 1c15adc..8faf42c 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 = "2.0.40";
+  public static final String LABEL = "2.0.41";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/graph/AppServices.java b/src/main/java/com/android/tools/r8/graph/AppServices.java
index 584a879..5c1c7ad 100644
--- a/src/main/java/com/android/tools/r8/graph/AppServices.java
+++ b/src/main/java/com/android/tools/r8/graph/AppServices.java
@@ -13,13 +13,13 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.io.ByteStreams;
 import java.io.IOException;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.IdentityHashMap;
 import java.util.List;
@@ -176,7 +176,7 @@
       private void readServiceImplementationsForService(
           String contents, Origin origin, List<DexType> serviceImplementations) {
         if (contents != null) {
-          Arrays.stream(contents.split(System.lineSeparator()))
+          StringUtils.splitLines(contents).stream()
               .map(String::trim)
               .map(this::prefixUntilCommentChar)
               .filter(line -> !line.isEmpty())
diff --git a/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderRewritingLineSeparatorTest.java b/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderRewritingLineSeparatorTest.java
new file mode 100644
index 0000000..9b828fc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderRewritingLineSeparatorTest.java
@@ -0,0 +1,120 @@
+// Copyright (c) 2020, 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.rewrite;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.rewrite.ServiceLoaderRewritingTest.MainRunner;
+import com.android.tools.r8.rewrite.ServiceLoaderRewritingTest.Service;
+import com.android.tools.r8.rewrite.ServiceLoaderRewritingTest.ServiceImpl;
+import com.android.tools.r8.rewrite.ServiceLoaderRewritingTest.ServiceImpl2;
+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.InstructionSubject;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.zip.ZipFile;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ServiceLoaderRewritingLineSeparatorTest extends TestBase {
+
+  private final TestParameters parameters;
+  private final Separator lineSeparator;
+
+  private final String EXPECTED_OUTPUT =
+      StringUtils.lines("Hello World!", "Hello World!", "Hello World!");
+
+  enum Separator {
+    WINDOWS,
+    LINUX;
+
+    public String getSeparator() {
+      switch (this) {
+        case WINDOWS:
+          return "\r\n";
+        case LINUX:
+          return "\n";
+        default:
+          assert false;
+      }
+      return null;
+    }
+  }
+
+  @Parameters(name = "{0}, separator: {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(), Separator.values());
+  }
+
+  public ServiceLoaderRewritingLineSeparatorTest(TestParameters parameters, Separator separator) {
+    this.parameters = parameters;
+    this.lineSeparator = separator;
+  }
+
+  @Test
+  public void testRewritingWithMultipleWithLineSeparator()
+      throws IOException, CompilationFailedException, ExecutionException {
+    Path path = temp.newFile("out.zip").toPath();
+    testForR8(parameters.getBackend())
+        .addInnerClasses(ServiceLoaderRewritingTest.class)
+        .addKeepMainRule(MainRunner.class)
+        .setMinApi(parameters.getApiLevel())
+        .addDataEntryResources(
+            DataEntryResource.fromBytes(
+                StringUtils.join(
+                        lineSeparator.getSeparator(),
+                        ServiceImpl.class.getTypeName(),
+                        ServiceImpl2.class.getTypeName())
+                    .getBytes(),
+                "META-INF/services/" + Service.class.getTypeName(),
+                Origin.unknown()))
+        .compile()
+        .writeToZip(path)
+        .run(parameters.getRuntime(), MainRunner.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT + StringUtils.lines("Hello World 2!"))
+        .inspect(
+            inspector -> {
+              // Check that we have actually rewritten the calls to ServiceLoader.load.
+              assertEquals(0, getServiceLoaderLoads(inspector));
+            });
+
+    // Check that we have removed the service configuration from META-INF/services.
+    ZipFile zip = new ZipFile(path.toFile());
+    assertNull(zip.getEntry("META-INF/services"));
+  }
+
+  private static long getServiceLoaderLoads(CodeInspector inspector) {
+    ClassSubject classSubject = inspector.clazz(MainRunner.class);
+    assertTrue(classSubject.isPresent());
+    return classSubject.allMethods().stream()
+        .mapToLong(
+            method ->
+                method
+                    .streamInstructions()
+                    .filter(ServiceLoaderRewritingLineSeparatorTest::isServiceLoaderLoad)
+                    .count())
+        .sum();
+  }
+
+  private static boolean isServiceLoaderLoad(InstructionSubject instruction) {
+    return instruction.isInvokeStatic()
+        && instruction.getMethod().qualifiedName().contains("ServiceLoader.load");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderRewritingTest.java b/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderRewritingTest.java
index ccfefda..4749163 100644
--- a/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderRewritingTest.java
@@ -137,7 +137,7 @@
 
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   public ServiceLoaderRewritingTest(TestParameters parameters) {
@@ -150,7 +150,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(ServiceLoaderRewritingTest.class)
         .addKeepMainRule(MainRunner.class)
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(parameters.getApiLevel())
         .addDataEntryResources(
             DataEntryResource.fromBytes(
                 StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
@@ -178,7 +178,7 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(ServiceLoaderRewritingTest.class)
         .addKeepMainRule(MainRunner.class)
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(parameters.getApiLevel())
         .addDataEntryResources(
             DataEntryResource.fromBytes(
                 StringUtils.lines(ServiceImpl.class.getTypeName(), ServiceImpl2.class.getTypeName())
@@ -236,7 +236,7 @@
         testForR8(parameters.getBackend())
             .addInnerClasses(ServiceLoaderRewritingTest.class)
             .addKeepMainRule(OtherRunner.class)
-            .setMinApi(parameters.getRuntime())
+            .setMinApi(parameters.getApiLevel())
             .addDataEntryResources(
                 DataEntryResource.fromBytes(
                     StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
@@ -304,7 +304,7 @@
             .addInnerClasses(ServiceLoaderRewritingTest.class)
             .addKeepMainRule(MainRunner.class)
             .addKeepClassRules(Service.class)
-            .setMinApi(parameters.getRuntime())
+            .setMinApi(parameters.getApiLevel())
             .addDataEntryResources(
                 DataEntryResource.fromBytes(
                     StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),