7ca4027957
Added new method class_in_module_of_loader to provide a standard format for class information within error messages. Reviewed-by: goetz, hseigel, mchung
435 lines
15 KiB
Java
435 lines
15 KiB
Java
/*
|
|
* 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 {
|
|
}
|