1 /* 2 * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8056900 8203826 27 * @summary Verifies message returned with NoClassDefFoundError exception. 28 * @library /test/lib 29 * @modules java.base/jdk.internal.misc 30 * java.compiler 31 * @build sun.hotspot.WhiteBox 32 * @run driver ClassFileInstaller sun.hotspot.WhiteBox 33 * sun.hotspot.WhiteBox$WhiteBoxPermission 34 * @run main/native/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions 35 * -XX:+WhiteBoxAPI NoClassDefFoundErrorTest 36 */ 37 38 import java.net.URL; 39 import java.net.URLClassLoader; 40 import jdk.test.lib.Asserts; 41 import jdk.test.lib.compiler.InMemoryJavaCompiler; 42 import jdk.internal.misc.Unsafe; 43 import sun.hotspot.WhiteBox; 44 45 public class NoClassDefFoundErrorTest { 46 47 static native void callDefineClass(String className); 48 static native void callFindClass(String className); 49 static { 50 System.loadLibrary("NoClassDefFoundMsg"); 51 } 52 53 // Test illegal class names. Too long or null. 54 static void test_classNames() throws Exception { 55 Unsafe unsafe = Unsafe.getUnsafe(); 56 57 byte klassbuf[] = InMemoryJavaCompiler.compile("TestClass", "class TestClass { }"); 58 59 // Create a class name of length 65536. 60 StringBuilder tooBigClassName = new StringBuilder("z"); 61 for (int x = 0; x < 16; x++) { 62 tooBigClassName = tooBigClassName.append(tooBigClassName); 63 } 64 65 // Test JVM_DefineClass() with long name. 66 try { 67 unsafe.defineClass(tooBigClassName.toString(), klassbuf, 4, klassbuf.length - 4, null, null); 68 throw new RuntimeException("defineClass did not throw expected NoClassDefFoundError"); 69 } catch (NoClassDefFoundError e) { 70 if (!e.getMessage().contains("Class name exceeds maximum length of ")) { 71 throw new RuntimeException("Wrong NoClassDefFoundError: " + e.getMessage()); 72 } 73 } 74 75 // Test JNI_DefineClass() with long name. 76 try { 77 callDefineClass(tooBigClassName.toString()); 78 throw new RuntimeException("DefineClass did not throw expected NoClassDefFoundError"); 79 } catch (NoClassDefFoundError e) { 80 if (!e.getMessage().contains("Class name exceeds maximum length of ")) { 81 throw new RuntimeException("Wrong NoClassDefFoundError: " + e.getMessage()); 82 } 83 } 84 85 // Test JNI_FindClass() with long name. 86 try { 87 callFindClass(tooBigClassName.toString()); 88 throw new RuntimeException("DefineClass did not throw expected NoClassDefFoundError"); 89 } catch (NoClassDefFoundError e) { 90 if (!e.getMessage().contains("Class name exceeds maximum length of ")) { 91 throw new RuntimeException("Wrong NoClassDefFoundError: " + e.getMessage()); 92 } 93 } 94 95 // Test JNI_FindClass() with null name. 96 try { 97 callFindClass(null); 98 throw new RuntimeException("FindClass did not throw expected NoClassDefFoundError"); 99 } catch (NoClassDefFoundError e) { 100 if (!e.getMessage().contains("No class name given")) { 101 throw new RuntimeException("Wrong NoClassDefFoundError: " + e.getMessage()); 102 } 103 } 104 } 105 106 static final WhiteBox wb = WhiteBox.getWhiteBox(); 107 static ClassLoader cl; 108 109 public static class A { 110 public static final int i = (new Object[1])[2].hashCode(); 111 } 112 public static class B extends A {} 113 public static class C extends B {} 114 115 static class ClassWithFailedInitializer { 116 public static final int i; 117 static { 118 cl = new URLClassLoader(new URL[] { 119 ClassWithFailedInitializer.class.getProtectionDomain().getCodeSource().getLocation() }, null); 120 try { 121 cl.loadClass("NoClassDefFoundErrorTest$C").newInstance(); 122 } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { 123 throw new RuntimeException("Class should be found", e); 124 } 125 i = 42; 126 } 127 } 128 129 // If static initialization of a class failed, later access to it 130 // throws a NoClassDefFoundError. This error chains the exeption 131 // thrown in the previous initialization. This test assures this is 132 // working properly. 133 static void test_chainStaticInitializerException() throws Exception { 134 wb.fullGC(); 135 136 try { 137 if (ClassWithFailedInitializer.i == 0) { 138 throw new RuntimeException("Class initialization succeeded but is designed to fail."); 139 } 140 throw new Exception("Expected exception was not thrown."); 141 } 142 catch (ExceptionInInitializerError e) { 143 e.printStackTrace(); 144 Asserts.assertNE(e.getCause(), null, "Expecting cause in ExceptionInInitializerError."); 145 Asserts.assertEQ(e.getCause().getClass(), ArrayIndexOutOfBoundsException.class, "sanity"); 146 } 147 148 try { 149 // The second attempt to access a field in 'ClassWithFailedInitializer' should result in a 150 // 'NoClassDefFoundError' instead of an 'ExceptionInInitializerError' because the class is 151 // already in an erro state and its static intializer won't be evaluated one more time (see 152 // JVMS, 5.5 Initialization). 153 // Here we also check that after 8203826 this initial ExceptionInInitializerError is saved 154 // for class 'ClassWithFailedInitializer' and chained as cause into the NoClassDefFoundError 155 // thrown during the second attempt to access a field in 'ClassWithFailedInitializer'. 156 if (ClassWithFailedInitializer.i == 0) { 157 throw new RuntimeException("Class initialization succeeded but is designed to fail."); 158 } 159 throw new Exception("Expected exception was not thrown."); 160 } catch (NoClassDefFoundError e) { 161 e.printStackTrace(); 162 Asserts.assertNE(e.getCause(), null, 163 "Expecting chained ExceptionInInitializerError exception"); 164 Asserts.assertEQ(e.getCause().getClass(), ExceptionInInitializerError.class, 165 "Wrong exception chained: " + e.getCause()); 166 Asserts.assertNE(e.getCause().getCause(), null, 167 "Expecting chained ArrayIndexOutOfBoundsException exception"); 168 Asserts.assertEQ(e.getCause().getCause().getClass(), ArrayIndexOutOfBoundsException.class, "sanity"); 169 } 170 171 try { 172 // Instantiating class 'B' from the same class loader which was already used by the static 173 // initializer of 'ClassWithFailedInitializer' to load class 'C' (and thus transitively 174 // 'B' and 'A') should result in a NoClassDefFoundError instead of an 175 // ExceptionInInitializerError because class 'B' is already in an error state because of 176 // the failure in the static initializer of its base class 'A' in the previous loading attemt. 177 // Here we check that after 8203826 this initial ExceptionInInitializerError is saved 178 // for class 'B' and chained as cause into the NoClassDefFoundError thrown during the second 179 // loading attempt for class 'B'. 180 Object b = cl.loadClass("NoClassDefFoundErrorTest$B").newInstance(); 181 throw new RuntimeException("Class initialization succeeded but is designed to fail."); 182 } catch (NoClassDefFoundError e) { 183 e.printStackTrace(); 184 Asserts.assertNE(e.getCause(), null, 185 "Expecting chained ExceptionInInitializerError exception"); 186 Asserts.assertEQ(e.getCause().getClass(), ExceptionInInitializerError.class, 187 "Wrong exception chained: " + e.getCause()); 188 Asserts.assertNE(e.getCause().getCause(), null, 189 "Expecting chained ArrayIndexOutOfBoundsException exception"); 190 Asserts.assertEQ(e.getCause().getCause().getClass(), ArrayIndexOutOfBoundsException.class, "sanity"); 191 } 192 193 // Accessing 'ClassWithFailedInitializer.i' above triggers the loading of it's base class 'A'. 194 // At this point however, class 'A' shouldn't be referenced from anywhere else except from the 195 // custom URLCLassLoader 'cl' which loaded 'A'. 196 // Notice that change 8203826 introduced the saving of the ExceptionInInitializerError which 197 // was thrown during the execution of A's static initializer (together with the recursively 198 // chained ArrayIndexOutOfBoundsException) inside a java.util.Hashtable object attached to the 199 // ClassLoaderData of A's class loader (see ClassLoaderData::record_init_exception() in 200 // hotspot/share/classfile/classLoaderData.cpp). This saved exception potentially references 201 // class 'A' in its native backtrace. 202 // Change 8203826 however, also takes care to remove all native backtraces from chained exceptions 203 // in order to prevent keeping the classes alive which are referenced from them. In order to do that 204 // all saved exceptions are converted to exceptions with a symbolic stack trace and a nulled-out 205 // backtrace (see java.lang.Throwable::removeNativeBacktrace()). 206 207 // At this point, class 'A' should be alive (but only referenced from its class loader). 208 Asserts.assertEQ(wb.isClassAlive("NoClassDefFoundErrorTest$A"), true, 209 "NoClassDefFoundErrorTest.A should be alive."); 210 211 cl = null; 212 wb.fullGC(); 213 214 // After freeing the last reference to the class loader and doing a full GC, class 'A' should be unloaded. 215 Asserts.assertEQ(wb.isClassAlive("NoClassDefFoundErrorTest$A"), false, 216 "NoClassDefFoundErrorTest.A should be dead at this point."); 217 } 218 219 public static void main(String args[]) throws Exception { 220 test_classNames(); 221 test_chainStaticInitializerException(); 222 } 223 }