Update asm to version 9.5

Change-Id: I7e9f4adc2bef84ebfb7deab303799cb9f7c3ec1a
diff --git a/build.gradle b/build.gradle
index fe2980a..85e3fc6 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,7 +29,7 @@
 
 ext {
     androidSupportVersion = '25.4.0'
-    asmVersion = '9.4'  // When updating update tools/asmifier.py, build.src and Toolhelper as well.
+    asmVersion = '9.5'  // When updating update tools/asmifier.py, build.src and Toolhelper as well.
     javassistVersion = '3.29.2-GA'
     espressoVersion = '3.0.0'
     fastutilVersion = '7.2.0'
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index 88e29ab..bfe46c2 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -9,7 +9,7 @@
 }
 
 ext {
-    asmVersion = '9.4'
+    asmVersion = '9.5'
 }
 
 dependencies {
diff --git a/src/main/java/com/android/tools/r8/cf/CfVersion.java b/src/main/java/com/android/tools/r8/cf/CfVersion.java
index 1fd5ce7..f69e532 100644
--- a/src/main/java/com/android/tools/r8/cf/CfVersion.java
+++ b/src/main/java/com/android/tools/r8/cf/CfVersion.java
@@ -44,6 +44,8 @@
   public static final CfVersion V19_PREVIEW = new CfVersion(Opcodes.V19 | Opcodes.V_PREVIEW);
   public static final CfVersion V20 = new CfVersion(Opcodes.V20);
   public static final CfVersion V20_PREVIEW = new CfVersion(Opcodes.V20 | Opcodes.V_PREVIEW);
+  public static final CfVersion V21 = new CfVersion(Opcodes.V21);
+  public static final CfVersion V21_PREVIEW = new CfVersion(Opcodes.V21 | Opcodes.V_PREVIEW);
 
   private final int version;
 
@@ -67,7 +69,8 @@
     CfVersion.V17,
     CfVersion.V18,
     CfVersion.V19,
-    CfVersion.V20
+    CfVersion.V20,
+    CfVersion.V21
   };
 
   // Private constructor in case we want to canonicalize versions.
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index dbed84f..4df769b 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -8,7 +8,6 @@
 
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.cf.CfVersion;
-import com.android.tools.r8.cf.code.CfFrame;
 import com.android.tools.r8.cf.code.CfFrameVerifier;
 import com.android.tools.r8.cf.code.CfFrameVerifier.StackMapStatus;
 import com.android.tools.r8.cf.code.CfFrameVerifierEventConsumer;
@@ -417,24 +416,6 @@
     return preamble;
   }
 
