1 /* 2 * Copyright (c) 2021, 2025, 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 package runtime.valhalla.inlinetypes; 24 25 import jdk.internal.value.ValueClass; 26 import jdk.test.lib.Asserts; 27 import jdk.internal.vm.annotation.LooselyConsistentValue; 28 import jdk.internal.vm.annotation.NullRestricted; 29 import jdk.internal.vm.annotation.Strict; 30 31 /* 32 * @test 33 * @summary Test several scenarios of class initialization failures 34 * @library /test/lib 35 * @modules java.base/jdk.internal.vm.annotation 36 * java.base/jdk.internal.value 37 * @enablePreview 38 * @compile ClassInitializationFailuresTest.java 39 * @run main/othervm runtime.valhalla.inlinetypes.ClassInitializationFailuresTest 40 */ 41 public class ClassInitializationFailuresTest { 42 static boolean failingInitialization = true; 43 static Object bo = null; 44 45 @LooselyConsistentValue 46 static value class BadOne { 47 int i = 0; 48 static { 49 if (ClassInitializationFailuresTest.failingInitialization) { 50 throw new RuntimeException("Failing initialization"); 51 } 52 } 53 } 54 55 @LooselyConsistentValue 56 static value class TestClass1 { 57 @Strict 58 @NullRestricted 59 BadOne badField = new BadOne(); 60 } 61 62 // Test handling of errors during the initialization of a value class 63 // Initialization of TestClass1 triggers the initialization of classes 64 // of all its value class typed fields, in this case BadOne 65 // Static initializer of BadOne throws an exception, so BadOne's initialization 66 // fails, which must caused the initialization of TestClass1 to fail too 67 // First attempt to instantiate TestClass1 must fail with an ExceptionInInitializerError 68 // because an exception has been thrown during the initialization process 69 // Second attempt to instantiate TestClass1 must fail with a NoClassDefFoundError 70 // because TestClass1 must already be in a failed initialization state (so no new 71 // attempt to initialize the class) 72 static void testClassInitialization() { 73 Throwable e = null; 74 try { 75 TestClass1 t1 = new TestClass1(); 76 } catch(Throwable t) { 77 e = t; 78 } 79 Asserts.assertNotNull(e, "Exception should have been thrown"); 80 Asserts.assertTrue(e.getClass() == ExceptionInInitializerError.class, "Must be an ExceptionInInitializerError"); 81 Asserts.assertTrue(e.getCause().getClass() == RuntimeException.class, "Must be the exception thown in the static initializer of BadOne"); 82 // Second attempt because it doesn't fail the same way 83 e = null; 84 try { 85 TestClass1 t1 = new TestClass1(); 86 } catch(Throwable t) { 87 e = t; 88 } 89 Asserts.assertNotNull(e, "Error should have been thrown"); 90 Asserts.assertTrue(e.getClass() == NoClassDefFoundError.class, "Must be a NoClassDefFoundError"); 91 Asserts.assertTrue(e.getCause().getClass() == ExceptionInInitializerError.class, "Must be an ExceptionInInitializerError"); 92 } 93 94 @LooselyConsistentValue 95 static value class BadTwo { 96 int i = 0; 97 static { 98 if (ClassInitializationFailuresTest.failingInitialization) { 99 throw new RuntimeException("Failing initialization"); 100 } 101 } 102 } 103 104 @LooselyConsistentValue 105 static value class BadThree { 106 int i = 0; 107 static { 108 if (ClassInitializationFailuresTest.failingInitialization) { 109 throw new RuntimeException("Failing initialization"); 110 } 111 } 112 } 113 114 // Same test as above, but for arrays of value objects 115 static void testArrayInitialization() { 116 // Testing anewarray when the value element class fails to initialize properly 117 Throwable e = null; 118 try { 119 BadTwo[] array = (BadTwo[]) ValueClass.newNullRestrictedNonAtomicArray(BadTwo.class, 10, new BadTwo()); 120 } catch(Throwable t) { 121 e = t; 122 } 123 Asserts.assertNotNull(e, "Error should have been thrown"); 124 Asserts.assertTrue(e.getClass() == ExceptionInInitializerError.class, " Must be an ExceptionInInitializerError"); 125 // Second attempt because it doesn't fail the same way 126 try { 127 BadTwo[] array = (BadTwo[]) ValueClass.newNullRestrictedNonAtomicArray(BadTwo.class, 10, new BadTwo()); 128 } catch(Throwable t) { 129 e = t; 130 } 131 Asserts.assertNotNull(e, "Error should have been thrown"); 132 Asserts.assertTrue(e.getClass() == NoClassDefFoundError.class, "Must be a NoClassDefFoundError"); 133 Asserts.assertTrue(e.getCause().getClass() == ExceptionInInitializerError.class, "Must be an ExceptionInInitializerError"); 134 /* 135 Transition model (annotations and array factory) doesn't permit multi-dimentional arrays tests 136 Disabling those tests for now 137 Testing multianewarray when the value element class fails to initialize properly 138 try { 139 BadThree[][] array = new BadThree[10][20]; 140 } catch(Throwable t) { 141 e = t; 142 } 143 Asserts.assertNotNull(e, "Error should have been thrown"); 144 Asserts.assertTrue(e.getClass() == ExceptionInInitializerError.class, " Must be an ExceptionInInitializerError"); 145 // Second attempt because it doesn't fail the same way 146 try { 147 BadThree[][][] array = new BadThree[10][30][10]; 148 } catch(Throwable t) { 149 e = t; 150 } 151 Asserts.assertNotNull(e, "Error should have been thrown"); 152 Asserts.assertTrue(e.getClass() == NoClassDefFoundError.class, "Must be a NoClassDefFoundError"); 153 Asserts.assertTrue(e.getCause().getClass() == ExceptionInInitializerError.class, "Must be an ExceptionInInitializerError"); 154 */ 155 } 156 157 @LooselyConsistentValue 158 static value class BadFour { 159 int i = 0; 160 static BadFour[] array; 161 static { 162 array = (BadFour[]) ValueClass.newNullRestrictedNonAtomicArray(BadFour.class, 10, new BadFour()); 163 if (ClassInitializationFailuresTest.failingInitialization) { 164 throw new RuntimeException("Failing initialization"); 165 } 166 } 167 } 168 169 // Even if a value class fails to initialize properly, some instances 170 // of this class can escape and be accessible. The JVM must be able to 171 // deal with those instances without crashes. The test below checks that 172 // escaped values stored in an array are handled correctly 173 static void testEscapedValueInArray() { 174 Throwable e = null; 175 try { 176 BadFour bt = new BadFour(); 177 } catch (Throwable t) { 178 e = t; 179 } 180 Asserts.assertNotNull(e, "Error must have been thrown"); 181 Asserts.assertTrue(e.getClass() == ExceptionInInitializerError.class, " Must be an ExceptionInInitializerError"); 182 e = null; 183 try { 184 BadFour t = BadFour.array[0]; 185 } catch(Throwable t) { 186 e = t; 187 } 188 Asserts.assertNotNull(e, "Error should have been thrown"); 189 Asserts.assertTrue(e.getClass() == NoClassDefFoundError.class, "Must be a NoClassDefFoundError"); 190 Asserts.assertTrue(e.getCause().getClass() == ExceptionInInitializerError.class, "Must be an ExceptionInInitializerError"); 191 } 192 193 @LooselyConsistentValue 194 static value class BadFive { 195 int i = 0; 196 static { 197 ClassInitializationFailuresTest.bo = new BadSix(); 198 if (ClassInitializationFailuresTest.failingInitialization) { 199 throw new RuntimeException("Failing initialization"); 200 } 201 } 202 } 203 204 static class BadSix { 205 BadFive bf = new BadFive(); 206 } 207 208 // Same test as above, but escaped values are stored in an object 209 static void testEscapedValueInObject() { 210 Throwable e = null; 211 try { 212 BadSix bt = new BadSix(); 213 } catch (Throwable t) { 214 e = t; 215 } 216 Asserts.assertNotNull(e, "Error must have been thrown"); 217 Asserts.assertNotNull(ClassInitializationFailuresTest.bo, "bo object should have been set"); 218 BadFive bf = ((BadSix)ClassInitializationFailuresTest.bo).bf; 219 } 220 221 public static void main(String[] args) { 222 testClassInitialization(); 223 testArrayInitialization(); 224 testEscapedValueInArray(); 225 testEscapedValueInObject(); 226 } 227 }