1 /*
  2  * Copyright (c) 2019, 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.test.lib.Asserts;
 26 import jdk.internal.vm.annotation.ImplicitlyConstructible;
 27 import jdk.internal.vm.annotation.LooselyConsistentValue;
 28 import jdk.internal.vm.annotation.NullRestricted;
 29 
 30 /*
 31  * @test
 32  * @summary Test initialization of static inline fields with circularity
 33  * @library /test/lib
 34  * @modules java.base/jdk.internal.vm.annotation
 35  * @enablePreview
 36  * @compile CircularityTest.java
 37  * @run main/othervm runtime.valhalla.inlinetypes.CircularityTest
 38  */
 39 
 40 
 41 public class CircularityTest {
 42     static boolean b = true;
 43     static int counter = 0;
 44     @ImplicitlyConstructible
 45     @LooselyConsistentValue
 46     static value class A {
 47         @NullRestricted
 48         static B b;
 49         @NullRestricted
 50         static C c;
 51         int i = 0;
 52     }
 53 
 54     @ImplicitlyConstructible
 55     @LooselyConsistentValue
 56     static value class B {
 57         static {
 58             Asserts.assertNotNull(A.c, "Should have returned C's default value");
 59         }
 60         int i = 0;
 61     }
 62 
 63     @ImplicitlyConstructible
 64     @LooselyConsistentValue
 65     static value class C {
 66         int i;
 67         public C(int i) {
 68             this.i = i;
 69         }
 70     }
 71 
 72     @ImplicitlyConstructible
 73     @LooselyConsistentValue
 74     static value class D {
 75         @NullRestricted
 76         static C c;
 77         int i = 0;
 78         static {
 79             if (CircularityTest.b) {
 80                 // throw an exception to cause D's initialization to fail
 81                 throw new RuntimeException();
 82             }
 83         }
 84     }
 85 
 86     @ImplicitlyConstructible
 87     @LooselyConsistentValue
 88     static value class E {
 89         @NullRestricted
 90         static F f;
 91         @NullRestricted
 92         static C c;
 93         int i = 0;
 94     }
 95 
 96     @ImplicitlyConstructible
 97     @LooselyConsistentValue
 98     static value class F {
 99         int i = 0;
100         static {
101             E.c = new C(5);
102         }
103     }
104 
105     @ImplicitlyConstructible
106     @LooselyConsistentValue
107     static value class G {
108         @NullRestricted
109         static H h;
110         int i = 0;
111     }
112 
113     @ImplicitlyConstructible
114     @LooselyConsistentValue
115     static value class H {
116         int i = 0;
117         static {
118             if (CircularityTest.b) {
119                 // throw an exception to cause H's initialization to fail
120                 throw new RuntimeException();
121             }
122         }
123     }
124 
125     @ImplicitlyConstructible
126     @LooselyConsistentValue
127     static value class I {
128         @NullRestricted
129         static J j;
130         @NullRestricted
131         static H h;
132         int i = 0;
133     }
134 
135     @ImplicitlyConstructible
136     @LooselyConsistentValue
137     static value class J {
138         int i = 0;
139         static {
140             CircularityTest.counter = 1;
141             H h = I.h;
142             CircularityTest.counter = 2;
143         }
144     }
145 
146     static public void main(String[] args) {
147         Throwable exception = null;
148         // Test 1:
149         // Initialization of A will trigger initialization of B which, in its static
150         // initializer will access a static inline field c of A that has not been initialized
151         // yet. The access must succeed (no exception) because the field is being
152         // accessed during the initialization of D, by the thread initializing D,
153         // and the value must be the default value of C (not null).
154         try {
155             A a = new A();
156         } catch (Throwable t) {
157             exception = t;
158         }
159         Asserts.assertNull(exception, "Circularity should not have caused exceptions");
160 
161         // Test 2:
162         // Class D will fail to initialized (exception thrown in its static initializer).
163         // Attempt to access a static inline field of D *after* its failed initialization
164         // should trigger an exception.
165         exception = null;
166         try {
167             D d = new D();
168         } catch (Throwable t) {
169             // ignoring the exception thrown to cause initialization failure
170         }
171         try {
172             C c = D.c;
173         } catch (Throwable t) {
174             exception = t;
175         }
176         Asserts.assertNotNull(exception, "Accessing static fields of a class which failed to initialized should throw an exception");
177         Asserts.assertEquals(exception.getClass(), java.lang.NoClassDefFoundError.class, "Wrong exception class");
178         // Test 3:
179         // Initialization of E will trigger the initialization of F which, in its static initalizer,
180         // will initialized a static inline field of F before the JVM does. The JVM must not
181         // overwrite the value set by user code.
182         E e = new E();
183         Asserts.assertEquals(E.c.i, 5, "JVM must not overwrite fields initialized by user code");
184 
185         // Test 4:
186         // Initialization of G should fail because its static inline field h
187         exception = null;
188         try {
189             G g = new G();
190         } catch(Throwable t) {
191             exception = t;
192         }
193         Asserts.assertNotNull(exception, "G's initialization should have failed");
194         Asserts.assertEquals(exception.getClass(), java.lang.ExceptionInInitializerError.class, "Wrong exception");
195 
196         // Test 5:
197         // Initialization of of I should fail when J tries to access I.h
198         exception = null;
199         try {
200             I i = new I();
201         } catch(Throwable t) {
202             exception = t;
203         }
204         Asserts.assertNotNull(exception, "I's initialization should have failed");
205         Asserts.assertEquals(exception.getClass(), java.lang.NoClassDefFoundError.class, "Wrong exception");
206         Asserts.assertEquals(CircularityTest.counter, 1, "Didn't failed at the right place");
207     }
208 }