< prev index next >
test/hotspot/jtreg/runtime/exceptionMsgs/NoClassDefFoundError/NoClassDefFoundErrorTest.java
Print this page
rev 50866 : 8203826: Chain exception from initialization in later NoClassDefFoundErrors.
*** 1,7 ****
/*
! * Copyright (c) 2017, Oracle and/or its affiliates. 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.
--- 1,7 ----
/*
! * Copyright (c) 2017, 2018, Oracle and/or its affiliates. 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.
*** 21,51 ****
* questions.
*/
/*
* @test
! * @bug 8056900
* @summary Verifies message returned with NoClassDefFoundError exception.
* @library /test/lib
* @modules java.base/jdk.internal.misc
* java.compiler
! * @run main/native NoClassDefFoundMsg
*/
import jdk.test.lib.compiler.InMemoryJavaCompiler;
import jdk.internal.misc.Unsafe;
! public class NoClassDefFoundMsg {
static native void callDefineClass(String className);
static native void callFindClass(String className);
static {
System.loadLibrary("NoClassDefFoundMsg");
}
!
! public static void main(String args[]) throws Exception {
Unsafe unsafe = Unsafe.getUnsafe();
byte klassbuf[] = InMemoryJavaCompiler.compile("TestClass", "class TestClass { }");
// Create a class name of length 65536.
--- 21,59 ----
* questions.
*/
/*
* @test
! * @bug 8056900 8203826
* @summary Verifies message returned with NoClassDefFoundError exception.
* @library /test/lib
* @modules java.base/jdk.internal.misc
* java.compiler
! * @build sun.hotspot.WhiteBox
! * @run driver ClassFileInstaller sun.hotspot.WhiteBox
! * sun.hotspot.WhiteBox$WhiteBoxPermission
! * @run main/native/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
! * -XX:+WhiteBoxAPI NoClassDefFoundErrorTest
*/
+ import java.net.URL;
+ import java.net.URLClassLoader;
+ import jdk.test.lib.Asserts;
import jdk.test.lib.compiler.InMemoryJavaCompiler;
import jdk.internal.misc.Unsafe;
+ import sun.hotspot.WhiteBox;
! public class NoClassDefFoundErrorTest {
static native void callDefineClass(String className);
static native void callFindClass(String className);
static {
System.loadLibrary("NoClassDefFoundMsg");
}
! // Test illegal class names. Too long or null.
! static void test_classNames() throws Exception {
Unsafe unsafe = Unsafe.getUnsafe();
byte klassbuf[] = InMemoryJavaCompiler.compile("TestClass", "class TestClass { }");
// Create a class name of length 65536.
*** 92,97 ****
--- 100,223 ----
if (!e.getMessage().contains("No class name given")) {
throw new RuntimeException("Wrong NoClassDefFoundError: " + e.getMessage());
}
}
}
+
+ static final WhiteBox wb = WhiteBox.getWhiteBox();
+ static ClassLoader cl;
+
+ public static class A {
+ public static final int i = (new Object[1])[2].hashCode();
+ }
+ public static class B extends A {}
+ public static class C extends B {}
+
+ static class ClassWithFailedInitializer {
+ public static final int i;
+ static {
+ cl = new URLClassLoader(new URL[] {
+ ClassWithFailedInitializer.class.getProtectionDomain().getCodeSource().getLocation() }, null);
+ try {
+ cl.loadClass("NoClassDefFoundErrorTest$C").newInstance();
+ } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
+ throw new RuntimeException("Class should be found", e);
+ }
+ i = 42;
+ }
+ }
+
+ // If static initialization of a class failed, later access to it
+ // throws a NoClassDefFoundError. This error chains the exeption
+ // thrown in the previous initialization. This test assures this is
+ // working properly.
+ static void test_chainStaticInitializerException() throws Exception {
+ wb.fullGC();
+
+ try {
+ if (ClassWithFailedInitializer.i == 0) {
+ throw new RuntimeException("Class initialization succeeded but is designed to fail.");
+ }
+ throw new Exception("Expected exception was not thrown.");
+ }
+ catch (ExceptionInInitializerError e) {
+ e.printStackTrace();
+ Asserts.assertNE(e.getCause(), null, "Expecting cause in ExceptionInInitializerError.");
+ Asserts.assertEQ(e.getCause().getClass(), ArrayIndexOutOfBoundsException.class, "sanity");
+ }
+
+ try {
+ // The second attempt to access a field in 'ClassWithFailedInitializer' should result in a
+ // 'NoClassDefFoundError' instead of an 'ExceptionInInitializerError' because the class is
+ // already in an erro state and its static intializer won't be evaluated one more time (see
+ // JVMS, 5.5 Initialization).
+ // Here we also check that after 8203826 this initial ExceptionInInitializerError is saved
+ // for class 'ClassWithFailedInitializer' and chained as cause into the NoClassDefFoundError
+ // thrown during the second attempt to access a field in 'ClassWithFailedInitializer'.
+ if (ClassWithFailedInitializer.i == 0) {
+ throw new RuntimeException("Class initialization succeeded but is designed to fail.");
+ }
+ throw new Exception("Expected exception was not thrown.");
+ } catch (NoClassDefFoundError e) {
+ e.printStackTrace();
+ Asserts.assertNE(e.getCause(), null,
+ "Expecting chained ExceptionInInitializerError exception");
+ Asserts.assertEQ(e.getCause().getClass(), ExceptionInInitializerError.class,
+ "Wrong exception chained: " + e.getCause());
+ Asserts.assertNE(e.getCause().getCause(), null,
+ "Expecting chained ArrayIndexOutOfBoundsException exception");
+ Asserts.assertEQ(e.getCause().getCause().getClass(), ArrayIndexOutOfBoundsException.class, "sanity");
+ }
+
+ try {
+ // Instantiating class 'B' from the same class loader which was already used by the static
+ // initializer of 'ClassWithFailedInitializer' to load class 'C' (and thus transitively
+ // 'B' and 'A') should result in a NoClassDefFoundError instead of an
+ // ExceptionInInitializerError because class 'B' is already in an error state because of
+ // the failure in the static initializer of its base class 'A' in the previous loading attemt.
+ // Here we check that after 8203826 this initial ExceptionInInitializerError is saved
+ // for class 'B' and chained as cause into the NoClassDefFoundError thrown during the second
+ // loading attempt for class 'B'.
+ Object b = cl.loadClass("NoClassDefFoundErrorTest$B").newInstance();
+ throw new RuntimeException("Class initialization succeeded but is designed to fail.");
+ } catch (NoClassDefFoundError e) {
+ e.printStackTrace();
+ Asserts.assertNE(e.getCause(), null,
+ "Expecting chained ExceptionInInitializerError exception");
+ Asserts.assertEQ(e.getCause().getClass(), ExceptionInInitializerError.class,
+ "Wrong exception chained: " + e.getCause());
+ Asserts.assertNE(e.getCause().getCause(), null,
+ "Expecting chained ArrayIndexOutOfBoundsException exception");
+ Asserts.assertEQ(e.getCause().getCause().getClass(), ArrayIndexOutOfBoundsException.class, "sanity");
+ }
+
+ // Accessing 'ClassWithFailedInitializer.i' above triggers the loading of it's base class 'A'.
+ // At this point however, class 'A' shouldn't be referenced from anywhere else except from the
+ // custom URLCLassLoader 'cl' which loaded 'A'.
+ // Notice that change 8203826 introduced the saving of the ExceptionInInitializerError which
+ // was thrown during the execution of A's static initializer (together with the recursively
+ // chained ArrayIndexOutOfBoundsException) inside a java.util.Hashtable object attached to the
+ // ClassLoaderData of A's class loader (see ClassLoaderData::record_init_exception() in
+ // hotspot/share/classfile/classLoaderData.cpp). This saved exception potentially references
+ // class 'A' in its native backtrace.
+ // Change 8203826 however, also takes care to remove all native backtraces from chained exceptions
+ // in order to prevent keeping the classes alive which are referenced from them. In order to do that
+ // all saved exceptions are converted to exceptions with a symbolic stack trace and a nulled-out
+ // backtrace (see java.lang.Throwable::removeNativeBacktrace()).
+
+ // At this point, class 'A' should be alive (but only referenced from its class loader).
+ Asserts.assertEQ(wb.isClassAlive("NoClassDefFoundErrorTest$A"), true,
+ "NoClassDefFoundErrorTest.A should be alive.");
+
+ cl = null;
+ wb.fullGC();
+
+ // After freeing the last reference to the class loader and doing a full GC, class 'A' should be unloaded.
+ Asserts.assertEQ(wb.isClassAlive("NoClassDefFoundErrorTest$A"), false,
+ "NoClassDefFoundErrorTest.A should be dead at this point.");
+ }
+
+ public static void main(String args[]) throws Exception {
+ test_classNames();
+ test_chainStaticInitializerException();
+ }
}
< prev index next >