Optimize line numbers, enabled only for testing.

Proguard-map output is not yet implemented.

Bug:
Change-Id: Id65ded0e0ac7d9b2c6b7cac6f4273f645e5c930c
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 85741e2..8c76c6c 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -106,25 +106,32 @@
 
   // PositionRemapper is a stateful function which takes a position (represented by a
   // DexDebugPositionState) and returns a remapped Position.
-  private static class PositionRemapper {
-    private int nextLineNumber;
+  private interface PositionRemapper {
+    Position createRemappedPosition(DexDebugPositionState positionState);
+  }
 
-    PositionRemapper(int nextLineNumber) {
-      this.nextLineNumber = nextLineNumber;
-    }
-
-    private Position createRemappedPosition(DexDebugPositionState positionState) {
-      // TODO(tamaskenez) Actual remapping is to be implemented here.
-      // For now this is only identity-mapping.
+  private static class IdentityPositionRemapper implements PositionRemapper {
+    public Position createRemappedPosition(DexDebugPositionState positionState) {
       return new Position(
           positionState.getCurrentLine(),
           positionState.getCurrentFile(),
           positionState.getCurrentMethod(),
           positionState.getCurrentCallerPosition());
     }
+  }
 
-    int getNextLineNumber() {
-      return nextLineNumber;
+  private static class OptimizingPositionRemapper implements PositionRemapper {
+    private int nextLineNumber = 1;
+
+    public Position createRemappedPosition(DexDebugPositionState positionState) {
+      Position newPosition =
+          new Position(
+              nextLineNumber,
+              positionState.getCurrentFile(),
+              positionState.getCurrentMethod(),
+              null);
+      ++nextLineNumber;
+      return newPosition;
     }
   }
 
@@ -210,7 +217,10 @@
                 return DexEncodedMethod.slowCompare(lhs, rhs);
               });
         }
