Add mapping information for compiler synthesized classes.

Bug: 172014416
Change-Id: I854918696057ca97373da6b1170fd7689a05b4a3
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
index 51c3272..4a50244 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -172,7 +172,7 @@
 
     public static final NoSignature NO_SIGNATURE = new NoSignature();
 
-    public NoSignature() {
+    private NoSignature() {
       super("NO SIGNATURE");
     }
 
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java
new file mode 100644
index 0000000..b0a13ba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java
@@ -0,0 +1,45 @@
+// 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.naming.mappinginformation;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+public class CompilerSynthesizedMappingInformation extends MappingInformation {
+
+  public static final String ID = "com.android.tools.r8.synthesized";
+
+  public CompilerSynthesizedMappingInformation() {
+    super(NO_LINE_NUMBER);
+  }
+
+  @Override
+  public boolean isCompilerSynthesizedMappingInformation() {
+    return true;
+  }
+
+  @Override
+  public CompilerSynthesizedMappingInformation asCompilerSynthesizedMappingInformation() {
+    return this;
+  }
+
+  @Override
+  public boolean allowOther(MappingInformation information) {
+    return !information.isCompilerSynthesizedMappingInformation();
+  }
+
+  @Override
+  public String serialize() {
+    JsonObject result = new JsonObject();
+    result.add(MAPPING_ID_KEY, new JsonPrimitive(ID));
+    return result.toString();
+  }
+
+  public static CompilerSynthesizedMappingInformation deserialize(
+      JsonObject object, DiagnosticsHandler diagnosticsHandler, int lineNumber) {
+    return new CompilerSynthesizedMappingInformation();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
index c1c5105..cb0708f 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
@@ -50,6 +50,14 @@
     return null;
   }
 
+  public boolean isCompilerSynthesizedMappingInformation() {
+    return false;
+  }
+
+  public CompilerSynthesizedMappingInformation asCompilerSynthesizedMappingInformation() {
+    return null;
+  }
+
   public abstract boolean allowOther(MappingInformation information);
 
   public static MappingInformation fromJsonObject(
@@ -75,6 +83,9 @@
         return MethodSignatureChangedInformation.build(object, diagnosticsHandler, lineNumber);
       case FileNameInformation.ID:
         return FileNameInformation.build(object, diagnosticsHandler, lineNumber);
+      case CompilerSynthesizedMappingInformation.ID:
+        return CompilerSynthesizedMappingInformation.deserialize(
+            object, diagnosticsHandler, lineNumber);
       default:
         diagnosticsHandler.info(MappingInformationDiagnostics.noHandlerFor(lineNumber, idString));
         return null;
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 47ccd91..f8ad5bb 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -47,6 +47,7 @@
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.naming.Range;
+import com.android.tools.r8.naming.mappinginformation.CompilerSynthesizedMappingInformation;
 import com.android.tools.r8.naming.mappinginformation.FileNameInformation;
 import com.android.tools.r8.retrace.internal.RetraceUtils;
 import com.android.tools.r8.shaking.KeepInfoCollection;
@@ -274,6 +275,8 @@
     ClassNameMapper.Builder classNameMapperBuilder = ClassNameMapper.builder();
     // Collect which files contain which classes that need to have their line numbers optimized.
     for (DexProgramClass clazz : application.classes()) {
+      boolean isSyntheticClass = appView.getSyntheticItems().isSyntheticClass(clazz);
+
       IdentityHashMap<DexString, List<DexEncodedMethod>> methodsByRenamedName =
           groupMethodsByRenamedName(appView.graphLens(), namingLens, clazz);
 
@@ -299,6 +302,12 @@
         }
       }
 
+      if (isSyntheticClass) {
+        onDemandClassNamingBuilder
+            .get()
+            .addMappingInformation(new CompilerSynthesizedMappingInformation());
+      }
+
       // If the class is renamed add it to the classNamingBuilder.
       addClassToClassNaming(originalType, renamedClassName, onDemandClassNamingBuilder);
 
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java b/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
index aa543c8..41d1c3f 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
@@ -5,15 +5,23 @@
 package com.android.tools.r8.retrace;
 
 import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileNameAndLineNumber;
+import static com.android.tools.r8.references.Reference.classFromClass;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isCompilerSynthesized;
 import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -48,22 +56,48 @@
         .run(parameters.getRuntime(), Main.class)
         .assertFailureWithErrorThatMatches(containsString("Hello World!"))
         .inspectStackTrace(
-            stackTrace -> {
-              assertThat(
-                  stackTrace,
-                  isSameExceptForFileNameAndLineNumber(
-                      StackTrace.builder()
-                          .addWithoutFileNameAndLineNumber(Main.class, JAVAC_LAMBDA_METHOD)
-                          // TODO(b/172014416): Support a D8 mapping and prune the synthetic.
-                          .applyIf(
-                              parameters.isDexRuntime(),
-                              b ->
-                                  b.addWithoutFileNameAndLineNumber(
-                                      SyntheticItemsTestUtils.syntheticLambdaClass(Main.class, 0),
-                                      "run"))
-                          .addWithoutFileNameAndLineNumber(Main.class, "runIt")
-                          .addWithoutFileNameAndLineNumber(Main.class, "main")
-                          .build()));
+            stackTrace ->
+                assertThat(
+                    stackTrace,
+                    isSameExceptForFileNameAndLineNumber(
+                        StackTrace.builder()
+                            .addWithoutFileNameAndLineNumber(Main.class, JAVAC_LAMBDA_METHOD)
+                            // TODO(b/172014416): Support a D8 mapping and prune the synthetic.
+                            .applyIf(
+                                parameters.isDexRuntime(),
+                                b ->
+                                    b.addWithoutFileNameAndLineNumber(
+                                        SyntheticItemsTestUtils.syntheticLambdaClass(Main.class, 0),
+                                        "run"))
+                            .addWithoutFileNameAndLineNumber(Main.class, "runIt")
+                            .addWithoutFileNameAndLineNumber(Main.class, "main")
+                            .build())));
+  }
+
+  @Test
+  public void testMappingInformation() throws Exception {
+    assumeTrue("R8/CF does not desugar", parameters.isDexRuntime());
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepAttributeSourceFile()
+        .addKeepAttributeLineNumberTable()
+        .noTreeShaking()
+        .noMinification()
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatMatches(containsString("Hello World!"))
+        .inspectFailure(
+            inspector -> {
+              Collection<ClassReference> inputs =
+                  ImmutableList.of(classFromClass(MyRunner.class), classFromClass(Main.class));
+              for (FoundClassSubject clazz : inspector.allClasses()) {
+                if (inputs.contains(clazz.getFinalReference())) {
+                  assertThat(clazz, not(isCompilerSynthesized()));
+                } else {
+                  assertThat(clazz, isCompilerSynthesized());
+                }
+              }
+              assertEquals(inputs.size() + 1, inspector.allClasses().size());
             });
   }
 
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index be358de..1763108 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -26,7 +26,9 @@
 import com.android.tools.r8.naming.MemberNaming;
 import com.android.tools.r8.naming.MemberNaming.FieldSignature;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.MemberNaming.NoSignature;
 import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.mappinginformation.MappingInformation;
 import com.android.tools.r8.naming.signature.GenericSignatureParser;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.FieldReference;
@@ -44,6 +46,7 @@
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -412,6 +415,27 @@
   }
 
   @Override
