Only update canonicalized call-sites once

Bug: b/289788048
Change-Id: I7c3496dac9d4803361bfef14d30c6de63997ff87
diff --git a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
index 04e7817..e4fc96d 100644
--- a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
+++ b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
@@ -858,8 +858,13 @@
               Comparator.comparing(mapping -> mapping.getOutline().toString()));
       IntBox firstAvailableRange = new IntBox(lastComposedRange.minifiedRange.to + 1);
       for (OutlineCallsiteMappingInformation outlineCallSite : outlineCallSites) {
-        for (ComputedMappedRangeForOutline computedMappedRangeForOutline :
-            outlineCallsitesToPatchUp.get(outlineCallSite)) {
+        // It should be sufficient to patch any call-site because they are referencing the same
+        // outline call-site information due to using an identity hashmap.
+        List<ComputedMappedRangeForOutline> computedMappedRangeForOutlines =
+            outlineCallsitesToPatchUp.get(outlineCallSite);
+        assert verifyAllOutlineCallSitesAreEqualTo(outlineCallSite, computedMappedRangeForOutlines);
+        ComputedMappedRangeForOutline computedMappedRangeForOutline =
+            ListUtils.first(computedMappedRangeForOutlines);
           Int2IntSortedMap newPositionMap =
               new Int2IntLinkedOpenHashMap(outlineCallSite.getPositions().size());
           visitOutlineMappedPositions(
@@ -892,7 +897,19 @@
                 outlineCallSite.setPositionsInternal(newPositionMap);
               });
         }
+    }
+
+    private boolean verifyAllOutlineCallSitesAreEqualTo(
+        OutlineCallsiteMappingInformation outlineCallSite,
+        List<ComputedMappedRangeForOutline> computedMappedRangeForOutlines) {
+      for (ComputedMappedRangeForOutline computedMappedRangeForOutline :
+          computedMappedRangeForOutlines) {
+        for (OutlineCallsiteMappingInformation outlineCallsiteMappingInformation :
+            computedMappedRangeForOutline.composed.getOutlineCallsiteInformation()) {
+          assert outlineCallSite == outlineCallsiteMappingInformation;
+        }
       }
+      return true;
     }
 
     /**
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateOutlineTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateOutlineTest.java
index 381afdd1..5c15d3d 100644
--- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateOutlineTest.java
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateOutlineTest.java
@@ -4,13 +4,13 @@
 
 package com.android.tools.r8.mappingcompose;
 
-import static org.junit.Assert.assertThrows;
+import static com.android.tools.r8.mappingcompose.ComposeTestHelpers.doubleToSingleQuote;
+import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.naming.MappingComposeException;
 import com.android.tools.r8.naming.MappingComposer;
 import com.android.tools.r8.utils.StringUtils;
 import org.junit.Test;
@@ -54,13 +54,34 @@
           // mappings in D8.
           "    1:10:void a():1:1 -> b",
           "    11:20:void a():1:1 -> b");
+  private static final String mappingResult =
+      StringUtils.unixLines(
+          "# {'id':'com.android.tools.r8.mapping','version':'2.2'}",
+          "package.Class -> package.new_internal.Y:",
+          "# {'id':'sourceFile','fileName':'FieldDefinition.java'}",
+          "    1:10:void foo():21:21 -> b",
+          "    # {'id':'com.android.tools.r8.outlineCallsite',"
+              + "'positions':{'1':21,'2':22},"
+              + "'outline':'Lpackage/internal/X;a(JJJ)J'}",
+          "    11:20:void foo():21:21 -> b",
+          "    # {'id':'com.android.tools.r8.outlineCallsite',"
+              + "'positions':{'1':21,'2':22},"
+              + "'outline':'Lpackage/internal/X;a(JJJ)J'}",
+          "    21:21:void foo():1337:1337 -> b",
+          "    22:22:void foo():44:44 -> b",
+          "package.Class$$ExternalSyntheticOutline0 -> package.internal.X:",
+          "# {'id':'sourceFile','fileName':'R8$$SyntheticClass'}",
+          "# {'id':'com.android.tools.r8.synthesized'}",
+          "    1:2:long package.Int2IntLinkedOpenHashMap"
+              + "$$InternalSyntheticOutline$HASH$0.m(long,long,long):0:1 -> a",
+          "    # {'id':'com.android.tools.r8.synthesized'}",
+          "    # {'id':'com.android.tools.r8.outline'}");
 
   @Test
   public void testCompose() throws Exception {
     ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
     ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
-    // TODO(b/289788048): We should be able to handle less precise input.
-    assertThrows(
-        MappingComposeException.class, () -> MappingComposer.compose(mappingForFoo, mappingForBar));
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    assertEquals(mappingResult, doubleToSingleQuote(composed));
   }
 }