Test for unsupported ConstantDynamic.

Change-Id: Id2d4e998cbd07aa48acf58dc6fa2d56eb2e60649
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 76dd396..4d8d00d 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -73,6 +73,7 @@
 import java.util.function.BiFunction;
 import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ConstantDynamic;
 import org.objectweb.asm.Handle;
 import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
@@ -798,6 +799,8 @@
         instructions.add(
             new CfConstMethodHandle(
                 DexMethodHandle.fromAsmHandle((Handle) cst, application, method.holder)));
+      } else if (cst instanceof ConstantDynamic) {
+        throw new CompilationError("Unsupported dynamic constant: " + cst.toString());
       } else {
         throw new CompilationError("Unsupported constant: " + cst.toString());
       }
diff --git a/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java b/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
index 612be2c6..88f89c3 100644
--- a/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
+++ b/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
@@ -6,6 +6,7 @@
 
 import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION;
 
+import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
@@ -20,6 +21,7 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import org.objectweb.asm.ConstantDynamic;
 import org.objectweb.asm.Handle;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -118,6 +120,8 @@
       updateConstraint(inliningConstraints.forConstClass(type, invocationContext));
     } else if (cst instanceof Handle) {
       updateConstraint(inliningConstraints.forConstMethodHandle());
+    } else if (cst instanceof ConstantDynamic) {
+      throw new CompilationError("Unsupported dynamic constant: " + cst.toString());
     } else {
       updateConstraint(inliningConstraints.forConstInstruction());
     }
diff --git a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
index 991b861..fc9ea8b 100644
--- a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
+++ b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
@@ -6,6 +6,7 @@
 import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION;
 
 import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexField;
@@ -15,6 +16,7 @@
 import com.android.tools.r8.graph.JarApplicationReader;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.graph.UseRegistry.MethodHandleUse;
+import org.objectweb.asm.ConstantDynamic;
 import org.objectweb.asm.Handle;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -60,6 +62,8 @@
       } else {
         registry.registerConstClass(application.getType((Type) cst));
       }