-        int nextLineNumber = 1;
+
+        PositionRemapper positionRemapper =
+            identityMapping ? new IdentityPositionRemapper() : new OptimizingPositionRemapper();
+
         for (DexEncodedMethod method : methods) {
           // Do the actual processing for each method.
           DexCode dexCode = method.getCode().asDexCode();
@@ -222,7 +232,6 @@
           // [processedEvents]
           PositionEventEmitter positionEventEmitter =
               new PositionEventEmitter(application.dexItemFactory, method.method, processedEvents);
-          PositionRemapper positionRemapper = new PositionRemapper(nextLineNumber);
 
           EventFilter eventFilter =
               new EventFilter(
@@ -236,7 +245,7 @@
           for (DexDebugEvent event : debugInfo.events) {
             event.accept(eventFilter);
           }
-          nextLineNumber = positionRemapper.getNextLineNumber();
+
           DexDebugInfo optimizedDebugInfo =
               new DexDebugInfo(
                   positionEventEmitter.getStartLine(),
diff --git a/src/test/debugTestResources/Inlining2.java b/src/test/debugTestResources/Inlining2.java
index 38f854c..a570ba3 100644
--- a/src/test/debugTestResources/Inlining2.java
+++ b/src/test/debugTestResources/Inlining2.java
@@ -2,6 +2,31 @@
 // 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.
 
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+
 public class Inlining2 {
   public static void inlineThisFromAnotherFile() {
     System.out.println("inlineThisFromAnotherFile");
diff --git a/src/test/debugTestResources/LineNumberOptimization1.java b/src/test/debugTestResources/LineNumberOptimization1.java
index c25ecc6..a97fc6d 100644
--- a/src/test/debugTestResources/LineNumberOptimization1.java
+++ b/src/test/debugTestResources/LineNumberOptimization1.java
@@ -8,7 +8,17 @@
     LineNumberOptimization2.callThisFromAnotherFile();
   }
 
+  private static void callThisFromSameFile(int a) {
+    System.out.println("callThisFromSameFile second overload");
+  }
+
+  private static void callThisFromSameFile(int a, int b) {
+    System.out.println("callThisFromSameFile third overload");
+  }
+
   public static void main(String[] args) {
     callThisFromSameFile();
+    callThisFromSameFile(1);
+    callThisFromSameFile(1, 2);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java b/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java
index d8649c7..2d109e3 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java
@@ -4,7 +4,11 @@
 package com.android.tools.r8.debug;
 
 import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
+import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
@@ -12,33 +16,64 @@
 public class DebugInfoWhenInliningTest extends DebugTestBase {
 
   public static final String SOURCE_FILE = "Inlining1.java";
-  private static DebuggeePath debuggeePath;
+  private static DebuggeePath debuggeePathNotOptimized;
+  private static DebuggeePath debuggeePathOptimized;
+
+  private static DebuggeePath makeDex(LineNumberOptimization lineNumberOptimization)
+      throws Exception {
+    return DebuggeePath.makeDex(
+        compileToDexViaR8(
+            oc -> {
+              oc.lineNumberOptimization = lineNumberOptimization;
+            },
+            null,
+            DEBUGGEE_JAR,
+            Collections.<String>emptyList(),
+            true,
+            CompilationMode.RELEASE));
+  }
 
   @BeforeClass
   public static void initDebuggeePath() throws Exception {
-    debuggeePath =
-        DebuggeePath.makeDex(
-            compileToDexViaR8(
-                null,
-                null,
-                DEBUGGEE_JAR,
-                Collections.<String>emptyList(),
-                true,
-                CompilationMode.RELEASE));
+    debuggeePathNotOptimized = makeDex(LineNumberOptimization.OFF);
+    debuggeePathOptimized = makeDex(LineNumberOptimization.ON);
   }
 
   @Test
-  public void testEachLine() throws Throwable {
+  public void testEachLineNotOptimized() throws Throwable {
+    // The reason why the not-optimized test contains half as many line numbers as the optimized
+    // one:
+    //
+    // In the Java source (Inlining1) each call is duplicated. Since they end up on the same line
+    // (innermost callee) the line numbers are actually 7, 7, 32, 32, ... but even if the positions
+    // are emitted duplicated in the dex code, the debugger stops only when there's a change.
+    int[] lineNumbers = {7, 32, 11, 7};
+    testEachLine(debuggeePathNotOptimized, lineNumbers);
+  }
+
+  @Test
+  public void testEachLineOptimized() throws Throwable {
+    int[] lineNumbers = {1, 2, 3, 4, 5, 6, 7, 8};
+    testEachLine(debuggeePathOptimized, lineNumbers);
+  }
+
+  private void testEachLine(DebuggeePath debuggeePath, int[] lineNumbers) throws Throwable {
     final String className = "Inlining1";
-    final String methodName = "main";
-    final String signature = "([Ljava/lang/String;)V";
-    runDebugTest(
-        debuggeePath,
-        className,
-        breakpoint(className, methodName, signature),
-        run(),
-        checkMethod(className, methodName, signature),
-        // TODO(tamaskenez) to be continued as the feature is implemented in class Inliner
-        run());
+    final String mainSignature = "([Ljava/lang/String;)V";
+    List<Command> commands = new ArrayList<Command>();
+    commands.add(breakpoint(className, "main", mainSignature));
+    commands.add(run());
+    boolean first = true;
+    for (int i : lineNumbers) {
+      if (first) {
+        first = false;
+      } else {
+        commands.add(stepOver());
+      }
+      commands.add(checkMethod(className, "main", mainSignature));
+      commands.add(checkLine(SOURCE_FILE, i));
+    }
+    commands.add(run());
+    runDebugTest(debuggeePath, className, commands);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
index 33a4b64..74a46ab 100644
--- a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
@@ -17,12 +17,6 @@
   private static DebuggeePath debuggeePathNotOptimized;
   private static DebuggeePath debuggeePathIdentityTest;
 
-  private final String class1 = "LineNumberOptimization1";
-  private final String class2 = "LineNumberOptimization2";
-  private final String file1 = class1 + ".java";
-  private final String file2 = class2 + ".java";
-  private final String mainSignature = "([Ljava/lang/String;)V";
-
   private static DebuggeePath makeDex(LineNumberOptimization lineNumberOptimization)
       throws Exception {
     return DebuggeePath.makeDex(
@@ -47,43 +41,63 @@
 
   @Test
   public void testNotOptimized() throws Throwable {
-    runDebugTest(
-        debuggeePathNotOptimized,
-        class1,
-        breakpoint(class1, "main", mainSignature),
-        run(),
-        checkMethod(class1, "main", mainSignature),
-        checkLine(file1, 12),
-        stepInto(),
-        checkMethod(class1, "callThisFromSameFile", "()V"),
-        checkLine(file1, 7),
-        stepOver(),
-        checkMethod(class1, "callThisFromSameFile", "()V"),
-        checkLine(file1, 8),
-        stepInto(INTELLIJ_FILTER),
-        checkMethod(class2, "callThisFromAnotherFile", "()V"),
-        checkLine(file2, 28),
-        run());
+    int[] lineNumbers = {20, 7, 8, 28, 8, 20, 21, 12, 21, 22, 16, 22};
+    test(debuggeePathNotOptimized, lineNumbers);
   }
 
   @Test
   public void testOptimized() throws Throwable {
+    int[] lineNumbers = {1, 1, 2, 1, 2, 1, 2, 3, 2, 3, 4, 3};
+    test(debuggeePathOptimized, lineNumbers);
+  }
+
+  private void test(DebuggeePath debuggeePath, int[] lineNumbers) throws Throwable {
+    final String class1 = "LineNumberOptimization1";
+    final String class2 = "LineNumberOptimization2";
+    final String file1 = class1 + ".java";
+    final String file2 = class2 + ".java";
+    final String mainSignature = "([Ljava/lang/String;)V";
+
     runDebugTest(
-        debuggeePathOptimized,
+        debuggeePath,
         class1,
         breakpoint(class1, "main", mainSignature),
         run(),
         checkMethod(class1, "main", mainSignature),
-        checkLine(file1, 12),
+        checkLine(file1, lineNumbers[0]),
         stepInto(),
         checkMethod(class1, "callThisFromSameFile", "()V"),
-        checkLine(file1, 7),
+        checkLine(file1, lineNumbers[1]),
         stepOver(),
         checkMethod(class1, "callThisFromSameFile", "()V"),
-        checkLine(file1, 8),
+        checkLine(file1, lineNumbers[2]),
         stepInto(INTELLIJ_FILTER),
         checkMethod(class2, "callThisFromAnotherFile", "()V"),
-        checkLine(file2, 28),
+        checkLine(file2, lineNumbers[3]),
+        stepOver(),
+        checkMethod(class1, "callThisFromSameFile", "()V"),
+        checkLine(file1, lineNumbers[4]),
+        stepOver(),
+        checkMethod(class1, "main", mainSignature),
+        checkLine(file1, lineNumbers[5]),
+        stepOver(),
+        checkMethod(class1, "main", mainSignature),
+        checkLine(file1, lineNumbers[6]),
+        stepInto(),
+        checkMethod(class1, "callThisFromSameFile", "(I)V"),
+        checkLine(file1, lineNumbers[7]),
+        stepOver(),
+        checkMethod(class1, "main", mainSignature),
+        checkLine(file1, lineNumbers[8]),
+        stepOver(),
+        checkMethod(class1, "main", mainSignature),
+        checkLine(file1, lineNumbers[9]),
+        stepInto(),
+        checkMethod(class1, "callThisFromSameFile", "(II)V"),
+        checkLine(file1, lineNumbers[10]),
+        stepOver(),
+        checkMethod(class1, "main", mainSignature),
+        checkLine(file1, lineNumbers[11]),
         run());
   }
 }