Version 2.2.29

Cherry-pick: [Retrace] Print waiting message on std-in
CL: https://r8-review.googlesource.com/54982

Cherry-pick: Remove ClassNameMapper from the public api of Retrace
CL: https://r8-review.googlesource.com/54841

Cherry-pick: [Retrace] Print an error message on invalid input file
CL: https://r8-review.googlesource.com/54986

Cherry-pick: [Retrace] Print usage on errors and version on help
CL: https://r8-review.googlesource.com/54987

Cherry-pick: Remove unused catches
CL: https://r8-review.googlesource.com/54989

Cherry-pick: [Retrace] Adjust testHelp test expectation
CL: https://r8-review.googlesource.com/54994

Bug: 169671295
Bug: 169672210
Bug: 163981284
Bug: 169552658
Bug: 169552039
Change-Id: I69e22c11f36934872f60070beda7a5a7341f63ff
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 781a82b..b1da4e4 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.2.28";
+  public static final String LABEL = "2.2.29";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/retrace/DirectClassNameMapperProguardMapProducer.java b/src/main/java/com/android/tools/r8/retrace/DirectClassNameMapperProguardMapProducer.java
new file mode 100644
index 0000000..8efe05a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/DirectClassNameMapperProguardMapProducer.java
@@ -0,0 +1,18 @@
+// 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.retrace;
+
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.retrace.RetraceCommand.ProguardMapProducer;
+
+public interface DirectClassNameMapperProguardMapProducer extends ProguardMapProducer {
+
+  ClassNameMapper getClassNameMapper();
+
+  @Override
+  default String get() {
+    throw new RuntimeException("Should not be called for DirectClassNameMapperProguardMapProducer");
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/InvalidMappingFileException.java b/src/main/java/com/android/tools/r8/retrace/InvalidMappingFileException.java
new file mode 100644
index 0000000..86e81e1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/InvalidMappingFileException.java
@@ -0,0 +1,20 @@
+// 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.retrace;
+
+import com.android.tools.r8.Keep;
+
+@Keep
+public final class InvalidMappingFileException extends RuntimeException {
+
+  public InvalidMappingFileException(Throwable throwable) {
+    super(throwable);
+  }
+
+  @Override
+  public String getMessage() {
+    return "Unable to parse mapping file";
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index f34a5aa..adf9583 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -10,9 +10,9 @@
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.Keep;
 import com.android.tools.r8.Version;
-import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.retrace.RetraceCommand.Builder;
 import com.android.tools.r8.retrace.RetraceCommand.ProguardMapProducer;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.OptionsParsing;
 import com.android.tools.r8.utils.OptionsParsing.ParseContext;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -120,7 +120,15 @@
           new StringDiagnostic(String.format("Could not find mapping file '%s'.", mappingPath)));
       throw new RetraceAbortException();
     }
-    return () -> new String(Files.readAllBytes(path));
+    return () -> {
+      try {
+        return new String(Files.readAllBytes(path));
+      } catch (IOException e) {
+        diagnosticsHandler.error(
+            new StringDiagnostic(String.format("Could not open mapping file '%s'.", mappingPath)));
+        throw new RuntimeException(e);
+      }
+    };
   }
 
   private static List<String> getStackTraceFromFile(
@@ -142,11 +150,9 @@
     try {
       Timing timing = Timing.create("R8 retrace", command.printMemory());
       timing.begin("Read proguard map");
-      ClassNameMapper classNameMapper =
-          ClassNameMapper.mapperFromString(
-              command.proguardMapProducer.get(), command.diagnosticsHandler);
+      RetraceApi retracer =
+          Retracer.create(command.proguardMapProducer, command.diagnosticsHandler);
       timing.end();
-      RetraceApi retracer = Retracer.create(classNameMapper);
       RetraceCommandLineResult result;
       timing.begin("Parse and Retrace");
       if (command.regularExpression != null) {
@@ -171,10 +177,9 @@
       if (command.printTimes()) {
         timing.report();
       }
-    } catch (IOException ex) {
-      command.diagnosticsHandler.error(
-          new StringDiagnostic("Could not open mapping input stream: " + ex.getMessage()));
-      throw new RetraceAbortException();
+    } catch (InvalidMappingFileException e) {
+      command.diagnosticsHandler.error(new ExceptionDiagnostic(e));
+      throw e;
     }
   }
 
@@ -207,6 +212,7 @@
         return;
       }
       assert Arrays.asList(mappedArgs).contains("--help");
+      System.out.println("Retrace " + Version.getVersionString());
       System.out.print(USAGE_MESSAGE);
       return;
     }