-  private static class PrunePreambleMethodVisitor extends MethodVisitor {
-
-    private boolean inPreamble = true;
-
-    public PrunePreambleMethodVisitor(MethodVisitor methodVisitor) {
-      super(InternalOptions.ASM_VERSION, methodVisitor);
-    }
-
-    @Override
-    public void visitLineNumber(int line, Label start) {
-      if (line == 0 && inPreamble) {
-        return;
-      }
-      inPreamble = false;
-      super.visitLineNumber(line, start);
-    }
-  }
-
   @Override
   public void writeCf(
       ProgramMethod method,
@@ -460,20 +441,20 @@
             || (appView.enableWholeProgramOptimizations()
                 && classFileVersion.isEqualTo(CfVersion.V1_6)
                 && !options.shouldKeepStackMapTable());
-    PrunePreambleMethodVisitor prunePreambleVisitor = new PrunePreambleMethodVisitor(visitor);
+    Position preamblePosition = getPreamblePosition();
+    boolean discardPreamble = preamblePosition != null && preamblePosition.isSyntheticPosition();
     for (CfInstruction instruction : instructions) {
-      if (discardFrames && instruction instanceof CfFrame) {
+      if (discardFrames && instruction.isFrame()) {
+        continue;
+      }
+      if (discardPreamble
+          && instruction.isPosition()
+          && instruction.asPosition().getPosition().equals(preamblePosition)) {
+        discardPreamble = false;
         continue;
       }
       instruction.write(
-          appView,
-          method,
-          dexItemFactory,
-          graphLens,
-          initClassLens,
-          namingLens,
-          rewriter,
-          prunePreambleVisitor);
+          appView, method, dexItemFactory, graphLens, initClassLens, namingLens, rewriter, visitor);
     }
     visitor.visitEnd();
     visitor.visitMaxs(maxStack, maxLocals);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index cf7a34f..588ce91 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -789,6 +789,9 @@
       while (it.hasNext()) {
         Instruction instruction = it.next();
         Position position = instruction.getPosition();
+        if (instruction.isArgument()) {
+          continue;
+        }
         if (instruction.isMoveException()) {
           assert current == null;
           current = position;
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index cd3c1c5..d256489 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -153,7 +153,7 @@
     }
   }
 
-  public static final CfVersion SUPPORTED_CF_VERSION = CfVersion.V20;
+  public static final CfVersion SUPPORTED_CF_VERSION = CfVersion.V21;
 
   public static final int SUPPORTED_DEX_VERSION =
       AndroidApiLevel.LATEST.getDexVersion().getIntValue();
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 8479714..7ac3869 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -138,8 +138,8 @@
 
   public static final String R8_TEST_BUCKET = "r8-test-results";
 
-  public static final String ASM_JAR = BUILD_DIR + "deps/asm-9.4.jar";
-  public static final String ASM_UTIL_JAR = BUILD_DIR + "deps/asm-util-9.4.jar";
+  public static final String ASM_JAR = BUILD_DIR + "deps/asm-9.5.jar";
+  public static final String ASM_UTIL_JAR = BUILD_DIR + "deps/asm-util-9.5.jar";
 
   public static final Path API_SAMPLE_JAR = Paths.get("tests", "r8_api_usage_sample.jar");
 
diff --git a/src/test/java/com/android/tools/r8/cf/CompanionClassPreamblePositionTest.java b/src/test/java/com/android/tools/r8/cf/CompanionClassPreamblePositionTest.java
index a281690..90fd31b 100644
--- a/src/test/java/com/android/tools/r8/cf/CompanionClassPreamblePositionTest.java
+++ b/src/test/java/com/android/tools/r8/cf/CompanionClassPreamblePositionTest.java
@@ -16,9 +16,11 @@
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import it.unimi.dsi.fastutil.ints.Int2IntMap;
 import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
 import java.nio.file.Path;
+import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -70,14 +72,21 @@
   }
 
   private void checkZeroLineIsPresent(CodeInspector inspector) throws Exception {
-    // ASM skips zero lines so use javap to check the presence of a zero line.
+    // Until version 9.5, ASM skips zero lines so use javap to check the presence of a zero line.
     ClassSubject itf = inspector.clazz(I.class);
     assertThat(itf.javap(true), containsString("line 0: 0"));
+    // Having updated to ASM 9.5 we can also check using code inspector.
+    Assert.assertTrue(itf.uniqueMethod().getLineNumberTable().getLines().contains(0));
   }
 
   private void checkZeroLineNotPresent(CodeInspector inspector) throws Exception {
+    // Until version 9.5, ASM skips zero lines so use javap to check the absence of a zero line.
     ClassSubject companion = inspector.companionClassFor(I.class);
     assertThat(companion.javap(true), not(containsString("line 0: 0")));
+    // Having updated to ASM 9.5 we can also check using code inspector.
+    MethodSubject method = companion.uniqueMethod();
+    Assert.assertTrue(method.hasLineNumberTable());
+    Assert.assertFalse(method.getLineNumberTable().getLines().contains(0));
   }
 
   private byte[] getTransformedI(boolean includeZero) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/cf/LeadingZeroPositionTest.java b/src/test/java/com/android/tools/r8/cf/LeadingZeroPositionTest.java
