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