@@ -233,6 +239,7 @@
   }
 
   private static List<String> getStackTraceFromStandardInput() {
+    System.out.println("Waiting for stack-trace input...");
     Scanner sc = new Scanner(new InputStreamReader(System.in, Charsets.UTF_8));
     List<String> readLines = new ArrayList<>();
     while (sc.hasNext()) {
@@ -252,9 +259,11 @@
       action.run();
     } catch (RetraceAbortException e) {
       // Detail of the errors were already reported
+      System.err.println(StringUtils.LINE_SEPARATOR + USAGE_MESSAGE + StringUtils.LINE_SEPARATOR);
       System.exit(STATUS_ERROR);
     } catch (RuntimeException e) {
       System.err.println("Retrace failed with an internal error.");
+      System.err.println(StringUtils.LINE_SEPARATOR + USAGE_MESSAGE + StringUtils.LINE_SEPARATOR);
       Throwable cause = e.getCause() == null ? e : e.getCause();
       cause.printStackTrace();
       System.exit(STATUS_ERROR);
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java b/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
index 64cf59a..3ead933 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
@@ -7,7 +7,6 @@
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.Keep;
 import com.android.tools.r8.utils.StringDiagnostic;
-import java.io.IOException;
 import java.util.List;
 import java.util.function.Consumer;
 
@@ -153,6 +152,6 @@
 
   @Keep
   public interface ProguardMapProducer {
-    String get() throws IOException;
+    String get();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/Retracer.java b/src/main/java/com/android/tools/r8/retrace/Retracer.java
index 0f7d547..62add05 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retracer.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retracer.java
@@ -4,12 +4,14 @@
 
 package com.android.tools.r8.retrace;
 
+import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.Keep;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.retrace.RetraceCommand.ProguardMapProducer;
 
 /** A default implementation for the retrace api using the ClassNameMapper defined in R8. */
 @Keep
@@ -21,8 +23,19 @@
     this.classNameMapper = classNameMapper;
   }
 
-  public static RetraceApi create(ClassNameMapper classNameMapper) {
-    return new Retracer(classNameMapper);
+  public static RetraceApi create(
+      ProguardMapProducer proguardMapProducer, DiagnosticsHandler diagnosticsHandler) {
+    if (proguardMapProducer instanceof DirectClassNameMapperProguardMapProducer) {
+      return new Retracer(
+          ((DirectClassNameMapperProguardMapProducer) proguardMapProducer).getClassNameMapper());
+    }
+    try {
+      ClassNameMapper classNameMapper =
+          ClassNameMapper.mapperFromString(proguardMapProducer.get(), diagnosticsHandler);
+      return new Retracer(classNameMapper);
+    } catch (Throwable throwable) {
+      throw new InvalidMappingFileException(throwable);
+    }
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
index 15ed0d9..85e6cbc 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
@@ -7,6 +7,7 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.StringContains.containsString;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -38,6 +39,8 @@
 public class RetraceCommandLineTests {
 
   private static final boolean testExternal = false;
+  private static final String WAITING_MESSAGE =
+      "Waiting for stack-trace input..." + StringUtils.LINE_SEPARATOR;
 
   @Rule public TemporaryFolder folder = new TemporaryFolder();
 
@@ -64,6 +67,18 @@
   }
 
   @Test
+  public void testInvalidMappingFile() throws IOException {
+    Path mappingFile = folder.newFile("mapping.txt").toPath();
+    Files.write(mappingFile, "foo.bar.baz <- is invalid mapping".getBytes());
+    Path stackTraceFile = folder.newFile("stacktrace.txt").toPath();
+    Files.write(stackTraceFile, new byte[0]);
+    runAbortTest(
+        containsString("Unable to parse mapping file"),
+        mappingFile.toString(),
+        stackTraceFile.toString());
+  }
+
+  @Test
   public void testVerbose() throws IOException {
     FoundMethodVerboseStackTrace stackTrace = new FoundMethodVerboseStackTrace();
     runTest(
@@ -135,7 +150,7 @@
   public void testHelp() throws IOException {
     ProcessResult processResult = runRetraceCommandLine(null, Arrays.asList("--help"));
     assertEquals(0, processResult.exitCode);
-    assertEquals(Retrace.USAGE_MESSAGE, processResult.stdout);
+    assertThat(processResult.stdout, containsString(Retrace.USAGE_MESSAGE));
   }
 
   @Test
@@ -155,6 +170,12 @@
     runTest("", SMILEY_EMOJI, true, SMILEY_EMOJI + StringUtils.LINE_SEPARATOR);
   }
 
+  @Test
+  public void testHelpMessageOnStdIn() throws IOException {
+    ProcessResult processResult = runRetrace("", "", true);
+    assertTrue(processResult.stdout.startsWith(WAITING_MESSAGE));
+  }
+
   private final String nonMappableStackTrace =
       StringUtils.lines(
           "com.android.r8.R8Exception: Problem when compiling program",
@@ -171,7 +192,12 @@
       throws IOException {
     ProcessResult result = runRetrace(mapping, stackTrace, stacktraceStdIn, args);
     assertEquals(0, result.exitCode);
-    assertEquals(expected, result.stdout);
+    String stdOut = result.stdout;
+    if (stacktraceStdIn) {
+      assertTrue(result.stdout.startsWith(WAITING_MESSAGE));
+      stdOut = result.stdout.substring(WAITING_MESSAGE.length());
+    }
+    assertEquals(expected, stdOut);
   }
 
   private void runAbortTest(Matcher<String> errorMatch, String... args) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index 731e807..bffcd14 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestDiagnosticMessagesImpl;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.retrace.Retrace.RetraceAbortException;
 import com.android.tools.r8.retrace.stacktraces.ActualBotStackTraceBase;
 import com.android.tools.r8.retrace.stacktraces.ActualIdentityStackTrace;
@@ -203,11 +202,8 @@
 
   private void inspectRetraceTest(
       StackTraceForTest stackTraceForTest, Consumer<RetraceApi> inspection) throws Exception {
-    TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
-    ClassNameMapper classNameMapper =
-        ClassNameMapper.mapperFromString(stackTraceForTest.mapping(), diagnosticsHandler);
-    RetraceApi retracer = Retracer.create(classNameMapper);
-    inspection.accept(retracer);
+    inspection.accept(
+        Retracer.create(stackTraceForTest::mapping, new TestDiagnosticMessagesImpl()));
   }
 
   private TestDiagnosticMessagesImpl runRetraceTest(StackTraceForTest stackTraceForTest) {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 6a5b949..522109a 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -7,6 +7,7 @@
 
 import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfTryCatch;
 import com.android.tools.r8.code.Instruction;
@@ -38,6 +39,7 @@
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.DirectClassNameMapperProguardMapProducer;
 import com.android.tools.r8.retrace.RetraceApi;
 import com.android.tools.r8.retrace.Retracer;
 import com.android.tools.r8.utils.AndroidApp;
@@ -486,6 +488,24 @@
   }
 
   public RetraceApi retrace() {
-    return Retracer.create(mapping);
+    return Retracer.create(
+        new InternalProguardMapProducer(
+            mapping == null ? ClassNameMapper.builder().build() : mapping),
+        new TestDiagnosticMessagesImpl());
+  }
+
+  public static class InternalProguardMapProducer
+      implements DirectClassNameMapperProguardMapProducer {
+
+    public final ClassNameMapper prebuiltMapper;
+
+    public InternalProguardMapProducer(ClassNameMapper prebuiltMapper) {
+      this.prebuiltMapper = prebuiltMapper;
+    }
+
+    @Override
+    public ClassNameMapper getClassNameMapper() {
+      return prebuiltMapper;
+    }
   }
 }