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