Version 2.0.43

Cherry pick: Don't emit line numbers when keepparameternames is set
CL: https://r8-review.googlesource.com/c/r8/+/49268

Bug: 150400371
Change-Id: Ia60a3c11bb4cb07cb4604b35fc3d3da705130951
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 3a0340e..993d66a 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.0.42";
+  public static final String LABEL = "2.0.43";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index d32abdf..a683332 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -290,9 +290,13 @@
       debugInfo = debugInfoIterator.hasNext() ? debugInfoIterator.next() : null;
     }
     int instructionNumber = 0;
+    Map<Integer, DebugLocalInfo> locals = Collections.emptyMap();
     for (Instruction insn : instructions) {
       while (debugInfo != null && debugInfo.address == insn.getOffset()) {
-        builder.append("         ").append(debugInfo.toString(false)).append("\n");
+        if (debugInfo.lineEntry || !locals.equals(debugInfo.locals)) {
+          builder.append("         ").append(debugInfo.toString(false)).append("\n");
+        }
+        locals = debugInfo.locals;
         debugInfo = debugInfoIterator.hasNext() ? debugInfoIterator.next() : null;
       }
       StringUtils.appendLeftPadded(builder, Integer.toString(instructionNumber++), 5);
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index d012556..09b40ee 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -147,11 +147,25 @@
     return code;
   }
 
