Rewrite invoke-custom instructions using enum unboxing lens

Bug: 172568606
Change-Id: Ia845ad4b78adbc70e4e1678880483292be7ba530
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 77a7d70..ad1663a 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -301,8 +301,7 @@
                     }
                   }
                   ObjectToOffsetMapping objectMapping =
-                      virtualFile.computeMapping(
-                          appView.appInfo(), graphLens, namingLens, initClassLens);
+                      virtualFile.computeMapping(appView, graphLens, namingLens, initClassLens);
                   MethodToCodeObjectMapping codeMapping =
                       rewriteCodeWithJumboStrings(
                           objectMapping, virtualFile.classes(), appView.appInfo().app());
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 b808097..108df1d 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
-import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexField;
@@ -210,10 +209,10 @@
   }
 
   public ObjectToOffsetMapping computeMapping(
-      AppInfo appInfo, GraphLens graphLens, NamingLens namingLens, InitClassLens initClassLens) {
+      AppView<?> appView, GraphLens graphLens, NamingLens namingLens, InitClassLens initClassLens) {
     assert transaction.isEmpty();
     return new ObjectToOffsetMapping(
-        appInfo,
+        appView,
         graphLens,
         namingLens,
         initClassLens,
@@ -742,7 +741,7 @@
       this.graphLens = graphLens;
       this.initClassLens = initClassLens;
       this.namingLens = namingLens;
-      this.rewriter = new LensCodeRewriterUtils(appView, graphLens);
+      this.rewriter = new LensCodeRewriterUtils(appView);
     }
 
     private <T extends DexItem> boolean maybeInsert(T item, Set<T> set, Set<T> baseSet) {
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
index b4797a9..c78f5ba 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
@@ -42,7 +42,7 @@
   private DexString firstJumboString;
 
   public ObjectToOffsetMapping(
-      AppInfo appInfo,
+      AppView<?> appView,
       GraphLens graphLens,
       NamingLens namingLens,
       InitClassLens initClassLens,
@@ -54,7 +54,7 @@
       Collection<DexString> strings,
       Collection<DexCallSite> callSites,
       Collection<DexMethodHandle> methodHandles) {
-    assert appInfo != null;
+    assert appView != null;
     assert graphLens != null;
     assert classes != null;
     assert protos != null;
@@ -68,8 +68,8 @@
     this.graphLens = graphLens;
     this.namingLens = namingLens;
     this.initClassLens = initClassLens;
-    this.lensCodeRewriter = new LensCodeRewriterUtils(appInfo, graphLens);
-    this.classes = sortClasses(appInfo, classes, namingLens);
+    this.lensCodeRewriter = new LensCodeRewriterUtils(appView);
+    this.classes = sortClasses(appView.appInfo(), classes, namingLens);
     this.protos = createSortedMap(protos, compare(namingLens), this::failOnOverflow);
     this.types = createSortedMap(types, compare(namingLens), this::failOnOverflow);
     this.methods = createSortedMap(methods, compare(namingLens), this::failOnOverflow);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
index 4b44ada..ae5e3f9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
@@ -33,20 +33,28 @@
 
 public class LensCodeRewriterUtils {
 
+  private final AppView<?> appView;
   private final DexDefinitionSupplier definitions;
   private final GraphLens graphLens;
 
   private final Map<DexProto, DexProto> protoFixupCache = new ConcurrentHashMap<>();
 
   public LensCodeRewriterUtils(AppView<?> appView) {
-    this(appView, appView.graphLens());
+    this.appView = appView;
+    this.definitions = appView;
+    this.graphLens = null;
   }
 
   public LensCodeRewriterUtils(DexDefinitionSupplier definitions, GraphLens graphLens) {
+    this.appView = null;
     this.definitions = definitions;
     this.graphLens = graphLens;
   }
 
+  private GraphLens graphLens() {
+    return appView != null ? appView.graphLens() : graphLens;
+  }
+
   public DexCallSite rewriteCallSite(DexCallSite callSite, ProgramMethod context) {
     DexItemFactory dexItemFactory = definitions.dexItemFactory();
     DexProto newMethodProto = rewriteProto(callSite.methodProto);
@@ -74,7 +82,7 @@
       DexMethod invokedMethod = methodHandle.asMethod();
       MethodHandleType oldType = methodHandle.type;
       MethodLookupResult lensLookup =
-          graphLens.lookupMethod(invokedMethod, context.getReference(), oldType.toInvokeType());
+          graphLens().lookupMethod(invokedMethod, context.getReference(), oldType.toInvokeType());
       DexMethod rewrittenTarget = lensLookup.getReference();
       DexMethod actualTarget;
       MethodHandleType newType;
@@ -115,7 +123,7 @@
       }
     } else {
       DexField field = methodHandle.asField();
-      DexField actualField = graphLens.lookupField(field);
+      DexField actualField = graphLens().lookupField(field);
       if (actualField != field) {
         return new DexMethodHandle(methodHandle.type, actualField, methodHandle.isInterface);
       }
@@ -158,7 +166,7 @@
         return rewriteDexMethodType(value.asDexValueMethodType());
       case TYPE:
         DexType oldType = value.asDexValueType().value;
-        DexType newType = graphLens.lookupType(oldType);
+        DexType newType = graphLens().lookupType(oldType);
         return newType != oldType ? new DexValueType(newType) : value;
       default:
         return value;
@@ -168,7 +176,7 @@
   public DexProto rewriteProto(DexProto proto) {
     return definitions
         .dexItemFactory()
-        .applyClassMappingToProto(proto, graphLens::lookupType, protoFixupCache);
+        .applyClassMappingToProto(proto, graphLens()::lookupType, protoFixupCache);
   }
 
   private DexValueMethodHandle rewriteDexValueMethodHandle(
diff --git a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
index b48e0a93..0fe19bc 100644
--- a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
+++ b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.dex;
 
 import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexDebugEvent;
 import com.android.tools.r8.graph.DexDebugInfo;
@@ -23,9 +24,11 @@
 
   private ObjectToOffsetMapping emptyObjectTObjectMapping() {
     return new ObjectToOffsetMapping(
-        AppInfo.createInitialAppInfo(
-            DexApplication.builder(new InternalOptions(new DexItemFactory(), new Reporter()), null)
-                .build()),
+        AppView.createForD8(
+            AppInfo.createInitialAppInfo(
+                DexApplication.builder(
+                        new InternalOptions(new DexItemFactory(), new Reporter()), null)
+                    .build())),
         GraphLens.getIdentityLens(),
         NamingLens.getIdentityLens(),
         InitClassLens.getDefault(),
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
index ab837d3..c494152 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.Diagnostic;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringUtils;
@@ -86,10 +87,12 @@
   }
 
   static List<Object[]> enumUnboxingTestParameters() {
-    return buildParameters(
-        getTestParameters().withDexRuntimes().withAllApiLevels().build(),
-        BooleanUtils.values(),
-        getAllEnumKeepRules());
+    return enumUnboxingTestParameters(
+        getTestParameters().withDexRuntimes().withAllApiLevels().build());
+  }
+
+  static List<Object[]> enumUnboxingTestParameters(TestParametersCollection testParameters) {
+    return buildParameters(testParameters, BooleanUtils.values(), getAllEnumKeepRules());
   }
 
   protected static EnumKeepRules[] getAllEnumKeepRules() {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/LambdaEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/LambdaEnumUnboxingTest.java
new file mode 100644
index 0000000..d6eb6d1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/LambdaEnumUnboxingTest.java
@@ -0,0 +1,97 @@
+// 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.enumunboxing;
+
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class LambdaEnumUnboxingTest extends EnumUnboxingTestBase {
+
+  private final TestParameters parameters;
+  private final boolean enumValueOptimization;
+  private final EnumKeepRules enumKeepRules;
+
+  @Parameters(name = "{0} valueOpt: {1} keep: {2}")
+  public static List<Object[]> data() {
+    return enumUnboxingTestParameters(getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  public LambdaEnumUnboxingTest(
+      TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
+    this.parameters = parameters;
+    this.enumValueOptimization = enumValueOptimization;
+    this.enumKeepRules = enumKeepRules;
+  }
+
+  @Test
+  public void testEnumUnboxing() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addKeepRules(enumKeepRules.getKeepRules())
+        .addOptionsModification(
+            options -> {
+              if (options.isGeneratingClassFiles()) {
+                // TODO(b/172568606): Remove this when enabled for CF by default.
+                assertFalse(options.enableEnumUnboxing);
+                options.enableEnumUnboxing = true;
+              }
+            })
+        .enableNeverClassInliningAnnotations()
+        .enableInliningAnnotations()
+        .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+        .addOptionsModification(options -> options.testing.enableEnumUnboxingDebugLogs = false)
+        .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("0", "0", "1", "0", "0");
+  }
+
+  @NeverClassInline
+  enum MyEnum {
+    A,
+    B,
+    C
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.println(MyEnum.A.ordinal());
+      boolean[] booleans = new boolean[] {true, false};
+      forEach(booleans, Main::printAndGetEnum);
+      System.out.println(printAndGetEnum(true).ordinal());
+    }
+
+    @NeverInline
+    private static MyEnum printAndGetEnum(boolean b) {
+      MyEnum myEnum = b ? MyEnum.A : MyEnum.B;
+      System.out.println(myEnum.ordinal());
+      return myEnum;
+    }
+
+    @NeverInline
+    static void forEach(boolean[] booleans, MyBooleanConsumer consumer) {
+      for (boolean b : booleans) {
+        consumer.accept(b);
+      }
+    }
+  }
+
+  interface MyBooleanConsumer {
+
+    void accept(Boolean b);
+  }
+}