| /* |
| * 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") & vm.flavor == "server" & !vm.emulatedClient & vm.compMode=="Xmixed" & (!vm.graal.enabled | vm.opt.TieredCompilation == true) & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel==4) |
| * @library /test/lib / |
| * @build sun.hotspot.WhiteBox |
| * @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission |
| * @compile IncompatibleClassChangeErrorTest.java |
| * @compile ImplementsSomeInterfaces.jasm ICC2_B.jasm ICC3_B.jasm ICC4_B.jasm |
| * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI |
| * -XX:CompileThreshold=1000 -XX:-BackgroundCompilation -XX:-Inline |
| * -XX:CompileCommand=exclude,test.IncompatibleClassChangeErrorTest::test_iccInt |
| * test.IncompatibleClassChangeErrorTest |
| */ |
| |
| package test; |
| |
| import sun.hotspot.WhiteBox; |
| import compiler.whitebox.CompilerWhiteBoxTest; |
| import java.lang.reflect.Method; |
| |
| // This test assembles an erroneous 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 test.ImplementsSomeInterfaces " + |
| "does not implement the requested interface test.InterfaceICCE1"; |
| private static String expectedErrorMessageCompiled = |
| "Class test.ICC2_B does not implement the requested interface test.ICC2_iB"; |
| // old message: "vtable stub" |
| |
| |
| private static boolean compile(Class<?> clazz, String name) { |
| try { |
| Method method = clazz.getMethod(name); |
| boolean enqueued = WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); |
| if (!enqueued) { |
| System.out.println("Warning: Blocking compilation failed for " + clazz.getName() + "." + name + " (timeout?)"); |
| return false; |
| } else if (!WHITE_BOX.isMethodCompiled(method)) { |
| throw new RuntimeException(clazz.getName() + "." + name + " is not compiled"); |
| } |
| } catch (NoSuchMethodException e) { |
| throw new RuntimeException(clazz.getName() + "." + name + " not found", e); |
| } |
| return true; |
| } |
| |
| public static boolean 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 |
| if (!compile(IncompatibleClassChangeErrorTest.class, "test_icc_compiled_itable_stub") || |
| !compile(ICC2_C.class, "b") || |
| !compile(ICC2_D.class, "b") || |
| !compile(ICC2_E.class, "b")) { |
| return false; |
| } |
| |
| System.out.println("warmup done."); |
| return true; |
| } |
| |
| // 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."); |
| } |
| if (enableChecks) { |
| System.out.println("Test 1 passed with message: " + errorMsg); |
| } |
| 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; |
| ICC2_B b = new ICC2_B(); |
| ICC2_C c = new ICC2_C(); |
| ICC2_D d = new ICC2_D(); |
| ICC2_E e = new ICC2_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++) { |
| ICC2_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("Test 2 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."); |
| } |
| } |
| |
| private static String expectedErrorMessage3 = |
| "class test.ICC3_B can not implement test.ICC3_A, because it is not an interface (test.ICC3_A is in unnamed module of loader 'app')"; |
| |
| public static void test3_implementsClass() throws Exception { |
| try { |
| new ICC3_B(); |
| throw new RuntimeException("Expected IncompatibleClassChangeError was not thrown."); |
| } catch (IncompatibleClassChangeError e) { |
| String errorMsg = e.getMessage(); |
| if (!errorMsg.equals(expectedErrorMessage3)) { |
| System.out.println("Expected: " + expectedErrorMessage3 + "\n" + |
| "but got: " + errorMsg); |
| throw new RuntimeException("Wrong error message of IncompatibleClassChangeError."); |
| } |
| System.out.println("Test 3 passed with message: " + errorMsg); |
| } catch (Throwable e) { |
| throw new RuntimeException("Caught unexpected exception: " + e); |
| } |
| } |
| |
| private static String expectedErrorMessage4 = |
| "class test.ICC4_B has interface test.ICC4_iA as super class"; |
| |
| public static void test4_extendsInterface() throws Exception { |
| try { |
| new ICC4_B(); |
| throw new RuntimeException("Expected IncompatibleClassChangeError was not thrown."); |
| } catch (IncompatibleClassChangeError e) { |
| String errorMsg = e.getMessage(); |
| if (!errorMsg.equals(expectedErrorMessage4)) { |
| System.out.println("Expected: " + expectedErrorMessage4 + "\n" + |
| "but got: " + errorMsg); |
| throw new RuntimeException("Wrong error message of IncompatibleClassChangeError."); |
| } |
| System.out.println("Test 4 passed with message: " + errorMsg); |
| } catch (Throwable e) { |
| throw new RuntimeException("Caught unexpected exception: " + e); |
| } |
| } |
| |
| public static void main(String[] args) throws Exception { |
| if (!setup_test()) { |
| return; |
| } |
| test_iccInt(); |
| test_icc_compiled_itable_stub(); |
| test3_implementsClass(); |
| test4_extendsInterface(); |
| } |
| } |
| |
| |
| // 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 ICC2_iA { |
| public void a(); |
| } |
| |
| interface ICC2_iB { |
| public void b(); |
| } |
| |
| // This is the erroneous class. A variant of it not |
| // implementing ICC2_iB is copied into the test before |
| // it is run. |
| class ICC2_B implements ICC2_iA, |
| // This interface is missing in the .jasm implementation. |
| ICC2_iB { |
| public void a() { |
| System.out.print("B.a() "); |
| } |
| |
| public void b() { |
| System.out.print("B.b() "); |
| } |
| } |
| |
| class ICC2_C implements ICC2_iA, ICC2_iB { |
| public void a() { |
| System.out.print("C.a() "); |
| } |
| |
| public void b() { |
| System.out.print("C.b() "); |
| } |
| } |
| |
| class ICC2_D implements ICC2_iA, ICC2_iB { |
| public void a() { |
| System.out.print("D.a() "); |
| } |
| |
| public void b() { |
| System.out.print("D.b() "); |
| } |
| } |
| |
| class ICC2_E implements ICC2_iA, ICC2_iB { |
| public void a() { |
| System.out.print("E.a() "); |
| } |
| |
| public void b() { |
| System.out.print("E.b() "); |
| } |
| } |
| |
| // Helper classes to test error where class appears in implements statement. |
| // |
| // Class hierachy: |
| // |
| // A Some Class. |
| // | |
| // B erroneous class. Correct B extends A, incorrect B (from jasm) implements A. |
| |
| class ICC3_A { |
| } |
| |
| class ICC3_B extends ICC3_A { |
| } |
| |
| // Helper classes to test error where interface appears in extends statement. |
| // |
| // Class hierachy: |
| // |
| // A Some Interface. |
| // | |
| // B erroneous class. Correct B implements A, incorrect B (from jasm) extends A. |
| |
| interface ICC4_iA { |
| } |
| |
| class ICC4_B implements ICC4_iA { |
| } |