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 }