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);
+ }
+ }
+ });
+ }
}