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 }