+  public boolean isCompilerSynthesized() {
+    if (naming == null) {
+      return false;
+    }
+    Map<Signature, List<MappingInformation>> additionalMappings = naming.getAdditionalMappings();
+    if (additionalMappings == null) {
+      return false;
+    }
+    List<MappingInformation> infos = additionalMappings.get(NoSignature.NO_SIGNATURE);
+    if (infos == null) {
+      return false;
+    }
+    for (MappingInformation info : infos) {
+      if (info.isCompilerSynthesizedMappingInformation()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @Override
   public boolean isLocalClass() {
     return dexClass.isLocalClass();
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
index 28f0b9c..e7db18c 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -218,6 +218,28 @@
     };
   }
 
+  public static Matcher<Subject> isCompilerSynthesized() {
+    return new TypeSafeMatcher<Subject>() {
+      @Override
+      protected boolean matchesSafely(Subject subject) {
+        return subject.isPresent() && subject.isCompilerSynthesized();
+      }
+
+      @Override
+      public void describeTo(Description description) {
+        description.appendText(" compiler synthesized");
+      }
+
+      @Override
+      public void describeMismatchSafely(final Subject subject, Description description) {
+        description
+            .appendText(type(subject) + " ")
+            .appendValue(name(subject))
+            .appendText(" was not");
+      }
+    };
+  }
+
   public static Matcher<ClassSubject> hasDefaultConstructor() {
     return new TypeSafeMatcher<ClassSubject>() {
       @Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Subject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Subject.java
index 05d83b7..29bc90b 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/Subject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Subject.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.utils.codeinspector;
 
+import com.android.tools.r8.errors.Unimplemented;
+
 public abstract class Subject {
 
   public abstract boolean isPresent();
@@ -11,4 +13,9 @@
   public abstract boolean isRenamed();
 
   public abstract boolean isSynthetic();
+
+  public boolean isCompilerSynthesized() {
+    throw new Unimplemented(
+        "Predicate not yet supported on Subject: " + getClass().getSimpleName());
+  }
 }