Desugar: make companion class an inner class

IntelliJ's debugger is not aware of the companion class synthesized
by D8 when desugaring. By making it an inner class of the interface
being desugared, we match the behavior of anonymous classes.

Bug: 77560662
Change-Id: Ia5d43794b031e7ae9c9b865f5e76d3e3934eca8b
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 9b8b8e2..8add960 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -65,8 +65,8 @@
 public final class InterfaceMethodRewriter {
 
   // Public for testing.
-  public static final String COMPANION_CLASS_NAME_SUFFIX = "-CC";
-  private static final String DEFAULT_METHOD_PREFIX = "$default$";
+  public static final String COMPANION_CLASS_NAME_SUFFIX = "$-CC";
+  public static final String DEFAULT_METHOD_PREFIX = "$default$";
   private static final String PRIVATE_METHOD_PREFIX = "$private$";
 
   private final IRConverter converter;
diff --git a/src/test/debugTestResourcesJava8/DebugInterfaceMethod.java b/src/test/debugTestResourcesJava8/DebugInterfaceMethod.java
index b407698..f951357 100644
--- a/src/test/debugTestResourcesJava8/DebugInterfaceMethod.java
+++ b/src/test/debugTestResourcesJava8/DebugInterfaceMethod.java
@@ -4,21 +4,10 @@
 
 public class DebugInterfaceMethod {
 
-  interface I {
-    default void doSomething(String msg) {
-      String name = getClass().getName();
-      System.out.println(name + ": " + msg);
-    }
-
-    static void printString(String msg) {
-      System.out.println(msg);
-    }
+  static class DefaultImpl implements InterfaceWithDefaultAndStaticMethods {
   }
 
-  static class DefaultImpl implements I {
-  }
-
-  static class OverrideImpl implements I {
+  static class OverrideImpl implements InterfaceWithDefaultAndStaticMethods {
 
     @Override
     public void doSomething(String msg) {
@@ -27,12 +16,12 @@
     }
   }
 
-  private static void testDefaultMethod(I i) {
+  private static void testDefaultMethod(InterfaceWithDefaultAndStaticMethods i) {
     i.doSomething("Test");
   }
 
   private static void testStaticMethod() {
-    I.printString("I'm a static method in interface");
+    InterfaceWithDefaultAndStaticMethods.printString("I'm a static method in interface");
   }
 
   public static void main(String[] args) {
diff --git a/src/test/debugTestResourcesJava8/InterfaceWithDefaultAndStaticMethods.java b/src/test/debugTestResourcesJava8/InterfaceWithDefaultAndStaticMethods.java
new file mode 100644
index 0000000..05f48ab
--- /dev/null
+++ b/src/test/debugTestResourcesJava8/InterfaceWithDefaultAndStaticMethods.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2018, 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.
+
+interface InterfaceWithDefaultAndStaticMethods {
+  default void doSomething(String msg) {
+    String name = getClass().getName();
+    System.out.println(name + ": " + msg);
+  }
+
+  static void printString(String msg) {
+    System.out.println(msg);
+  }
+}
diff --git a/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
index 8a1119a..587d947 100644
--- a/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
+++ b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
@@ -290,7 +290,7 @@
     private static Class getCompanionClassOrInterface() {
       try {
         return Class.forName("lambdadesugaringnplus."
-            + "LambdasWithStaticAndDefaultMethods$B38302860$AnnotatedInterface-CC");
+            + "LambdasWithStaticAndDefaultMethods$B38302860$AnnotatedInterface$-CC");
       } catch (Exception e) {
         return AnnotatedInterface.class;
       }
diff --git a/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java b/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
index 6ecdca7..9bd3c18 100644
--- a/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
@@ -4,7 +4,10 @@
 
 package com.android.tools.r8.debug;
 
+import static org.junit.Assert.assertEquals;
+
 import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
+import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
 import java.util.ArrayList;
@@ -19,7 +22,8 @@
 public class InterfaceMethodTest extends DebugTestBase {
 
   private static final Path JAR = DebugTestBase.DEBUGGEE_JAVA8_JAR;
-  private static final String SOURCE_FILE = "DebugInterfaceMethod.java";
+  private static final String TEST_SOURCE_FILE = "DebugInterfaceMethod.java";
+  private static final String INTERFACE_SOURCE_FILE = "InterfaceWithDefaultAndStaticMethods.java";
 
   @Parameters(name = "{0}")
   public static Collection configs() {
@@ -43,23 +47,42 @@
     String parameterName = "msg";
     String localVariableName = "name";
 
+    final String defaultMethodContainerClass;
+    final String defaultMethodName;
+    final String defaultMethodThisName;
+    if (supportsDefaultMethod(config)) {
+      defaultMethodContainerClass = "InterfaceWithDefaultAndStaticMethods";
+      defaultMethodName = "doSomething";
+      defaultMethodThisName = "this";
+    } else {
+      defaultMethodContainerClass = "InterfaceWithDefaultAndStaticMethods"
+          + InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
+      // IntelliJ's debugger does not know about the companion class. The only way to match it with
+      // the source file or the desguared interface is to make it an inner class.
+      assertEquals('$', InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX.charAt(0));
+      defaultMethodName = InterfaceMethodRewriter.DEFAULT_METHOD_PREFIX + "doSomething";
+      defaultMethodThisName = "-this";
+    }
+
+
     List<Command> commands = new ArrayList<>();
     commands.add(breakpoint(debuggeeClass, "testDefaultMethod"));
     commands.add(run());
     commands.add(checkMethod(debuggeeClass, "testDefaultMethod"));
-    commands.add(checkLine(SOURCE_FILE, 31));
+    commands.add(checkLine(TEST_SOURCE_FILE, 20));
+
+    // Step into the default method.
     commands.add(stepInto(INTELLIJ_FILTER));
-    commands.add(checkLine(SOURCE_FILE, 9));
-    if (supportsDefaultMethod(config)) {
-      commands.add(checkLocal("this"));
-    } else {
-      commands.add(checkLocal("-this"));
-    }
+    commands.add(checkLine(INTERFACE_SOURCE_FILE, 7));
+    commands.add(checkMethod(defaultMethodContainerClass, defaultMethodName));
+    commands.add(checkLocal(defaultMethodThisName));
     commands.add(checkLocal(parameterName));
     commands.add(stepOver(INTELLIJ_FILTER));
+    commands.add(checkLine(INTERFACE_SOURCE_FILE, 8));
+    commands.add(checkMethod(defaultMethodContainerClass, defaultMethodName));
+    commands.add(checkLocal(defaultMethodThisName));
     commands.add(checkLocal(parameterName));
     commands.add(checkLocal(localVariableName));
-    // TODO(shertz) check current method name ?
     commands.add(run());
     commands.add(run()  /* resume after 2nd breakpoint */);
 
@@ -77,7 +100,7 @@
     commands.add(run());
     commands.add(run() /* resume after 1st breakpoint */);
     commands.add(checkMethod(debuggeeClass, "testDefaultMethod"));
-    commands.add(checkLine(SOURCE_FILE, 31));
+    commands.add(checkLine(TEST_SOURCE_FILE, 20));
     commands.add(stepInto());
     commands.add(checkMethod("DebugInterfaceMethod$OverrideImpl", "doSomething"));
     commands.add(checkLocal("this"));
@@ -96,14 +119,30 @@
     String debuggeeClass = "DebugInterfaceMethod";
     String parameterName = "msg";
 
+    final String staticMethodContainerClass;
+    final String staticMethodName = "printString";
+    if (supportsDefaultMethod(config)) {
+      staticMethodContainerClass = "InterfaceWithDefaultAndStaticMethods";
+    } else {
+      staticMethodContainerClass = "InterfaceWithDefaultAndStaticMethods"
+          + InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
+    }
+
     List<Command> commands = new ArrayList<>();
     commands.add(breakpoint(debuggeeClass, "testStaticMethod"));
     commands.add(run());
     commands.add(checkMethod(debuggeeClass, "testStaticMethod"));
-    commands.add(checkLine(SOURCE_FILE, 35));
+    commands.add(checkLine(TEST_SOURCE_FILE, 24));
+
+    // Step into static method.
     commands.add(stepInto());
+    commands.add(checkLine(INTERFACE_SOURCE_FILE, 12));
+    commands.add(checkMethod(staticMethodContainerClass, staticMethodName));
+    commands.add(checkNoLocal("this"));
+    commands.add(checkNoLocal("-this"));
     commands.add(checkLocal(parameterName));
     commands.add(stepOver());
+    commands.add(checkLine(INTERFACE_SOURCE_FILE, 13));
     commands.add(checkLocal(parameterName));
     commands.add(run());