Only emit the outline positions once for each mapped position

As a side note this also fixes b/284938939 since we emit synthetic outline caller positions after all real positions in the method mapping block.

Bug: b/263357015
Bug: b/284938939
Bug: b/293630963
Bug: b/294904638
Change-Id: I76fac27234be6b231c538109d1cf2e86f5ae7155
diff --git a/src/main/java/com/android/tools/r8/ir/code/Position.java b/src/main/java/com/android/tools/r8/ir/code/Position.java
index b27f447..c7829a5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Position.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Position.java
@@ -274,6 +274,10 @@
 
   public abstract PositionBuilder<?, ?> builderWithCopy();
 
+  public OutlineCallerPosition asOutlineCaller() {
+    return null;
+  }
+
   public abstract static class PositionBuilder<
       P extends Position, B extends PositionBuilder<P, B>> {
 
@@ -608,6 +612,11 @@
     }
 
     @Override
+    public OutlineCallerPosition asOutlineCaller() {
+      return this;
+    }
+
+    @Override
     public DexMethod getOutlineCallee() {
       return outlineCallee;
     }
diff --git a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
index 694fe01..7c2c9d6 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Position.OutlineCallerPosition;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.ClassNaming;
 import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
@@ -56,6 +57,7 @@
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -294,6 +296,8 @@
 
       mappedPositions.sort(Comparator.comparing(MappedPosition::getObfuscatedLine));
 
+      Map<OutlineCallerPosition, MappedRange> outlineCallerPositions = new LinkedHashMap<>();
+
       // Update memberNaming with the collected positions, merging multiple positions into a
       // single region whenever possible.
       for (int i = 0; i < mappedPositions.size(); /* updated in body */ ) {
@@ -344,44 +348,51 @@
                 originalRange,
                 prunedInlinedClasses,
                 cardinalRangeCache);
+        // firstPosition will contain a potential outline caller.
+        if (firstPosition.isOutlineCaller()) {
+          outlineCallerPositions.putIfAbsent(firstPosition.asOutlineCaller(), lastMappedRange);
+        }
         methodSpecificMappingInformation.consume(
             info -> lastMappedRange.addMappingInformation(info, Unreachable::raise));
-        // firstPosition will contain a potential outline caller.
-        if (firstPosition.getOutlineCallee() != null) {
-          Int2IntMap positionMap = new Int2IntArrayMap();
-          int maxPc = ListUtils.last(mappedPositions).getObfuscatedLine();
-          firstPosition
-              .getOutlinePositions()
-              .forEach(
-                  (line, position) -> {
-                    int placeHolderLineToBeFixed;
-                    if (canUseDexPc) {
-                      placeHolderLineToBeFixed = maxPc + line + 1;
-                    } else {
-                      placeHolderLineToBeFixed =
-                          positionRemapper.createRemappedPosition(position).getSecond().getLine();
-                    }
-                    positionMap.put((int) line, placeHolderLineToBeFixed);
-                    getMappedRangesForPosition(
-                        appView,
-                        getOriginalMethodSignature,
-                        getBuilder(),
-                        position,
-                        residualSignature,
-                        nonCardinalRangeCache.get(
-                            placeHolderLineToBeFixed, placeHolderLineToBeFixed),
-                        nonCardinalRangeCache.get(position.getLine(), position.getLine()),
-                        prunedInlinedClasses,
-                        cardinalRangeCache);
-                  });
-          outlinesToFix
-              .computeIfAbsent(
-                  firstPosition.getOutlineCallee(),
-                  outline -> new OutlineFixupBuilder(computeMappedMethod(outline, appView)))
-              .addMappedRangeForOutlineCallee(lastMappedRange, positionMap);
-        }
         i = j;
       }
+      IntBox maxPc = new IntBox(ListUtils.last(mappedPositions).getObfuscatedLine());
+      for (Map.Entry<OutlineCallerPosition, MappedRange> outlinePositionEntry :
+          outlineCallerPositions.entrySet()) {
+        Int2IntMap positionMap = new Int2IntArrayMap();
+        outlinePositionEntry
+            .getKey()
+            .getOutlinePositions()
+            .forEach(
+                (line, position) -> {
+                  int placeHolderLineToBeFixed;
+                  if (canUseDexPc) {
+                    placeHolderLineToBeFixed = maxPc.get() + line + 1;
+                  } else {
+                    placeHolderLineToBeFixed =
+                        positionRemapper.createRemappedPosition(position).getSecond().getLine();
+                  }
+                  positionMap.put((int) line, placeHolderLineToBeFixed);
+                  MappedRange lastRange =
+                      getMappedRangesForPosition(
+                          appView,
+                          getOriginalMethodSignature,
+                          getBuilder(),
+                          position,
+                          residualSignature,
+                          nonCardinalRangeCache.get(
+                              placeHolderLineToBeFixed, placeHolderLineToBeFixed),
+                          nonCardinalRangeCache.get(position.getLine(), position.getLine()),
+                          prunedInlinedClasses,
+                          cardinalRangeCache);
+                  maxPc.set(lastRange.minifiedRange.to);
+                });
+        outlinesToFix
+            .computeIfAbsent(
+                outlinePositionEntry.getKey().getOutlineCallee(),
+                outline -> new OutlineFixupBuilder(computeMappedMethod(outline, appView)))
+            .addMappedRangeForOutlineCallee(outlinePositionEntry.getValue(), positionMap);
+      }
       assert mappedPositions.size() <= 1
           || getBuilder().hasNoOverlappingRangesForSignature(residualSignature);
       return this;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineMappingInformationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineMappingInformationTest.java
index 362ac40..a51667e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineMappingInformationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineMappingInformationTest.java
@@ -98,9 +98,8 @@
     String proguardMap = compileResult.getProguardMap();
 
     if (parameters.isDexRuntime()) {
-      // TODO(b/263357015, b/293630963): Outline information is duplicated for pc encoding.
-      assertEquals(
-          20, StringUtils.occurrences(proguardMap, "com.android.tools.r8.outlineCallsite"));
+      // TODO(b/263357015, b/293630963): Outline information is not reset for new default events.
+      assertEquals(6, StringUtils.occurrences(proguardMap, "com.android.tools.r8.outlineCallsite"));
     } else {
       assertEquals(4, StringUtils.occurrences(proguardMap, "com.android.tools.r8.outlineCallsite"));
     }