blob: ccd90efefd4b73fe731caa563ef0623824af1ce7 [file] [log] [blame]
/*
* Copyright 2015 SAP AG. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8141551
* @summary C2 can not handle returns with inccompatible interface arrays
* @library /testlibrary /testlibrary/whitebox/
* @build sun.hotspot.WhiteBox
* @run main ClassFileInstaller sun.hotspot.WhiteBox
* sun.hotspot.WhiteBox$WhiteBoxPermission
* @run main/othervm
* -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions
* -XX:+WhiteBoxAPI
* -Xbatch
* -XX:CompileThreshold=1
* -XX:-TieredCompilation
* -XX:CICompilerCount=1
* -XX:+PrintCompilation
* -XX:+PrintInlining
* -XX:CompileCommand=compileonly,MeetIncompatibleInterfaceArrays*.run
* -XX:CompileCommand=dontinline,TestMeetIncompatibleInterfaceArrays$Helper.createI2*
* -XX:CompileCommand=quiet
* TestMeetIncompatibleInterfaceArrays 0
* @run main/othervm
* -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions
* -XX:+WhiteBoxAPI
* -Xbatch
* -XX:CompileThreshold=1
* -XX:-TieredCompilation
* -XX:CICompilerCount=1
* -XX:+PrintCompilation
* -XX:+PrintInlining
* -XX:CompileCommand=compileonly,MeetIncompatibleInterfaceArrays*.run
* -XX:CompileCommand=inline,TestMeetIncompatibleInterfaceArrays$Helper.createI2*
* -XX:CompileCommand=quiet
* TestMeetIncompatibleInterfaceArrays 1
* @run main/othervm
* -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions
* -XX:+WhiteBoxAPI
* -Xbatch
* -XX:CompileThreshold=1
* -XX:Tier0InvokeNotifyFreqLog=0 -XX:Tier2InvokeNotifyFreqLog=0 -XX:Tier3InvokeNotifyFreqLog=0 -XX:Tier23InlineeNotifyFreqLog=0
* -XX:Tier3InvocationThreshold=2 -XX:Tier3MinInvocationThreshold=2 -XX:Tier3CompileThreshold=2
* -XX:Tier4InvocationThreshold=1 -XX:Tier4MinInvocationThreshold=1 -XX:Tier4CompileThreshold=1
* -XX:+TieredCompilation
* -XX:CICompilerCount=2
* -XX:+PrintCompilation
* -XX:+PrintInlining
* -XX:CompileCommand=compileonly,MeetIncompatibleInterfaceArrays*.run
* -XX:CompileCommand=compileonly,TestMeetIncompatibleInterfaceArrays$Helper.createI2*
* -XX:CompileCommand=inline,TestMeetIncompatibleInterfaceArrays$Helper.createI2*
* -XX:CompileCommand=quiet
* TestMeetIncompatibleInterfaceArrays 2
*
* @author volker.simonis@gmail.com
*/
import java.io.FileOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
import sun.hotspot.WhiteBox;
public class TestMeetIncompatibleInterfaceArrays extends ClassLoader {
private static final WhiteBox WB = WhiteBox.getWhiteBox();
public static interface I1 { public String getName(); }
public static interface I2 { public String getName(); }
public static class I2C implements I2 { public String getName() { return "I2";} }
public static class I21C implements I2, I1 { public String getName() { return "I2 and I1";} }
public static class Helper {
public static I2 createI2Array0() {
return new I2C();
}
public static I2[] createI2Array1() {
return new I2C[] { new I2C() };
}
public static I2[][] createI2Array2() {
return new I2C[][] { new I2C[] { new I2C() } };
}
public static I2[][][] createI2Array3() {
return new I2C[][][] { new I2C[][] { new I2C[] { new I2C() } } };
}
public static I2[][][][] createI2Array4() {
return new I2C[][][][] { new I2C[][][] { new I2C[][] { new I2C[] { new I2C() } } } };
}
public static I2[][][][][] createI2Array5() {
return new I2C[][][][][] { new I2C[][][][] { new I2C[][][] { new I2C[][] { new I2C[] { new I2C() } } } } };
}
public static I2 createI21Array0() {
return new I21C();
}
public static I2[] createI21Array1() {
return new I21C[] { new I21C() };
}
public static I2[][] createI21Array2() {
return new I21C[][] { new I21C[] { new I21C() } };
}
public static I2[][][] createI21Array3() {
return new I21C[][][] { new I21C[][] { new I21C[] { new I21C() } } };
}
public static I2[][][][] createI21Array4() {
return new I21C[][][][] { new I21C[][][] { new I21C[][] { new I21C[] { new I21C() } } } };
}
public static I2[][][][][] createI21Array5() {
return new I21C[][][][][] { new I21C[][][][] { new I21C[][][] { new I21C[][] { new I21C[] { new I21C() } } } } };
}
}
// Location for the generated class files
public static final String PATH = System.getProperty("test.classes", ".") + java.io.File.separator;
/*
* With 'good == false' this helper method creates the following classes
* (using the nested 'Helper' class and the nested interfaces 'I1' and 'I2').
* For brevity I omit the enclosing class 'TestMeetIncompatibleInterfaceArrays' in the
* following examples:
*
* public class MeetIncompatibleInterfaceArrays0ASM {
* public static I1 run() {
* return Helper.createI2Array0(); // returns I2
* }
* public static void test() {
* I1 i1 = run();
* System.out.println(i1.getName());
* }
* }
* public class MeetIncompatibleInterfaceArrays1ASM {
* public static I1[] run() {
* return Helper.createI2Array1(); // returns I2[]
* }
* public static void test() {
* I1[] i1 = run();
* System.out.println(i1[0].getName());
* }
* }
* ...
* // MeetIncompatibleInterfaceArrays4ASM is special because it creates
* // an illegal class which will be rejected by the verifier.
* public class MeetIncompatibleInterfaceArrays4ASM {
* public static I1[][][][] run() {
* return Helper.createI2Array3(); // returns I1[][][] which gives a verifier error because return expects I1[][][][]
* }
* public static void test() {
* I1[][][][][] i1 = run();
* System.out.println(i1[0][0][0][0][0].getName());
* }
* ...
* public class MeetIncompatibleInterfaceArrays5ASM {
* public static I1[][][][][] run() {
* return Helper.createI2Array5(); // returns I2[][][][][]
* }
* public static void test() {
* I1[][][][][] i1 = run();
* System.out.println(i1[0][0][0][0][0].getName());
* }
* }
*
* Notice that this is not legal Java code. We would have to use a cast in "run()" to make it legal:
*
* public static I1[] run() {
* return (I1[])Helper.createI2Array1(); // returns I2[]
* }
*
* But in pure bytecode, the "run()" methods are perfectly legal:
*
* public static I1[] run();
* Code:
* 0: invokestatic #16 // Method Helper.createI2Array1:()[LI2;
* 3: areturn
*
* The "test()" method calls the "getName()" function from I1 on the objects returned by "run()".
* This will epectedly fail with an "IncompatibleClassChangeError" because the objects returned
* by "run()" (and by createI2Array()) are actually of type "I2C" and only implement "I2" but not "I1".
*
*
* With 'good == true' this helper method will create the following classes:
*
* public class MeetIncompatibleInterfaceArraysGood0ASM {
* public static I1 run() {
* return Helper.createI21Array0(); // returns I2
* }
* public static void test() {
* I1 i1 = run();
* System.out.println(i1.getName());
* }
* }
*
* Calling "test()" on these objects will succeed and output "I2 and I1" because now the "run()"
* method calls "createI21Array()" which actually return an object (or an array of objects) of
* type "I21C" which implements both "I2" and "I1".
*
* Notice that at the bytecode level, the code for the "run()" and "test()" methods in
* "MeetIncompatibleInterfaceArraysASM" and "MeetIncompatibleInterfaceArraysGoodASM" look exactly
* the same. I.e. the verifier has no chance to verify if the I2 object returned by "createI1Array()"
* or "createI21Array()" implements "I1" or not. That's actually the reason why both versions of
* generated classes are legal from a verifier point of view.
*
*/
static void generateTestClass(int dim, boolean good) throws Exception {
String baseClassName = "MeetIncompatibleInterfaceArrays";
if (good)
baseClassName += "Good";
String createName = "createI2" + (good ? "1" : "") + "Array";
String a = "";
for (int i = 0; i < dim; i++)
a += "[";
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
cw.visit(V1_8, ACC_PUBLIC, baseClassName + dim + "ASM", null, "java/lang/Object", null);
MethodVisitor constr = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
constr.visitCode();
constr.visitVarInsn(ALOAD, 0);
constr.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
constr.visitInsn(RETURN);
constr.visitMaxs(0, 0);
constr.visitEnd();
MethodVisitor run = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "run",
"()" + a + "LTestMeetIncompatibleInterfaceArrays$I1;", null, null);
run.visitCode();
if (dim == 4) {
run.visitMethodInsn(INVOKESTATIC, "TestMeetIncompatibleInterfaceArrays$Helper", createName + 3,
"()" + "[[[" + "LTestMeetIncompatibleInterfaceArrays$I2;", false);
} else {
run.visitMethodInsn(INVOKESTATIC, "TestMeetIncompatibleInterfaceArrays$Helper", createName + dim,
"()" + a + "LTestMeetIncompatibleInterfaceArrays$I2;", false);
}
run.visitInsn(ARETURN);
run.visitMaxs(0, 0);
run.visitEnd();
MethodVisitor test = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "test", "()V", null, null);
test.visitCode();
test.visitMethodInsn(INVOKESTATIC, baseClassName + dim + "ASM", "run",
"()" + a + "LTestMeetIncompatibleInterfaceArrays$I1;", false);
test.visitVarInsn(ASTORE, 0);
if (dim > 0) {
test.visitVarInsn(ALOAD, 0);
for (int i = 1; i <= dim; i++) {
test.visitInsn(ICONST_0);
test.visitInsn(AALOAD);
}
test.visitVarInsn(ASTORE, 1);
}
test.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
test.visitVarInsn(ALOAD, dim > 0 ? 1 : 0);
test.visitMethodInsn(INVOKEINTERFACE, "TestMeetIncompatibleInterfaceArrays$I1", "getName",
"()Ljava/lang/String;", true);
test.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false);
test.visitInsn(RETURN);
test.visitMaxs(0, 0);
test.visitEnd();
// Get the bytes of the class..
byte[] b = cw.toByteArray();
// ..and write them into a class file (for debugging)
FileOutputStream fos = new FileOutputStream(PATH + baseClassName + dim + "ASM.class");
fos.write(b);
fos.close();
}
public static String[][] tier = { { "interpreted", "C2 (tier 4) without inlining", "C2 (tier4) without inlining" },
{ "interpreted", "C2 (tier 4) with inlining", "C2 (tier4) with inlining" },
{ "interpreted", "C1 (tier 3) with inlining", "C2 (tier4) with inlining" } };
public static void main(String[] args) throws Exception {
final int pass = Integer.parseInt(args.length > 0 ? args[0] : "0");
// Load and initialize some classes required for compilation
Class.forName("TestMeetIncompatibleInterfaceArrays$I1");
Class.forName("TestMeetIncompatibleInterfaceArrays$I2");
Class.forName("TestMeetIncompatibleInterfaceArrays$Helper");
for (int g = 0; g < 2; g++) {
String baseClassName = "MeetIncompatibleInterfaceArrays";
boolean good = (g == 0) ? false : true;
if (good)
baseClassName += "Good";
for (int i = 0; i < 6; i++) {
System.out.println();
System.out.println("Creating " + baseClassName + i + "ASM.class");
System.out.println("========================================" + "=" + "=========");
// Create the "MeetIncompatibleInterfaceArrays<i>ASM" class
generateTestClass(i, good);
Class<?> c = null;
try {
c = Class.forName(baseClassName + i + "ASM");
} catch (VerifyError ve) {
if (i == 4) {
System.out.println("OK - must be (" + ve.getMessage() + ").");
} else {
throw ve;
}
continue;
}
// Call MeetIncompatibleInterfaceArrays<i>ASM.test()
Method m = c.getMethod("test");
Method r = c.getMethod("run");
for (int j = 0; j < 3; j++) {
System.out.println((j + 1) + ". invokation of " + baseClassName + i + "ASM.test() [should be "
+ tier[pass][j] + "]");
try {
m.invoke(null);
} catch (InvocationTargetException ite) {
if (good) {
throw ite;
} else {
if (ite.getCause() instanceof IncompatibleClassChangeError) {
System.out.println(" OK - catched InvocationTargetException("
+ ite.getCause().getMessage() + ").");
} else {
throw ite;
}
}
}
}
System.out.println("Method " + r + (WB.isMethodCompiled(r) ? " has" : " has not") + " been compiled.");
if (!WB.isMethodCompiled(r)) {
throw new Exception("Method " + r + " must be compiled!");
}
}
}
}
}