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