Version 2.2.28

Cherry pick: Fix inability to map static interface method during outlining
CL: https://r8-review.googlesource.com/c/r8/+/54901

Bug: 169584856
Change-Id: I7863073e54e0a3f8bdd8cef284f2c0399e7cd931
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 1031247..781a82b 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.2.27";
+  public static final String LABEL = "2.2.28";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index b276437..5eb823f 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -928,10 +928,7 @@
 
     @Override
     public DexMethod getOriginalMethodSignature(DexMethod method) {
-      DexMethod originalMethod =
-          originalMethodSignatures != null
-              ? originalMethodSignatures.getOrDefault(method, method)
-              : method;
+      DexMethod originalMethod = internalGetPreviousMethodSignature(method);
       return getPrevious().getOriginalMethodSignature(originalMethod);
     }
 
@@ -949,9 +946,7 @@
         return originalMethod;
       }
       DexMethod renamedMethod = getPrevious().getRenamedMethodSignature(originalMethod, applied);
-      return originalMethodSignatures != null
-          ? originalMethodSignatures.inverse().getOrDefault(renamedMethod, renamedMethod)
-          : renamedMethod;
+      return internalGetNextMethodSignature(renamedMethod);
     }
 
     @Override
@@ -1042,6 +1037,12 @@
           : method;
     }
 
+    protected DexMethod internalGetNextMethodSignature(DexMethod method) {
+      return originalMethodSignatures != null
+          ? originalMethodSignatures.inverse().getOrDefault(method, method)
+          : method;
+    }
+
     @Override
     public DexMethod lookupGetFieldForMethod(DexField field, DexMethod context) {
       return getPrevious().lookupGetFieldForMethod(field, context);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 20c3ae4..80ad71a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -106,7 +106,7 @@
         implMethod.copyMetadata(virtual);
         virtual.setDefaultInterfaceMethodImplementation(implMethod);
         companionMethods.add(implMethod);
-        graphLensBuilder.recordOrigin(implMethod.method, virtual.method);
+        graphLensBuilder.recordCodeMovedToCompanionClass(virtual.method, implMethod.method);
       }
 
       // Remove bridge methods.
@@ -448,15 +448,17 @@
     }
 
     @Override
-    public DexMethod getOriginalMethodSignature(DexMethod method) {
-      DexMethod originalMethod = extraOriginalMethodSignatures.get(method);
-      if (originalMethod == null) {
-        originalMethod =
-            originalMethodSignatures != null
-                ? originalMethodSignatures.getOrDefault(method, method)
-                : method;
-      }
-      return getPrevious().getOriginalMethodSignature(originalMethod);
+    protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+      return extraOriginalMethodSignatures.getOrDefault(
+          method, originalMethodSignatures.getOrDefault(method, method));
+    }
+
+    @Override
+    protected DexMethod internalGetNextMethodSignature(DexMethod method) {
+      return originalMethodSignatures
+          .inverse()
+          .getOrDefault(
+              method, extraOriginalMethodSignatures.inverse().getOrDefault(method, method));
     }
 
     @Override
@@ -472,11 +474,10 @@
 
       private final BiMap<DexMethod, DexMethod> extraOriginalMethodSignatures = HashBiMap.create();
 
-      public void recordOrigin(DexMethod method, DexMethod origin) {
-        if (method == origin) {
-          return;
-        }
-        extraOriginalMethodSignatures.put(method, origin);
+      public void recordCodeMovedToCompanionClass(DexMethod from, DexMethod to) {
+        assert from != to;
+        originalMethodSignatures.put(from, from);
+        extraOriginalMethodSignatures.put(to, from);
       }
 
       @Override
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromStaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromStaticInterfaceMethodTest.java
new file mode 100644
index 0000000..ec4c859
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromStaticInterfaceMethodTest.java
@@ -0,0 +1,114 @@
+// Copyright (c) 2019, 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.ir.optimize.outliner;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class OutlineFromStaticInterfaceMethodTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public OutlineFromStaticInterfaceMethodTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .addOptionsModification(
+            options -> {
+              if (parameters.isCfRuntime()) {
+                assert !options.outline.enabled;
+                options.outline.enabled = true;
+              }
+              options.outline.threshold = 2;
+              options.outline.minSize = 2;
+            })
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!", "Hello world!");
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject interfaceSubject =
+        parameters.isCfRuntime()
+                || parameters
+                    .getApiLevel()
+                    .isGreaterThanOrEqualTo(apiLevelWithStaticInterfaceMethodsSupport())
+            ? inspector.clazz(I.class)
+            : inspector.clazz(
+                I.class.getTypeName() + InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX);
+    assertThat(interfaceSubject, isPresent());
+
+    MethodSubject greetMethodSubject = interfaceSubject.uniqueMethodWithName("greet");
+    assertThat(greetMethodSubject, isPresent());
+    assertEquals(
+        1,
+        greetMethodSubject.streamInstructions().filter(InstructionSubject::isInvokeStatic).count());
+  }
+
+  static class TestClass {
+
+    public static void main(String... args) {
+      greet();
+      I.greet();
+    }
+
+    @NeverInline
+    static void greet() {
+      Greeter.hello();
+      Greeter.world();
+    }
+  }
+
+  interface I {
+
+    @NeverInline
+    static void greet() {
+      Greeter.hello();
+      Greeter.world();
+    }
+  }
+
+  @NeverClassInline
+  public static class Greeter {
+
+    @NeverInline
+    public static void hello() {
+      System.out.print("Hello");
+    }
+
+    @NeverInline
+    public static void world() {
+      System.out.println(" world!");
+    }
+  }
+}