+    } else if (cst instanceof ConstantDynamic) {
+      throw new CompilationError("Unsupported dynamic constant: " + cst.toString());
     } else if (cst instanceof Handle) {
       DexMethodHandle handle = DexMethodHandle.fromAsmHandle((Handle) cst, application, clazz);
       registry.registerMethodHandle(
diff --git a/src/test/java/com/android/tools/r8/TestRuntime.java b/src/test/java/com/android/tools/r8/TestRuntime.java
index c008660..6347853 100644
--- a/src/test/java/com/android/tools/r8/TestRuntime.java
+++ b/src/test/java/com/android/tools/r8/TestRuntime.java
@@ -23,23 +23,20 @@
 
   // Enum describing the possible/supported CF runtimes.
   public enum CfVm {
-    JDK8("jdk8"),
-    JDK9("jdk9"),
-    JDK11("jdk11");
+    JDK8("jdk8", 52),
+    JDK9("jdk9", 53),
+    JDK11("jdk11", 55);
 
     private final String name;
+    private final int classfileVersion;
 
-    public static CfVm fromName(String v) {
-      for (CfVm value : CfVm.values()) {
-        if (value.name.equals(v)) {
-          return value;
-        }
-      }
-      return null;
+    CfVm(String name, int classfileVersion) {
+      this.name = name;
+      this.classfileVersion = classfileVersion;
     }
 
-    CfVm(String name) {
-      this.name = name;
+    public int getClassfileVersion() {
+      return classfileVersion;
     }
 
     public static CfVm first() {
@@ -58,10 +55,6 @@
       return this.ordinal() <= other.ordinal();
     }
 
-    public boolean hasModularRuntime() {
-      return this != JDK8;
-    }
-
     @Override
     public String toString() {
       return name;
diff --git a/src/test/java/com/android/tools/r8/cf/ConstantDynamicHolderTest.java b/src/test/java/com/android/tools/r8/cf/ConstantDynamicHolderTest.java
new file mode 100644
index 0000000..81f50fc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/ConstantDynamicHolderTest.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2019, 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.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+import static org.objectweb.asm.Opcodes.H_INVOKESTATIC;
+
+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.TestRuntime.CfVm;
+import java.io.IOException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.ConstantDynamic;
+import org.objectweb.asm.Handle;
+
+@RunWith(Parameterized.class)
+public class ConstantDynamicHolderTest extends TestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+        .withDexRuntimes()
+        .withAllApiLevels()
+        .build();
+  }
+
+  final TestParameters parameters;
+
+  public ConstantDynamicHolderTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForJvm()
+        .addProgramClassFileData(getTransformedMain())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("null");
+  }
+
+  @Test(expected = CompilationFailedException.class)
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8()
+        .addProgramClassFileData(getTransformedMain())
+        .setMinApi(parameters.getApiLevel())
+        .compileWithExpectedDiagnostics(
+            diagnostics ->
+                diagnostics.assertErrorMessageThatMatches(
+                    containsString("Unsupported dynamic constant")));
+  }
+
+  @Test(expected = CompilationFailedException.class)
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(getTransformedMain())
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Main.class)
+        .compileWithExpectedDiagnostics(
+            diagnostics ->
+                diagnostics.assertErrorMessageThatMatches(
+                    containsString("Unsupported dynamic constant")));
+  }
+
+  private byte[] getTransformedMain() throws IOException {
+    return transformer(Main.class)
+        .setMinVersion(CfVm.JDK11)
+        .transformLdcInsnInMethod(
+            "main",
+            (value, continuation) -> {
+              assertEquals("replaced by dynamic null constant", value);
+              continuation.apply(getDynamicConstant());
+            })
+        .transform();
+  }
+
+  private ConstantDynamic getDynamicConstant() {
+    return new ConstantDynamic(
+        "dynamicnull",
+        "Ljava/lang/String;",
+        new Handle(
+            H_INVOKESTATIC,
+            "java/lang/invoke/ConstantBootstraps",
+            "nullConstant",
+            "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)"
+                + "Ljava/lang/Object;",
+            false));
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.println("replaced by dynamic null constant");
+    }
+  }
+}
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 d0d40bb..09faf46 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -5,6 +5,7 @@
 
 import static org.objectweb.asm.Opcodes.ASM7;
 
+import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.MethodAccessFlags;
@@ -27,8 +28,6 @@
 
 public class ClassFileTransformer {
 
-  private static final int NEST_SUPPORTED_VERSION = 55;
-
   /**
    * Basic algorithm for transforming the content of a class file.
    *
@@ -166,6 +165,10 @@
         });
   }
 
+  public ClassFileTransformer setMinVersion(CfVm jdk) {
+    return setMinVersion(jdk.getClassfileVersion());
+  }
+
   public ClassFileTransformer setMinVersion(int minVersion) {
     return addClassTransformer(
         new ClassTransformer() {
@@ -185,7 +188,7 @@
 
   public ClassFileTransformer setNest(Class<?> host, Class<?>... members) {
     assert !Arrays.asList(members).contains(host);
-    return setMinVersion(NEST_SUPPORTED_VERSION)
+    return setMinVersion(CfVm.JDK11)
         .addClassTransformer(
             new ClassTransformer() {
 
@@ -294,4 +297,31 @@
           }
         });
   }
+
+  /** Abstraction of the MethodVisitor.visitLdcInsn method with its continuation. */
+  @FunctionalInterface
+  public interface LdcInsnTransform {
+    void visitLdcInsn(Object value, LdcInsnTransformContinuation continuation);
+  }
+
+  /** Continuation for transforming a method. Will continue with the super visitor if called. */
+  @FunctionalInterface
+  public interface LdcInsnTransformContinuation {
+    void apply(Object value);
+  }
+
+  public ClassFileTransformer transformLdcInsnInMethod(
+      String methodName, LdcInsnTransform transform) {
+    return addMethodTransformer(
+        new MethodTransformer() {
+          @Override
+          public void visitLdcInsn(Object value) {
+            if (getContext().method.getMethodName().equals(methodName)) {
+              transform.visitLdcInsn(value, super::visitLdcInsn);
+            } else {
+              super.visitLdcInsn(value);
+            }
+          }
+        });
+  }
 }