Don't encode stateless lambdas as singletons.

Bug: 222081665
Change-Id: I3a047573ca41a59fa4b665c10c489dbd717751fd
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index a014283..27298a9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -99,13 +99,15 @@
 
     this.target = createTarget(accessedFrom);
 
-    boolean stateless = isStateless();
+    boolean statelessSingleton = isStatelessSingleton();
     this.classConstructor =
-        !stateless
-            ? null
-            : factory.createMethod(type, constructorProto, factory.classConstructorMethodName);
+        statelessSingleton
+            ? factory.createMethod(type, constructorProto, factory.classConstructorMethodName)
+            : null;
     this.lambdaField =
-        !stateless ? null : factory.createField(type, type, factory.lambdaInstanceFieldName);
+        statelessSingleton
+            ? factory.createField(type, type, factory.lambdaInstanceFieldName)
+            : null;
 
     // Synthesize the program class once all fields are set.
     synthesizeLambdaClass(builder, desugarInvoke);
@@ -145,8 +147,8 @@
             appView.dexItemFactory().createString("f$" + index));
   }
 
-  public final boolean isStateless() {
-    return descriptor.isStateless();
+  public final boolean isStatelessSingleton() {
+    return appView.options().createSingletonsForStatelessLambdas && descriptor.isStateless();
   }
 
   // Synthesize virtual methods.
@@ -193,13 +195,14 @@
 
   // Synthesize direct methods.
   private void synthesizeDirectMethods(SyntheticProgramClassBuilder builder) {
-    boolean stateless = isStateless();
-    List<DexEncodedMethod> methods = new ArrayList<>(stateless ? 2 : 1);
+    boolean statelessSingleton = isStatelessSingleton();
+    List<DexEncodedMethod> methods = new ArrayList<>(statelessSingleton ? 2 : 1);
 
     // Constructor.
     MethodAccessFlags accessFlags =
         MethodAccessFlags.fromSharedAccessFlags(
-            (stateless ? Constants.ACC_PRIVATE : Constants.ACC_PUBLIC) | Constants.ACC_SYNTHETIC,
+            (statelessSingleton ? Constants.ACC_PRIVATE : Constants.ACC_PUBLIC)
+                | Constants.ACC_SYNTHETIC,
             true);
     methods.add(
         DexEncodedMethod.syntheticBuilder()
@@ -211,7 +214,7 @@
             .build());
 
     // Class constructor for stateless lambda classes.
-    if (stateless) {
+    if (statelessSingleton) {
       methods.add(
           DexEncodedMethod.syntheticBuilder()
               .setMethod(classConstructor)
@@ -249,7 +252,7 @@
 
   // Synthesize static fields to represent singleton instance.
   private void synthesizeStaticFields(SyntheticProgramClassBuilder builder) {
-    if (isStateless()) {
+    if (isStatelessSingleton()) {
       // Create instance field for stateless lambda.
       assert this.lambdaField != null;
       builder.setStaticFields(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
index 4c45086..f7e8d1a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
@@ -121,7 +121,7 @@
 
     eventConsumer.acceptLambdaClass(lambdaClass, context);
 
-    if (lambdaClass.isStateless()) {
+    if (lambdaClass.isStatelessSingleton()) {
       return ImmutableList.of(
           new CfStaticFieldRead(lambdaClass.lambdaField, lambdaClass.lambdaField));
     }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 2b8d72a..2408193 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -359,6 +359,10 @@
   // Boolean value indicating that byte code pass through may be enabled.
   public boolean enableCfByteCodePassThrough = false;
 
+  // Flag to control the representation of stateless lambdas.
+  // See b/222081665 for context.
+  public boolean createSingletonsForStatelessLambdas = false;
+
   // Contain the contents of the build properties file from the compiler command.
   public DumpOptions dumpOptions;
 
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index ec8d314..8478b17 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -131,7 +131,7 @@
         .withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K))
         .withBuilderTransformation(
             b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
-        .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaring"))
+        .withDexCheck(inspector -> checkLambdaCount(inspector, 4, "lambdadesugaring"))
         .run();
   }
 
@@ -170,7 +170,7 @@
         .withMinApiLevel(AndroidApiLevel.N)
         .withBuilderTransformation(
             b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
-        .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaring"))
+        .withDexCheck(inspector -> checkLambdaCount(inspector, 4, "lambdadesugaring"))
         .run();
   }