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 }