+  public static class DebugParsingOptions {
+    public final boolean lineInfo;
+    public final boolean localInfo;
+    public final int asmReaderOptions;
+
+    public DebugParsingOptions(boolean lineInfo, boolean localInfo, int asmReaderOptions) {
+      this.lineInfo = lineInfo;
+      this.localInfo = localInfo;
+      this.asmReaderOptions = asmReaderOptions;
+    }
+  }
+
   public void parseCode(ReparseContext context, boolean useJsrInliner) {
-    int parsingOptions = getParsingOptions(application, reachabilitySensitive);
+    DebugParsingOptions parsingOptions = getParsingOptions(application, reachabilitySensitive);
+
     ClassCodeVisitor classVisitor =
-        new ClassCodeVisitor(context.owner, createCodeLocator(context), application, useJsrInliner);
-    new ClassReader(context.classCache).accept(classVisitor, parsingOptions);
+        new ClassCodeVisitor(
+            context.owner, createCodeLocator(context), application, useJsrInliner, parsingOptions);
+    new ClassReader(context.classCache).accept(classVisitor, parsingOptions.asmReaderOptions);
   }
 
   private void setCode(CfCode code) {
@@ -251,17 +265,20 @@
     private final BiFunction<String, String, LazyCfCode> codeLocator;
     private final JarApplicationReader application;
     private boolean usrJsrInliner;
+    private final DebugParsingOptions debugParsingOptions;
 
     ClassCodeVisitor(
         DexClass clazz,
         BiFunction<String, String, LazyCfCode> codeLocator,
         JarApplicationReader application,
-        boolean useJsrInliner) {
+        boolean useJsrInliner,
+        DebugParsingOptions debugParsingOptions) {
       super(InternalOptions.ASM_VERSION);
       this.clazz = clazz;
       this.codeLocator = codeLocator;
       this.application = application;
       this.usrJsrInliner = useJsrInliner;
+      this.debugParsingOptions = debugParsingOptions;
     }
 
     @Override
@@ -272,7 +289,8 @@
         LazyCfCode code = codeLocator.apply(name, desc);
         if (code != null) {
           DexMethod method = application.getMethod(clazz.type, name, desc);
-          MethodCodeVisitor methodVisitor = new MethodCodeVisitor(application, method, code);
+          MethodCodeVisitor methodVisitor =
+              new MethodCodeVisitor(application, method, code, debugParsingOptions);
           if (!usrJsrInliner) {
             return methodVisitor;
           }
@@ -286,6 +304,7 @@
   private static class MethodCodeVisitor extends MethodVisitor {
     private final JarApplicationReader application;
     private final DexItemFactory factory;
+    private final DebugParsingOptions debugParsingOptions;
     private int maxStack;
     private int maxLocals;
     private List<CfInstruction> instructions;
@@ -296,8 +315,13 @@
     private final LazyCfCode code;
     private final DexMethod method;
 
-    MethodCodeVisitor(JarApplicationReader application, DexMethod method, LazyCfCode code) {
+    MethodCodeVisitor(
+        JarApplicationReader application,
+        DexMethod method,
+        LazyCfCode code,
+        DebugParsingOptions debugParsingOptions) {
       super(InternalOptions.ASM_VERSION);
+      this.debugParsingOptions = debugParsingOptions;
       assert code != null;
       this.application = application;
       this.factory = application.getFactory();
@@ -895,14 +919,16 @@
     @Override
     public void visitLocalVariable(
         String name, String desc, String signature, Label start, Label end, int index) {
-      DebugLocalInfo debugLocalInfo =
-          canonicalize(
-              new DebugLocalInfo(
-                  factory.createString(name),
-                  factory.createType(desc),
-                  signature == null ? null : factory.createString(signature)));
-      localVariables.add(
-          new LocalVariableInfo(index, debugLocalInfo, getLabel(start), getLabel(end)));
+      if (debugParsingOptions.localInfo) {
+        DebugLocalInfo debugLocalInfo =
+            canonicalize(
+                new DebugLocalInfo(
+                    factory.createString(name),
+                    factory.createType(desc),
+                    signature == null ? null : factory.createString(signature)));
+        localVariables.add(
+            new LocalVariableInfo(index, debugLocalInfo, getLabel(start), getLabel(end)));
+      }
     }
 
     private DebugLocalInfo canonicalize(DebugLocalInfo debugLocalInfo) {
@@ -911,7 +937,9 @@
 
     @Override
     public void visitLineNumber(int line, Label start) {
-      instructions.add(new CfPosition(getLabel(start), new Position(line, null, method, null)));
+      if (debugParsingOptions.lineInfo) {
+        instructions.add(new CfPosition(getLabel(start), new Position(line, null, method, null)));
+      }
     }
 
     @Override
@@ -923,7 +951,7 @@
     }
   }
 
-  private static int getParsingOptions(
+  private static DebugParsingOptions getParsingOptions(
       JarApplicationReader application, boolean reachabilitySensitive) {
     int parsingOptions =
         application.options.testing.readInputStackMaps
@@ -931,18 +959,24 @@
             : ClassReader.SKIP_FRAMES;
 
     ProguardConfiguration configuration = application.options.getProguardConfiguration();
-    if (configuration != null && !configuration.isKeepParameterNames()) {
-      ProguardKeepAttributes keep =
-          application.options.getProguardConfiguration().getKeepAttributes();
-      if (!application.options.getProguardConfiguration().isKeepParameterNames()
-          && !keep.localVariableTable
-          && !keep.localVariableTypeTable
-          && !keep.lineNumberTable
-          && !reachabilitySensitive) {
-        parsingOptions |= ClassReader.SKIP_DEBUG;
-      }
+    if (configuration == null) {
+      return new DebugParsingOptions(true, true, parsingOptions);
     }
-    return parsingOptions;
+    ProguardKeepAttributes keep =
+        application.options.getProguardConfiguration().getKeepAttributes();
+
+    boolean localsInfo =
+        configuration.isKeepParameterNames()
+            || keep.localVariableTable
+            || keep.localVariableTypeTable
+            || reachabilitySensitive;
+    boolean lineInfo = keep.lineNumberTable;
+
+    if (!localsInfo && !lineInfo) {
+      parsingOptions |= ClassReader.SKIP_DEBUG;
+    }
+
+    return new DebugParsingOptions(lineInfo, localsInfo, parsingOptions);
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java b/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java
new file mode 100644
index 0000000..68c1d36
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b150400371/DebuginfoForInlineFrameRegressionTest.java
@@ -0,0 +1,77 @@
+// 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.regress.b150400371;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import it.unimi.dsi.fastutil.ints.IntArraySet;
+import it.unimi.dsi.fastutil.ints.IntSet;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DebuginfoForInlineFrameRegressionTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntime(Version.DEFAULT).withAllApiLevels().build();
+  }
+
+  public DebuginfoForInlineFrameRegressionTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testNoLinesForNonInline() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(DebuginfoForInlineFrameRegressionTest.class)
+        .addKeepMainRule(InlineInto.class)
+        .addKeepRules("-keepparameternames")
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), InlineInto.class)
+        .assertSuccessWithOutputLines("42foo")
+        .inspect(
+            (inspector) -> {
+              MethodSubject main =
+                  inspector.method(InlineInto.class.getDeclaredMethod("main", String[].class));
+              IntSet lines = new IntArraySet(main.getLineNumberTable().getLines());
+              assertEquals(2, lines.size());
+            });
+  }
+
+  public static class InlineInto {
+    public static void main(String[] args) {
+      boolean late = System.currentTimeMillis() > 2;
+      String a = late ? "42" : "43";
+      String b = late ? "foo" : "bar";
+      String foo = InlineFrom.foo();
+      String result;
+      if (System.currentTimeMillis() < 2) {
+        result = a + b + foo + " that will never happen";
+      } else {
+        result = a + b;
+      }
+      System.out.println(late ? result : "never");
+      if (!late) {
+        System.out.println(late ? result : "never");
+        System.out.println(late ? result : "never");
+      }
+    }
+  }
+
+  public static class InlineFrom {
+    public static String foo() {
+      return "bar" + System.currentTimeMillis();
+    }
+  }
+}