Allow comments and whitespace in ART profiles

Fixes: b/271160555
Change-Id: I1ce71acad9bea7c06261219ad35ec527481b7ecf
diff --git a/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java b/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java
index 85cca78..6c9ec51 100644
--- a/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java
+++ b/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java
@@ -50,9 +50,12 @@
           BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
         int lineNumber = 1;
         while (bufferedReader.ready()) {
-          String rule = bufferedReader.readLine();
-          if (!parseRule(rule)) {
-            parseError(rule, lineNumber, origin);
+          String line = bufferedReader.readLine();
+          String lineWithoutComment = removeCommentFromLine(line);
+          if (isWhitespace(lineWithoutComment)) {
+            // Skip.
+          } else if (!parseRule(lineWithoutComment)) {
+            parseError(line, lineNumber, origin);
           }
           lineNumber++;
         }
@@ -72,6 +75,15 @@
     }
   }
 
+  private boolean isWhitespace(String line) {
+    for (int i = 0; i < line.length(); i++) {
+      if (!Character.isWhitespace(line.charAt(i))) {
+        return false;
+      }
+    }
+    return true;
+  }
+
   public boolean parseRule(String rule) {
     try {
       ArtProfileMethodRuleInfoImpl.Builder methodRuleInfoBuilder =
@@ -148,6 +160,14 @@
     return true;
   }
 
+  private static String removeCommentFromLine(String line) {
+    int commentStartIndex = line.indexOf('#');
+    if (commentStartIndex >= 0) {
+      return line.substring(0, commentStartIndex).stripTrailing();
+    }
+    return line;
+  }
+
   public static class Builder implements HumanReadableArtProfileParserBuilder {
 
     private Consumer<HumanReadableArtProfileParserErrorDiagnostic> diagnosticConsumer;
diff --git a/src/test/java/com/android/tools/r8/profile/art/format/ArtProfileWithCommentsAndWhitespaceTest.java b/src/test/java/com/android/tools/r8/profile/art/format/ArtProfileWithCommentsAndWhitespaceTest.java
new file mode 100644
index 0000000..834a5c1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/format/ArtProfileWithCommentsAndWhitespaceTest.java
@@ -0,0 +1,112 @@
+// 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.profile.art.format;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.profile.art.ArtProfileBuilder;
+import com.android.tools.r8.profile.art.ArtProfileProvider;
+import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.UTF8TextInputStream;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Path;
+import org.junit.BeforeClass;
+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 ArtProfileWithCommentsAndWhitespaceTest extends TestBase {
+
+  private static final MethodReference MAIN_METHOD_REFERENCE =
+      MethodReferenceUtils.mainMethod(Main.class);
+
+  private static Path profileWithComments;
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDefaultRuntimes().withMinimumApiLevel().build();
+  }
+
+  @BeforeClass
+  public static void setup() throws Exception {
+    profileWithComments = getStaticTemp().newFile().toPath();
+    FileUtils.writeTextFile(
+        profileWithComments,
+        "# Comment 1",
+        " # Comment 2",
+        " ",
+        "\t",
+        "HSP" + MethodReferenceUtils.toSmaliString(MAIN_METHOD_REFERENCE),
+        "# Comment 3");
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    parameters.assumeDexRuntime();
+    testForD8()
+        .addProgramClasses(Main.class)
+        .addArtProfileForRewriting(getArtProfileProvider())
+        .release()
+        .setMinApi(parameters)
+        .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages)
+        .inspectResidualArtProfile(this::inspectResidualArtProfile);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(Main.class)
+        .addKeepMainRule(Main.class)
+        .addArtProfileForRewriting(getArtProfileProvider())
+        .setMinApi(parameters)
+        .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages)
+        .inspectResidualArtProfile(this::inspectResidualArtProfile);
+  }
+
+  private ArtProfileProvider getArtProfileProvider() {
+    return new ArtProfileProvider() {
+
+      @Override
+      public void getArtProfile(ArtProfileBuilder profileBuilder) {
+        try {
+          profileBuilder.addHumanReadableArtProfile(
+              new UTF8TextInputStream(profileWithComments), parserBuilder -> {});
+        } catch (IOException e) {
+          throw new UncheckedIOException(e);
+        }
+      }
+
+      @Override
+      public Origin getOrigin() {
+        return new PathOrigin(profileWithComments);
+      }
+    };
+  }
+
+  private void inspectResidualArtProfile(ArtProfileInspector profileInspector) {
+    profileInspector.assertContainsMethodRule(MAIN_METHOD_REFERENCE).assertContainsNoOtherRules();
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.println("Hello, world!");
+    }
+  }
+}