--- old/test/hotspot/jtreg/runtime/noClassDefFoundMsg/NoClassDefFoundMsg.java 2018-07-06 15:39:25.794210718 +0000 +++ /dev/null 2018-07-06 13:41:09.078998377 +0000 @@ -1,97 +0,0 @@ -/* - * 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. - * - * 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 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. - StringBuilder tooBigClassName = new StringBuilder("z"); - for (int x = 0; x < 16; x++) { - tooBigClassName = tooBigClassName.append(tooBigClassName); - } - - // Test JVM_DefineClass() with long name. - try { - unsafe.defineClass(tooBigClassName.toString(), klassbuf, 4, klassbuf.length - 4, null, null); - throw new RuntimeException("defineClass did not throw expected NoClassDefFoundError"); - } catch (NoClassDefFoundError e) { - if (!e.getMessage().contains("Class name exceeds maximum length of ")) { - throw new RuntimeException("Wrong NoClassDefFoundError: " + e.getMessage()); - } - } - - // Test JNI_DefineClass() with long name. - try { - callDefineClass(tooBigClassName.toString()); - throw new RuntimeException("DefineClass did not throw expected NoClassDefFoundError"); - } catch (NoClassDefFoundError e) { - if (!e.getMessage().contains("Class name exceeds maximum length of ")) { - throw new RuntimeException("Wrong NoClassDefFoundError: " + e.getMessage()); - } - } - - // Test JNI_FindClass() with long name. - try { - callFindClass(tooBigClassName.toString()); - throw new RuntimeException("DefineClass did not throw expected NoClassDefFoundError"); - } catch (NoClassDefFoundError e) { - if (!e.getMessage().contains("Class name exceeds maximum length of ")) { - throw new RuntimeException("Wrong NoClassDefFoundError: " + e.getMessage()); - } - } - - // Test JNI_FindClass() with null name. - try { - callFindClass(null); - throw new RuntimeException("FindClass did not throw expected NoClassDefFoundError"); - } catch (NoClassDefFoundError e) { - if (!e.getMessage().contains("No class name given")) { - throw new RuntimeException("Wrong NoClassDefFoundError: " + e.getMessage()); - } - } - } -} --- /dev/null 2018-07-06 13:41:09.078998377 +0000 +++ new/test/hotspot/jtreg/runtime/exceptionMsgs/NoClassDefFoundError/NoClassDefFoundErrorTest.java 2018-07-06 15:39:25.643209645 +0000 @@ -0,0 +1,229 @@ +/* + * 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. + * + * 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 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. + StringBuilder tooBigClassName = new StringBuilder("z"); + for (int x = 0; x < 16; x++) { + tooBigClassName = tooBigClassName.append(tooBigClassName); + } + + // Test JVM_DefineClass() with long name. + try { + unsafe.defineClass(tooBigClassName.toString(), klassbuf, 4, klassbuf.length - 4, null, null); + throw new RuntimeException("defineClass did not throw expected NoClassDefFoundError"); + } catch (NoClassDefFoundError e) { + if (!e.getMessage().contains("Class name exceeds maximum length of ")) { + throw new RuntimeException("Wrong NoClassDefFoundError: " + e.getMessage()); + } + } + + // Test JNI_DefineClass() with long name. + try { + callDefineClass(tooBigClassName.toString()); + throw new RuntimeException("DefineClass did not throw expected NoClassDefFoundError"); + } catch (NoClassDefFoundError e) { + if (!e.getMessage().contains("Class name exceeds maximum length of ")) { + throw new RuntimeException("Wrong NoClassDefFoundError: " + e.getMessage()); + } + } + + // Test JNI_FindClass() with long name. + try { + callFindClass(tooBigClassName.toString()); + throw new RuntimeException("DefineClass did not throw expected NoClassDefFoundError"); + } catch (NoClassDefFoundError e) { + if (!e.getMessage().contains("Class name exceeds maximum length of ")) { + throw new RuntimeException("Wrong NoClassDefFoundError: " + e.getMessage()); + } + } + + // Test JNI_FindClass() with null name. + try { + callFindClass(null); + throw new RuntimeException("FindClass did not throw expected NoClassDefFoundError"); + } catch (NoClassDefFoundError e) { + 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 e) { + throw new RuntimeException("Class should be found", e); + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException("This should not happen", 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 == 42) { + throw new RuntimeException("Class initialization succeeded but is designed to fail."); + } + } + 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 == 42) { + 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.assertTrue(e.getCause().toString() + .startsWith(ExceptionInInitializerError.class.getName()), + "Wrong exception chained: " + e.getCause()); + Asserts.assertNE(e.getCause().getCause(), null, + "Expecting chained ArrayIndexOutOfBoundsException exception"); + Asserts.assertTrue(e.getCause().getCause().toString() + .startsWith(ArrayIndexOutOfBoundsException.class.getName()), + "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.assertTrue(e.getCause().toString() + .startsWith(ExceptionInInitializerError.class.getName()), + "Wrong exception chained: " + e.getCause()); + Asserts.assertNE(e.getCause().getCause(), null, + "Expecting chained ArrayIndexOutOfBoundsException exception"); + Asserts.assertTrue(e.getCause().getCause().toString() + .startsWith(ArrayIndexOutOfBoundsException.class.getName()), + "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(); + } +}