Version 2.1.7-r3

Cherry pick: Update synthesized $load method names
CL: https://r8-review.googlesource.com/c/r8/+/51402

Cherry pick: Workaround for non-determinism in service loader rewriter
CL: https://r8-review.googlesource.com/c/r8/+/51384

Cherry pick: Update ServiceLoaderRewriter to synchronize on synthetic clazz creation
CL: https://r8-review.googlesource.com/c/r8/+/50340

Cherry pick: Use AtomicInteger for increment synthesized service-loader methods
CL: https://r8-review.googlesource.com/c/r8/+/48420

Bug: 148929520
Bug: 153127674
Bug: 156054499
Bug: 162704239
Change-Id: I09a14a24d160338f20c0b7b972738d42103377cb
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 0eb833c..311ebc0 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.1.7-r2";
+  public static final String LABEL = "2.1.7-r3";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 8cb71bb..b3e26f1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.graph;
 
 import static com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement.computeLeastUpperBoundOfInterfaces;
+import static com.android.tools.r8.ir.optimize.ServiceLoaderRewriter.SERVICE_LOADER_CLASS_NAME;
 import static com.google.common.base.Predicates.alwaysTrue;
 
 import com.android.tools.r8.dex.Constants;
@@ -352,7 +353,8 @@
       createStaticallyKnownType(invocationHandlerDescriptor);
   public final DexType proxyType = createStaticallyKnownType(proxyDescriptor);
   public final DexType serviceLoaderType = createStaticallyKnownType(serviceLoaderDescriptor);
-
+  public final DexType serviceLoaderRewrittenClassType =
+      createStaticallyKnownType("L" + SERVICE_LOADER_CLASS_NAME + ";");
   public final DexType serviceLoaderConfigurationErrorType =
       createStaticallyKnownType(serviceLoaderConfigurationErrorDescriptor);
   public final DexType listType = createStaticallyKnownType(listDescriptor);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
index 856baa9..506b981 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -30,11 +30,14 @@
 import com.android.tools.r8.ir.desugar.ServiceLoaderSourceCode;
 import com.android.tools.r8.origin.SynthesizedOrigin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.IntBox;
