Revert "Remove handling of shared synthetics in output"

This reverts commit 5cf6624c2b6263c6554f7f64823f930a8c804891.

Reason for revert: Still used in tests

Change-Id: Id25b4eea24b5e1edee7d6d47d899872603597e3c
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 90af811..fa7e21d 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -641,11 +641,12 @@
     }
     // At least one method needs a jumbo string in which case we construct a thread local mapping
     // for all code objects and write the processed results into that map.
-    // TODO(b/181636450): Reconsider the code mapping setup now that synthetics are never duplicated
-    //  in outputs.
     Map<DexEncodedMethod, DexCode> codeMapping = new IdentityHashMap<>();
     for (DexProgramClass clazz : classes) {
-      assert !appView.getSyntheticItems().isSharedSynthetic(clazz);
+      // TODO(b/181636450): Reconsider the code mapping setup now that synthetics are never
+      //  duplicated in outputs.
+      boolean isSharedSynthetic =
+          appView.getSyntheticItems().getSynthesizingContexts(clazz.getType()).size() > 1;
       clazz.forEachMethod(
           method -> {
             DexCode code =
@@ -654,10 +655,12 @@
                     application.dexItemFactory,
                     options.testing.forceJumboStringProcessing);
             codeMapping.put(method, code);
-            // The mapping now has ownership of the methods code object. This ensures freeing of
-            // code resources once the map entry is cleared and also ensures that we don't end up
-            // using the incorrect code pointer again later!
-            method.removeCode();
+            if (!isSharedSynthetic) {
+              // If the class is not a shared class the mapping now has ownership of the methods
+              // code object. This ensures freeing of code resources once the map entry is cleared
+              // and also ensures that we don't end up using the incorrect code pointer again later!
+              method.removeCode();
+            }
           });
     }
     return MethodToCodeObjectMapping.fromMapBacking(codeMapping);
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 56a6e0c..889fc27 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -653,7 +653,8 @@
     }
   }
 
-  private void writeEncodedMethods(Iterable<DexEncodedMethod> unsortedMethods) {
+  private void writeEncodedMethods(
+      Iterable<DexEncodedMethod> unsortedMethods, boolean isSharedSynthetic) {
     List<DexEncodedMethod> methods = IterableUtils.toNewArrayList(unsortedMethods);
     methods.sort(
         (a, b) ->
@@ -674,7 +675,7 @@
         dest.putUleb128(mixedSectionOffsets.getOffsetFor(code));
         // Writing the methods starts to take up memory so we are going to flush the
         // code objects since they are no longer necessary after this.
-        codeMapping.clearCode(method);
+        codeMapping.clearCode(method, isSharedSynthetic);
       }
     }
   }
@@ -688,9 +689,10 @@
     dest.putUleb128(clazz.getMethodCollection().numberOfVirtualMethods());
     writeEncodedFields(clazz.staticFields());
     writeEncodedFields(clazz.instanceFields());
-    assert !appInfo.getSyntheticItems().isSharedSynthetic(clazz);
-    writeEncodedMethods(clazz.directMethods());
-    writeEncodedMethods(clazz.virtualMethods());
+    boolean isSharedSynthetic =
+        appInfo.getSyntheticItems().getSynthesizingContexts(clazz.getType()).size() > 1;
+    writeEncodedMethods(clazz.directMethods(), isSharedSynthetic);
+    writeEncodedMethods(clazz.virtualMethods(), isSharedSynthetic);
   }
 
   private void addStaticFieldValues(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/dex/MethodToCodeObjectMapping.java b/src/main/java/com/android/tools/r8/dex/MethodToCodeObjectMapping.java
index 5541398..feeb64b 100644
--- a/src/main/java/com/android/tools/r8/dex/MethodToCodeObjectMapping.java
+++ b/src/main/java/com/android/tools/r8/dex/MethodToCodeObjectMapping.java
@@ -13,7 +13,7 @@
 
   public abstract DexCode getCode(DexEncodedMethod method);
 
-  public abstract void clearCode(DexEncodedMethod method);
+  public abstract void clearCode(DexEncodedMethod method, boolean isSharedSynthetic);
 
   public abstract boolean verifyCodeObjects(Collection<DexCode> codes);
 
@@ -37,8 +37,11 @@
     }
 
     @Override
-    public void clearCode(DexEncodedMethod method) {
-      method.removeCode();
+    public void clearCode(DexEncodedMethod method, boolean isSharedSynthetic) {
+      // When using methods directly any shared class needs to maintain its methods as read-only.
+      if (!isSharedSynthetic) {
+        method.removeCode();
+      }
     }
 
     @Override
@@ -61,7 +64,7 @@
     }
 
     @Override
-    public void clearCode(DexEncodedMethod method) {
+    public void clearCode(DexEncodedMethod method, boolean isSharedSynthetic) {
       // We can safely clear the thread local pointer to even shared methods.
       codes.put(method, null);
     }
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index a11e0ab..e44973a 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -329,10 +329,11 @@
       Collection<DexProgramClass> synthetics = new ArrayList<>();
       // Assign dedicated virtual files for all program classes.
       for (DexProgramClass clazz : appView.appInfo().classes()) {
+        Collection<DexType> contexts =
+            appView.getSyntheticItems().getSynthesizingContexts(clazz.getType());
         // TODO(b/181636450): Simplify this making use of the assumption that synthetics are never
         //  duplicated.
-        if (!combineSyntheticClassesWithPrimaryClass
-            || !appView.getSyntheticItems().isSyntheticClass(clazz)) {
+        if (!combineSyntheticClassesWithPrimaryClass || contexts.isEmpty()) {
           VirtualFile file =
               new VirtualFile(
                   virtualFiles.size(),
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 28401a5..d6c93aa 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -267,10 +267,6 @@
     return Collections.emptyList();
   }
 
-  public boolean isSharedSynthetic(DexProgramClass clazz) {
-    return getSynthesizingContexts(clazz.getType()).size() >= 2;
-  }
-
   // TODO(b/180091213): Implement this and remove client provided the oracle.
   public Set<DexReference> getSynthesizingContexts(
       DexProgramClass clazz, SynthesizingContextOracle oracle) {