test/hotspot/jtreg/runtime/exceptionMsgs/IncompatibleClassChangeError/IncompatibleClassChangeErrorTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/exceptionMsgs/IncompatibleClassChangeError/IncompatibleClassChangeErrorTest.java Thu Feb 08 09:23:49 2018 +0100
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 SAP SE. 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
+ * @summary Check that the verbose message of ICCE is printed correctly.
+ * The test forces errors in vtable stubs and interpreter.
+ * @requires !(os.arch=="arm")
+ * @library /test/lib /
+ * @build sun.hotspot.WhiteBox
+ * @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @compile IncompatibleClassChangeErrorTest.java
+ * @compile ImplementsSomeInterfaces.jasm ICC_B.jasm
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ * -XX:-BackgroundCompilation -XX:-Inline
+ * -XX:CompileCommand=exclude,IncompatibleClassChangeErrorTest::test_iccInt
+ * IncompatibleClassChangeErrorTest
+ */
+
+import sun.hotspot.WhiteBox;
+import compiler.whitebox.CompilerWhiteBoxTest;
+import java.lang.reflect.Method;
+
+// This test assembles an errorneous installation of classes.
+// First, compile the test by @compile. This results in a legal set
+// of classes.
+// Then, with jasm, generate incompatible classes that overwrite
+// the class files in the build directory.
+// Last, call the real tests throwing IncompatibleClassChangeErrors
+// and check the messages generated.
+public class IncompatibleClassChangeErrorTest {
+
+ private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+
+ private static boolean enableChecks = true;
+
+ private static String expectedErrorMessageInterpreted =
+ "Class ImplementsSomeInterfaces " +
+ "does not implement the requested interface InterfaceICCE1";
+ private static String expectedErrorMessageCompiled =
+ "Class ICC_B does not implement the requested interface ICC_iB";
+ // old message: "vtable stub"
+
+ public static void setup_test() {
+ // Assure all exceptions are loaded.
+ new AbstractMethodError();
+ new IncompatibleClassChangeError();
+
+ enableChecks = false;
+ // Warmup
+ System.out.println("warmup:");
+ test_iccInt();
+ test_icc_compiled_itable_stub();
+ enableChecks = true;
+
+ // Compile
+ try {
+ Method method = IncompatibleClassChangeErrorTest.class.getMethod("test_icc_compiled_itable_stub");
+ WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
+ if (!WHITE_BOX.isMethodCompiled(method)) {
+ throw new RuntimeException(method.getName() + " is not compiled");
+ }
+ method = ICC_C.class.getMethod("b");
+ WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
+ if (!WHITE_BOX.isMethodCompiled(method)) {
+ throw new RuntimeException("ICC_C." + method.getName() + " is not compiled");
+ }
+ method = ICC_D.class.getMethod("b");
+ WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
+ if (!WHITE_BOX.isMethodCompiled(method)) {
+ throw new RuntimeException("ICC_D." + method.getName() + " is not compiled");
+ }
+ method = ICC_E.class.getMethod("b");
+ WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
+ if (!WHITE_BOX.isMethodCompiled(method)) {
+ throw new RuntimeException("ICC_E." + method.getName() + " is not compiled");
+ }
+ } catch (NoSuchMethodException e) { }
+ System.out.println("warmup done.");
+ }
+
+ // Should never be compiled.
+ public static void test_iccInt() {
+ boolean caught_icc = false;
+ try {
+ InterfaceICCE1 objectInterface = new ImplementsSomeInterfaces();
+ // IncompatibleClassChangeError gets thrown in
+ // - TemplateTable::invokeinterface()
+ // - LinkResolver::runtime_resolve_interface_method()
+ objectInterface.aFunctionOfMyInterface();
+ } catch (IncompatibleClassChangeError e) {
+ String errorMsg = e.getMessage();
+ if (enableChecks && !errorMsg.equals(expectedErrorMessageInterpreted)) {
+ System.out.println("Expected: " + expectedErrorMessageInterpreted + "\n" +
+ "but got: " + errorMsg);
+ throw new RuntimeException("Wrong error message of IncompatibleClassChangeError.");
+ }
+ caught_icc = true;
+ } catch (Throwable e) {
+ throw new RuntimeException("Caught unexpected exception: " + e);
+ }
+
+ // Check we got the exception.
+ if (!caught_icc) {
+ throw new RuntimeException("Expected IncompatibleClassChangeError was not thrown.");
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // Test AbstractMethodErrors detected in itable stubs.
+ // Note: How can we verify that we really stepped through the vtable stub?
+ // - Bimorphic inlining should not happen since we have no profiling data when
+ // we compile the method
+ // - As a result, an inline cache call should be generated
+ // - This inline cache call is patched into a real vtable call at the first
+ // re-resolve, which happens constantly during the first 10 iterations of the loop.
+ // => we should be fine! :-)
+ public static void test_icc_compiled_itable_stub() {
+ // Allocated the objects we need and call a valid method.
+ boolean caught_icc = false;
+ ICC_B b = new ICC_B();
+ ICC_C c = new ICC_C();
+ ICC_D d = new ICC_D();
+ ICC_E e = new ICC_E();
+ b.a();
+ c.a();
+ d.a();
+ e.a();
+
+ try {
+ final int iterations = 10;
+ // Test: calls b.b() in the last iteration.
+ for (int i = 0; i < iterations; i++) {
+ ICC_iB a = b;
+ if (i % 3 == 0 && i < iterations - 1) {
+ a = c;
+ }
+ if (i % 3 == 1 && i < iterations - 1) {
+ a = d;
+ }
+ if (i % 3 == 2 && i < iterations - 1) {
+ a = e;
+ }
+ a.b();
+ }
+ } catch (AbstractMethodError exc) {
+ // It's a subclass of IncompatibleClassChangeError, so we must catch this first.
+ System.out.println();
+ System.out.println(exc);
+ if (enableChecks) {
+ String errorMsg = exc.getMessage();
+ if (errorMsg == null) {
+ throw new RuntimeException("Caught unexpected AbstractMethodError with empty message.");
+ }
+ throw new RuntimeException("Caught unexpected AbstractMethodError.");
+ }
+ } catch (IncompatibleClassChangeError exc) {
+ caught_icc = true;
+ System.out.println();
+ String errorMsg = exc.getMessage();
+ if (enableChecks && errorMsg == null) {
+ System.out.println(exc);
+ throw new RuntimeException("Empty error message of IncompatibleClassChangeError.");
+ }
+ if (enableChecks &&
+ !errorMsg.equals(expectedErrorMessageCompiled)) {
+ System.out.println("Expected: " + expectedErrorMessageCompiled + "\n" +
+ "but got: " + errorMsg);
+ System.out.println(exc);
+ throw new RuntimeException("Wrong error message of IncompatibleClassChangeError.");
+ }
+ if (enableChecks) {
+ System.out.println("Passed with message: " + errorMsg);
+ }
+ } catch (Throwable exc) {
+ throw exc; // new RuntimeException("Caught unexpected exception: " + exc);
+ }
+
+ // Check we got the exception at some point.
+ if (enableChecks && !caught_icc) {
+ throw new RuntimeException("Expected IncompatibleClassChangeError was not thrown.");
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ setup_test();
+ test_iccInt();
+ test_icc_compiled_itable_stub();
+ }
+}
+
+
+// Helper classes to test incompatible class change in interpreter.
+//
+// The test also contains .jasm files with implementations
+// of the classes that shall generate the errors.
+
+
+// I0 // interface defining aFunctionOfMyInterface()
+// |
+// | I1 // interface
+// | |
+// A0 | // abstract class
+// \ /
+// C // class not implementing I1 and
+// not implementing I0::aFunctionOfMyInterface()
+//
+// Test is expected to throw error because of missing interface and not
+// because of missing method.
+
+interface InterfaceICCE0 {
+ public String firstFunctionOfMyInterface0();
+ public String secondFunctionOfMyInterface0();
+}
+
+interface InterfaceICCE1 {
+
+ public String firstFunctionOfMyInterface();
+
+ public String secondFunctionOfMyInterface();
+
+ public String aFunctionOfMyInterface();
+}
+
+abstract class AbstractICCE0 implements InterfaceICCE0 {
+ abstract public String firstAbstractMethod();
+ abstract public String secondAbstractMethod();
+
+ abstract public String anAbstractMethod();
+}
+
+class ImplementsSomeInterfaces extends
+ AbstractICCE0
+ // This interface is missing in the .jasm implementation.
+ implements InterfaceICCE1
+{
+
+ public String firstAbstractMethod() {
+ return this.getClass().getName();
+ }
+
+ public String secondAbstractMethod() {
+ return this.getClass().getName();
+ }
+
+ // This method is missing in the .jasm implementation.
+ public String anAbstractMethod() {
+ return this.getClass().getName();
+ }
+
+ public String firstFunctionOfMyInterface0() {
+ return this.getClass().getName();
+ }
+
+ public String secondFunctionOfMyInterface0() {
+ return this.getClass().getName();
+ }
+
+ public String firstFunctionOfMyInterface() {
+ return this.getClass().getName();
+ }
+
+ public String secondFunctionOfMyInterface() {
+ return this.getClass().getName();
+ }
+
+ // This method is missing in the .jasm implementation.
+ public String aFunctionOfMyInterface() {
+ return this.getClass().getName();
+ }
+}
+
+// Helper classes to test incompatible class change in itable stub.
+//
+// Class hierachy:
+//
+// iA,iB (interfaces)
+// /|\ \
+// C D E \
+// B (bad class, missing interface implementation)
+
+interface ICC_iA {
+ public void a();
+}
+
+interface ICC_iB {
+ public void b();
+}
+
+// This is the errorneous class. A variant of it not
+// implementing ICC_iB is copied into the test before
+// it is run.
+class ICC_B implements ICC_iA,
+ // This interface is missing in the .jasm implementation.
+ ICC_iB {
+ public void a() {
+ System.out.print("B.a() ");
+ }
+
+ public void b() {
+ System.out.print("B.b() ");
+ }
+}
+
+class ICC_C implements ICC_iA, ICC_iB {
+ public void a() {
+ System.out.print("C.a() ");
+ }
+
+ public void b() {
+ System.out.print("C.b() ");
+ }
+}
+
+class ICC_D implements ICC_iA, ICC_iB {
+ public void a() {
+ System.out.print("D.a() ");
+ }
+
+ public void b() {
+ System.out.print("D.b() ");
+ }
+}
+
+class ICC_E implements ICC_iA, ICC_iB {
+ public void a() {
+ System.out.print("E.a() ");
+ }
+
+ public void b() {
+ System.out.print("E.b() ");
+ }
+}