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(),