Version 2.0.42

Cherry pick: Allow unreachable ret instructioins in the input.

CL: https://r8-review.googlesource.com/c/r8/+/49261
Bug: 150274427
Change-Id: Ia2b3624dd35e11146836ac25dd91deb59f67dfd6
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 8faf42c..3a0340e 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.41";
+  public static final String LABEL = "2.0.42";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index e30a78d..9eedd04 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -27,6 +27,7 @@
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.cf.code.CfInvokeDynamic;
+import com.android.tools.r8.cf.code.CfJsrRet;
 import com.android.tools.r8.cf.code.CfLabel;
 import com.android.tools.r8.cf.code.CfLoad;
 import com.android.tools.r8.cf.code.CfLogicalBinop;
@@ -654,6 +655,11 @@
     builder.append(type.getType().toString());
   }
 
+  public void print(CfJsrRet ret) {
+    indent();
+    builder.append("ret ").append(ret.getLocal());
+  }
+
   private String getLabel(CfLabel label) {
     return labelToIndex != null ? ("L" + labelToIndex.getInt(label)) : "L?";
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
new file mode 100644
index 0000000..32c3040
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfJsrRet.java
@@ -0,0 +1,56 @@
+// 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.cf.code;
+
+import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.ir.conversion.CfSourceCode;
+import com.android.tools.r8.ir.conversion.CfState;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.naming.NamingLens;
+import org.objectweb.asm.MethodVisitor;
+
+public class CfJsrRet extends CfInstruction {
+
+  private static CompilationError error() {
+    throw new CompilationError(
+        "Invalid compilation of code with reachable jump subroutine RET instruction");
+  }
+
+  private final int local;
+
+  public CfJsrRet(int local) {
+    this.local = local;
+  }
+
+  @Override
+  public void write(MethodVisitor visitor, NamingLens lens) {
+    throw error();
+  }
+
+  @Override
+  public void print(CfPrinter printer) {
+    printer.print(this);
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
+    throw error();
+  }
+
+  @Override
+  public ConstraintWithTarget inliningConstraint(InliningConstraints inliningConstraints,
+      DexType invocationContext, GraphLense graphLense, AppView<?> appView) {
+    throw error();
+  }
+
+  public int getLocal() {
+    return local;
+  }
+}
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 edec3a9..d012556 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.cf.code.CfInvokeDynamic;
+import com.android.tools.r8.cf.code.CfJsrRet;
 import com.android.tools.r8.cf.code.CfLabel;
 import com.android.tools.r8.cf.code.CfLoad;
 import com.android.tools.r8.cf.code.CfLogicalBinop;
@@ -654,7 +655,10 @@
           type = ValueType.OBJECT;
           break;
         case Opcodes.RET:
-          throw new JsrEncountered("RET should be handled by the ASM jsr inliner");
+          {
+            instructions.add(new CfJsrRet(var));
+            return;
+          }
         default:
           throw new Unreachable("Unexpected VarInsn opcode: " + opcode);
       }
diff --git a/src/test/java/com/android/tools/r8/regress/b150274427/JsrRetRegressionTest.java b/src/test/java/com/android/tools/r8/regress/b150274427/JsrRetRegressionTest.java
new file mode 100644
index 0000000..3c41a63
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b150274427/JsrRetRegressionTest.java
@@ -0,0 +1,92 @@
+// 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.b150274427;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.transformers.MethodTransformer;
+import java.io.IOException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class JsrRetRegressionTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+  }
+
+  public JsrRetRegressionTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testUnreachableJsrRet() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClassFileData(getTransformClass(false))
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertFailureWithErrorThatThrows(RuntimeException.class);
+  }
+
+  @Test
+  public void testReachableJsrRet() throws Exception {
+    if (parameters.isCfRuntime()) {
+      testForJvm()
+          .addProgramClassFileData(getTransformClass(true))
+          .run(parameters.getRuntime(), TestClass.class)
+          .assertFailureWithErrorThatThrows(VerifyError.class);
+      return;
+    }
+    try {
+      testForD8()
+          .addProgramClassFileData(getTransformClass(true))
+          .setMinApi(parameters.getApiLevel())
+          .compileWithExpectedDiagnostics(
+              diagnostics -> {
+                diagnostics.assertErrorMessageThatMatches(containsString("RET"));
+              });
+      fail();
+    } catch (CompilationFailedException e) {
+      // Expected error.
+    }
+  }
+
+  private byte[] getTransformClass(boolean replaceThrow) throws IOException {
+    return transformer(TestClass.class)
+        .setVersion(50)
+        .addMethodTransformer(
+            new MethodTransformer() {
+              @Override
+              public void visitInsn(int opcode) {
+                if (opcode == Opcodes.ATHROW) {
+                  if (!replaceThrow) {
+                    super.visitInsn(opcode);
+                  }
+                  super.visitVarInsn(Opcodes.RET, 0);
+                } else {
+                  super.visitInsn(opcode);
+                }
+              }
+            })
+        .transform();
+  }
+
+  private static class TestClass {
+
+    public static void main(String[] args) {
+      throw new RuntimeException();
+      // Reachable or unreachable JSR inserted here.
+    }
+  }
+}
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 52777d9..22a91f8 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -190,6 +190,21 @@
         });
   }
 
+  public ClassFileTransformer setVersion(int newVersion) {
+    return addClassTransformer(
+        new ClassTransformer() {
+          @Override
+          public void visit(
+              int version,
+              int access,
+              String name,
+              String signature,
+              String superName,
+              String[] interfaces) {
+            super.visit(newVersion, access, name, signature, superName, interfaces);
+          }
+        });
+  }
 
   public ClassFileTransformer setMinVersion(CfVm jdk) {
     return setMinVersion(jdk.getClassfileVersion());