new file mode 100644
index 0000000..28dc97e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/LeadingZeroPositionTest.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2023, 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.cf;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class LeadingZeroPositionTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello, world");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withDefaultRuntimes()
+        .withApiLevel(AndroidApiLevel.B)
+        .enableApiLevelsForCf()
+        .build();
+  }
+
+  public LeadingZeroPositionTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testZeroInInput() throws Exception {
+    Assume.assumeTrue(parameters.isCfRuntime());
+    testForJvm(parameters)
+        .addProgramClassFileData(getTransformedClass())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED)
+        .inspect(this::checkZeroLineIsPresent);
+  }
+
+  @Test
+  public void testZeroAfterD8() throws Exception {
+    testForD8(parameters.getBackend())
+        .addProgramClassFileData(getTransformedClass())
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), TestClass.class)
+        .inspect(this::checkZeroLineIsPresent);
+  }
+
+  private void checkZeroLineIsPresent(CodeInspector inspector) throws Exception {
+    MethodSubject method = inspector.clazz(TestClass.class).mainMethod();
+    assertTrue(method.getLineNumberTable().getLines().contains(0));
+  }
+
+  private byte[] getTransformedClass() throws Exception {
+    return transformer(TestClass.class)
+        .setPredictiveLineNumbering(MethodPredicate.onName("main"), 0)
+        .transform();
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      System.nanoTime(); // line 0 - input line, so we expect to preserve it to the output.
+      System.nanoTime(); // line 100
+      System.nanoTime(); // line 200
+      System.nanoTime(); // line 300
+      System.out.println("Hello, world"); // line 400
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/MultipleZeroPositionsTest.java b/src/test/java/com/android/tools/r8/cf/MultipleZeroPositionsTest.java
new file mode 100644
index 0000000..d32a594
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/MultipleZeroPositionsTest.java
@@ -0,0 +1,122 @@
+// Copyright (c) 2023, 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.cf;
+
+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.transformers.ClassFileTransformer.LineTranslation;
+import com.android.tools.r8.transformers.MethodTransformer.MethodContext;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import it.unimi.dsi.fastutil.ints.Int2IntMap;
+import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
+import java.nio.file.Path;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MultipleZeroPositionsTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello, world");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withDefaultCfRuntime()
+        .withApiLevel(AndroidApiLevel.B)
+        .enableApiLevelsForCf()
+        .build();
+  }
+
+  public MultipleZeroPositionsTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testZeroInInput() throws Exception {
+    testForJvm(parameters)
+        .addProgramClassFileData(getTransformedClass())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED)
+        .inspect(this::checkZeroLineCount);
+  }
+
+  @Test
+  public void testZeroAfterD8() throws Exception {
+    Path out =
+        testForD8(parameters.getBackend())
+            .addProgramClassFileData(getTransformedClass())
+            .setMinApi(parameters)
+            .compile()
+            .writeToZip();
+    testForJvm(parameters)
+        .addProgramFiles(out)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED)
+        .inspect(this::checkZeroLineCount);
+  }
+
+  private void checkZeroLineCount(CodeInspector inspector) throws Exception {
+    int expected = 3;
+    // Until version 9.5, ASM skips zero lines so use javap to check the presence of a zero line.
+    ClassSubject clazz = inspector.clazz(TestClass.class);
+    String javap = clazz.javap(true);
+    int actual = countOccurrences(javap, "line 0: ");
+    assertEquals(
+        "Expected " + expected + " 'line 0' entries, got " + actual + "\n" + javap,
+        expected,
+        actual);
+  }
+
+  private int countOccurrences(String string, String substring) {
+    int i = 0;
+    int found = 0;
+    while (true) {
+      i = string.indexOf(substring, i);
+      if (i == -1) {
+        return found;
+      }
+      ++found;
+      ++i;
+    }
+  }
+
+  private byte[] getTransformedClass() throws Exception {
+    return transformer(TestClass.class)
+        .setPredictiveLineNumbering(
+            new LineTranslation() {
+              Int2IntMap map = new Int2IntOpenHashMap();
+
+              @Override
+              public int translate(MethodContext context, int inputLine) {
+                if (context.getReference().getMethodName().equals("main")) {
+                  return map.computeIfAbsent(
+                      inputLine, ignore -> map.size() % 2 == 0 ? 1 + map.size() : 0);
+                }
+                return inputLine;
+              }
+            })
+        .transform();
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      System.nanoTime(); // line
+      System.nanoTime(); // 0
+      System.nanoTime(); // line
+      System.nanoTime(); // 0
+      System.out.println("Hello, world");
+      ; // line
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/AsmZeroLineEntryRegressionTest.java b/src/test/java/com/android/tools/r8/debuginfo/AsmZeroLineEntryRegressionTest.java
index acbe0e5..0436b0d 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/AsmZeroLineEntryRegressionTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/AsmZeroLineEntryRegressionTest.java
@@ -62,8 +62,8 @@
                 getClassWithZeroLineEntry(), Reference.classFromClass(TestClass.class)))
         .run(parameters.getRuntime(), TestClass.class)
         .assertFailureWithErrorThatThrows(RuntimeException.class)
-        // If this becomes zero ASM has been updated to not assign line zero a special meaning.
-        .inspectOriginalStackTrace(st -> checkLineNumber(st, 1));
+        // From version 9.5 ASM was updated to not assign line zero a special meaning.
+        .inspectOriginalStackTrace(st -> checkLineNumber(st, 0));
   }
 
   private void checkLineNumber(StackTrace st, int lineNumber) {
diff --git a/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java b/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java
index 3831e44..1e47370 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.graph.DexDebugInfo;
 import com.android.tools.r8.naming.retrace.StackTrace;
 import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
 import com.android.tools.r8.utils.BooleanUtils;
 import java.io.IOException;
 import java.util.Collections;
@@ -48,7 +49,7 @@
   private byte[] getTestClassTransformed() throws IOException {
     return transformer(TestClass.class)
         .setSourceFile(INPUT_SOURCE_FILE)
-        .setPredictiveLineNumbering()
+        .setPredictiveLineNumbering(MethodPredicate.all(), -1 /* start with no line */)
         .transform();
   }
 
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 63573d2..e010aa2 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -1282,7 +1282,7 @@
   }
 
   public ClassFileTransformer setPredictiveLineNumbering(MethodPredicate predicate) {
-    return setPredictiveLineNumbering(predicate, 0);
+    return setPredictiveLineNumbering(predicate, 100);
   }
 
   public interface LineTranslation {
@@ -1306,7 +1306,8 @@
         int nextLine = lines.getOrDefault(method, startingLineNumber);
         // Increment the actual line content by 100 so that each one is clearly distinct
         // from a PC value for any of the methods.
-        lines.put(method, nextLine + 100);
+        int nextNextLine = nextLine == -1 ? 100 : nextLine + 100;
+        lines.put(method, nextNextLine);
         return nextLine;
       }
       return line;
diff --git a/tools/asmifier.py b/tools/asmifier.py
index 82b90af..504f2bf 100755
--- a/tools/asmifier.py
+++ b/tools/asmifier.py
@@ -10,7 +10,7 @@
 import sys
 import utils
 
-ASM_VERSION = '9.4'
+ASM_VERSION = '9.5'
 ASM_JAR = 'asm-' + ASM_VERSION + '.jar'
 ASM_UTIL_JAR = 'asm-util-' + ASM_VERSION + '.jar'