+import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * ServiceLoaderRewriter will attempt to rewrite calls on the form of: ServiceLoader.load(X.class,
@@ -67,8 +70,9 @@
 
   public static final String SERVICE_LOADER_CLASS_NAME = "$$ServiceLoaderMethods";
   private static final String SERVICE_LOADER_METHOD_PREFIX_NAME = "$load";
+  private static final int SERVICE_LOADER_METHOD_HASH_LENGTH = 7;
 
-  private DexProgramClass synthesizedClass;
+  private AtomicReference<DexProgramClass> synthesizedClass = new AtomicReference<>();
   private ConcurrentHashMap<DexType, DexEncodedMethod> synthesizedServiceLoaders =
       new ConcurrentHashMap<>();
 
@@ -79,12 +83,13 @@
   }
 
   public DexProgramClass getSynthesizedClass() {
-    return synthesizedClass;
+    return synthesizedClass.get();
   }
 
   public void rewrite(IRCode code) {
     DexItemFactory factory = appView.dexItemFactory();
     InstructionListIterator instructionIterator = code.instructionListIterator();
+    IntBox synthesizedLoadMethodCounter = new IntBox();
     while (instructionIterator.hasNext()) {
       Instruction instruction = instructionIterator.next();
 
@@ -168,7 +173,9 @@
           synthesizedServiceLoaders.computeIfAbsent(
               constClass.getValue(),
               service -> {
-                DexEncodedMethod addedMethod = createSynthesizedMethod(service, classes);
+                DexEncodedMethod addedMethod =
+                    createSynthesizedMethod(
+                        service, classes, code.method, synthesizedLoadMethodCounter);
                 if (appView.options().isGeneratingClassFiles()) {
                   addedMethod.upgradeClassFileVersion(code.method.getClassFileVersion());
                 }
@@ -180,55 +187,86 @@
     }
   }
 
-  private DexEncodedMethod createSynthesizedMethod(DexType serviceType, List<DexClass> classes) {
-    DexType serviceLoaderType =
-        appView.dexItemFactory().createType("L" + SERVICE_LOADER_CLASS_NAME + ";");
-    if (synthesizedClass == null) {
-      assert !appView.options().encodeChecksums;
-      ChecksumSupplier checksumSupplier = DexProgramClass::invalidChecksumRequest;
-      synthesizedClass =
-          new DexProgramClass(
-              serviceLoaderType,
-              null,
-              new SynthesizedOrigin("Service Loader desugaring", getClass()),
-              ClassAccessFlags.fromDexAccessFlags(
-                  Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC),
-              appView.dexItemFactory().objectType,
-              DexTypeList.empty(),
-              appView.dexItemFactory().createString("ServiceLoader"),
-              null,
-              Collections.emptyList(),
-              null,
-              Collections.emptyList(),
-              DexAnnotationSet.empty(),
-              DexEncodedField.EMPTY_ARRAY, // Static fields.
-              DexEncodedField.EMPTY_ARRAY, // Instance fields.
-              DexEncodedMethod.EMPTY_ARRAY,
-              DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
-              appView.dexItemFactory().getSkipNameValidationForTesting(),
-              checksumSupplier);
-      appView.appInfo().addSynthesizedClass(synthesizedClass);
-    }
+  private DexEncodedMethod createSynthesizedMethod(
+      DexType serviceType,
+      List<DexClass> classes,
+      DexEncodedMethod context,
+      IntBox synthesizedLoadMethodCounter) {
+    DexProgramClass synthesizedClass = getOrSetSynthesizedClass();
+    String hashCode = Integer.toString(context.method.hashCode());
+    String methodNamePrefix =
+        SERVICE_LOADER_METHOD_PREFIX_NAME
+            + "$"
+            + StringUtils.replaceAll(context.method.holder.toSourceString(), ".", "$")
+            + "$"
+            + (context.isInitializer()
+                ? (context.isClassInitializer() ? "$clinit" : "$init")
+                : context.method.name.toSourceString())
+            + "$"
+            + hashCode.substring(0, Math.min(SERVICE_LOADER_METHOD_HASH_LENGTH, hashCode.length()))
+            + "$";
     DexProto proto = appView.dexItemFactory().createProto(appView.dexItemFactory().iteratorType);
-    DexMethod method =
-        appView
-            .dexItemFactory()
-            .createMethod(
-                serviceLoaderType,
-                proto,
-                SERVICE_LOADER_METHOD_PREFIX_NAME + synthesizedServiceLoaders.size());
-    MethodAccessFlags methodAccess =
-        MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_STATIC, false);
-    DexEncodedMethod encodedMethod =
-        new DexEncodedMethod(
-            method,
-            methodAccess,
-            DexAnnotationSet.empty(),
-            ParameterAnnotationsList.empty(),
-            ServiceLoaderSourceCode.generate(serviceType, classes, appView.dexItemFactory()),
-            true);
-    synthesizedClass.addDirectMethod(encodedMethod);
-    return encodedMethod;
+    synchronized (synthesizedClass) {
+      DexMethod methodReference;
+      do {
+        methodReference =
+            appView
+                .dexItemFactory()
+                .createMethod(
+                    appView.dexItemFactory().serviceLoaderRewrittenClassType,
+                    proto,
+                    methodNamePrefix + "$" + synthesizedLoadMethodCounter.getAndIncrement());
+      } while (synthesizedClass.lookupMethod(methodReference) != null);
+      DexEncodedMethod method =
+          new DexEncodedMethod(
+              methodReference,
+              MethodAccessFlags.fromSharedAccessFlags(
+                  Constants.ACC_PUBLIC | Constants.ACC_STATIC, false),
+              DexAnnotationSet.empty(),
+              ParameterAnnotationsList.empty(),
+              ServiceLoaderSourceCode.generate(serviceType, classes, appView.dexItemFactory()),
+              true);
+      synthesizedClass.addDirectMethod(method);
+      return method;
+    }
+  }
+
+  private DexProgramClass getOrSetSynthesizedClass() {
+    if (synthesizedClass.get() != null) {
+      return synthesizedClass.get();
+    }
+    assert !appView.options().encodeChecksums;
+    ChecksumSupplier checksumSupplier = DexProgramClass::invalidChecksumRequest;
+    DexProgramClass clazz =
+        synthesizedClass.updateAndGet(
+            existingClazz -> {
+              if (existingClazz != null) {
+                return existingClazz;
+              }
+              return new DexProgramClass(
+                  appView.dexItemFactory().serviceLoaderRewrittenClassType,
+                  null,
+                  new SynthesizedOrigin("Service Loader desugaring", getClass()),
+                  ClassAccessFlags.fromDexAccessFlags(
+                      Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC),
+                  appView.dexItemFactory().objectType,
+                  DexTypeList.empty(),
+                  appView.dexItemFactory().createString("ServiceLoader"),
+                  null,
+                  Collections.emptyList(),
+                  null,
+                  Collections.emptyList(),
+                  DexAnnotationSet.empty(),
+                  DexEncodedField.EMPTY_ARRAY, // Static fields.
+                  DexEncodedField.EMPTY_ARRAY, // Instance fields.
+                  DexEncodedMethod.EMPTY_ARRAY,
+                  DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
+                  appView.dexItemFactory().getSkipNameValidationForTesting(),
+                  checksumSupplier);
+            });
+    assert clazz != null;
+    appView.appInfo().addSynthesizedClass(clazz);
+    return clazz;
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/utils/IntBox.java b/src/main/java/com/android/tools/r8/utils/IntBox.java
new file mode 100644
index 0000000..ebb92ca
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/IntBox.java
@@ -0,0 +1,32 @@
+// 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.utils;
+
+public class IntBox {
+
+  private int value;
+
+  public IntBox() {}
+
+  public IntBox(int initialValue) {
+    set(initialValue);
+  }
+
+  public int get() {
+    return value;
+  }
+
+  public int getAndIncrement() {
+    return value++;
+  }
+
+  public void increment() {
+    value++;
+  }
+
+  public void set(int value) {
+    this.value = value;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
index fdf7e98..02e83de 100644
--- a/src/main/java/com/android/tools/r8/utils/StringUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -11,6 +11,8 @@
 import java.util.Collection;
 import java.util.List;
 import java.util.function.Function;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 public class StringUtils {
   public static char[] EMPTY_CHAR_ARRAY = {};
@@ -327,4 +329,8 @@
     }
     return string.length();
   }
+
+  public static String replaceAll(String subject, String target, String replacement) {
+    return subject.replaceAll(Pattern.quote(target), Matcher.quoteReplacement(replacement));
+  }
 }