1 /*
  2  * Copyright (c) 2020, 2026, 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 compiler.valhalla.inlinetypes;
 25 
 26 import compiler.lib.ir_framework.CompLevel;
 27 import compiler.lib.ir_framework.Run;
 28 import compiler.lib.ir_framework.Scenario;
 29 import compiler.lib.ir_framework.Test;
 30 
 31 import jdk.internal.value.ValueClass;
 32 import jdk.internal.vm.annotation.LooselyConsistentValue;
 33 import jdk.internal.vm.annotation.NullRestricted;
 34 
 35 import jdk.test.lib.Asserts;
 36 
 37 /*
 38  * @test
 39  * @key randomness
 40  * @summary Verify that chains of getfields on flat fields are correctly optimized.
 41  * @library /test/lib /
 42  * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
 43  * @enablePreview
 44  * @modules java.base/jdk.internal.value
 45  *          java.base/jdk.internal.vm.annotation
 46  * @compile GetfieldChains.jcod
 47  * @run main/timeout=300 compiler.valhalla.inlinetypes.TestGetfieldChains
 48  */
 49 
 50 @LooselyConsistentValue
 51 value class Point {
 52     int x = 4;
 53     int y = 7;
 54 }
 55 
 56 @LooselyConsistentValue
 57 value class Rectangle {
 58     @NullRestricted
 59     Point p0 = new Point();
 60     @NullRestricted
 61     Point p1 = new Point();
 62 }
 63 
 64 class NamedRectangle {
 65     @NullRestricted
 66     Rectangle rect;
 67     String name = "";
 68 
 69     NamedRectangle() {
 70         rect = new Rectangle();
 71         super();
 72     }
 73 
 74     static int getP1X(NamedRectangle nr) {
 75         return nr.rect
 76             .p1
 77             .x;
 78     }
 79 
 80     static Point getP1(NamedRectangle nr) {
 81         return nr.rect
 82             .p1;
 83     }
 84 }
 85 
 86 public class TestGetfieldChains {
 87 
 88     public static void main(String[] args) {
 89 
 90         final Scenario[] scenarios = {
 91                 new Scenario(0,
 92                         // C1 only
 93                         "-XX:TieredStopAtLevel=1",
 94                         "-XX:+TieredCompilation"),
 95                 new Scenario(1,
 96                         // C2 only. (Make sure the tests are correctly written)
 97                         "-XX:TieredStopAtLevel=4",
 98                         "-XX:-TieredCompilation",
 99                         "-XX:-OmitStackTraceInFastThrow"),
100                 new Scenario(2,
101                         // interpreter only
102                         "-Xint"),
103                 new Scenario(3,
104                         // Xcomp Only C1
105                         "-XX:TieredStopAtLevel=1",
106                         "-XX:+TieredCompilation",
107                         "-Xcomp"),
108                 new Scenario(4,
109                         // Xcomp Only C2
110                         "-XX:TieredStopAtLevel=4",
111                         "-XX:-TieredCompilation",
112                         "-XX:-OmitStackTraceInFastThrow",
113                         "-Xcomp")
114         };
115 
116         InlineTypes.getFramework()
117                    .addScenarios(scenarios)
118                    .addFlags("--enable-preview",
119                              "--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED",
120                              "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED")
121                    .start();
122     }
123 
124 
125     // Simple chain of getfields ending with value type field
126     @Test(compLevel = CompLevel.C1_SIMPLE)
127     public int test1() {
128         return NamedRectangle.getP1X(new NamedRectangle());
129     }
130 
131     @Run(test = "test1")
132     public void test1_verifier() {
133         int res = test1();
134         Asserts.assertEQ(res, 4);
135     }
136 
137     // Simple chain of getfields ending with a flat field
138     @Test(compLevel = CompLevel.C1_SIMPLE)
139     public Point test2() {
140         return NamedRectangle.getP1(new NamedRectangle());
141     }
142 
143     @Run(test = "test2")
144     public void test2_verifier() {
145         Point p = test2();
146         Asserts.assertEQ(p.x, 4);
147         Asserts.assertEQ(p.y, 7);
148     }
149 
150     // Chain of getfields but the initial receiver is null
151     @Test(compLevel = CompLevel.C1_SIMPLE)
152     public NullPointerException test3() {
153         NullPointerException npe = null;
154         try {
155             NamedRectangle.getP1X(null);
156             throw new RuntimeException("No NullPointerException thrown");
157         } catch (NullPointerException e) {
158             npe = e;
159         }
160         return npe;
161     }
162 
163     @Run(test = "test3")
164     public void test3_verifier() {
165         NullPointerException npe = test3();
166         Asserts.assertNE(npe, null);
167         StackTraceElement st = npe.getStackTrace()[0];
168         Asserts.assertEQ(st.getMethodName(), "getP1X");
169     }
170 
171     // Chain of getfields but one getfield in the middle of the chain triggers an illegal access
172     @Test(compLevel = CompLevel.C1_SIMPLE)
173     public IllegalAccessError test4() {
174         IllegalAccessError iae = null;
175         try {
176             int i = NamedRectangleP.getP1Y(new NamedRectangleP());
177             throw new RuntimeException("No IllegalAccessError thrown");
178         } catch (IllegalAccessError e) {
179             iae = e;
180         }
181         return iae;
182     }
183 
184     @Run(test = "test4")
185     public void test4_verifier() {
186         IllegalAccessError iae = test4();
187         Asserts.assertNE(iae, null);
188         StackTraceElement st = iae.getStackTrace()[0];
189         Asserts.assertEQ(st.getMethodName(), "getP1Y");
190         Asserts.assertTrue(iae.getMessage().contains("class compiler.valhalla.inlinetypes.NamedRectangleP tried to access private field compiler.valhalla.inlinetypes.RectangleP.p1"));
191     }
192 
193     // Chain of getfields but the last getfield triggers a NoSuchFieldError
194     @Test(compLevel = CompLevel.C1_SIMPLE)
195     public NoSuchFieldError test5() {
196         NoSuchFieldError nsfe = null;
197         try {
198             int i = NamedRectangleN.getP1X(new NamedRectangleN());
199             throw new RuntimeException("No NoSuchFieldError thrown");
200         } catch (NoSuchFieldError e) {
201             nsfe = e;
202         }
203         return nsfe;
204     }
205 
206     @Run(test = "test5")
207     public void test5_verifier() {
208         NoSuchFieldError nsfe = test5();
209         Asserts.assertNE(nsfe, null);
210         StackTraceElement st = nsfe.getStackTrace()[0];
211         Asserts.assertEQ(st.getMethodName(), "getP1X");
212         Asserts.assertEQ(nsfe.getMessage(), "Class compiler.valhalla.inlinetypes.PointN does not have member field 'int x'");
213     }
214 
215     @LooselyConsistentValue
216     static value class EmptyType1 { }
217 
218     @LooselyConsistentValue
219     static value class EmptyContainer1 {
220         int i = 0;
221         @NullRestricted
222         EmptyType1 et = new EmptyType1();
223     }
224 
225     @LooselyConsistentValue
226     static value class Container1 {
227         @NullRestricted
228         EmptyContainer1 container0 = new EmptyContainer1();
229         @NullRestricted
230         EmptyContainer1 container1 = new EmptyContainer1();
231     }
232 
233     @Test(compLevel = CompLevel.C1_SIMPLE)
234     public EmptyType1 test6() {
235         Container1 c = new Container1();
236         return c.container1.et;
237     }
238 
239     @Run(test = "test6")
240     public void test6_verifier() {
241         EmptyType1 et = test6();
242         Asserts.assertEQ(et, new EmptyType1());
243     }
244 
245     @Test(compLevel = CompLevel.C1_SIMPLE)
246     public EmptyType1 test7() {
247         Container1[] ca = (Container1[])ValueClass.newNullRestrictedNonAtomicArray(Container1.class, 10, new Container1());
248         return ca[3].container0.et;
249     }
250 
251     @Run(test = "test7")
252     public void test7_verifier() {
253         EmptyType1 et = test7();
254         Asserts.assertEQ(et, new EmptyType1());
255     }
256 
257     // Same as test6/test7 but not null-free and EmptyContainer2 with only one field
258 
259     static value class EmptyType2 { }
260 
261     // TODO 8376254: C1 bailouts if the type of the nullable flat field is uninitialized
262     static final EmptyType2 LOAD_EMPTY_TYPE_2 = new EmptyType2();
263     static final EmptyContainer2 LOAD_EMPTY_CONTAINER_2 = new EmptyContainer2();
264 
265     static value class EmptyContainer2 {
266         EmptyType2 et = null;
267     }
268 
269     static value class Container2 {
270         EmptyContainer2 container0 = new EmptyContainer2();
271         EmptyContainer2 container1 = new EmptyContainer2();
272     }
273 
274     @Test(compLevel = CompLevel.C1_SIMPLE)
275     public EmptyType2 test8() {
276         Container2 c = new Container2();
277         return c.container1.et;
278     }
279 
280     @Run(test = "test8")
281     public void test8_verifier() {
282         EmptyType2 et = test8();
283         Asserts.assertEQ(et, null);
284     }
285 
286     @Test(compLevel = CompLevel.C1_SIMPLE)
287     public EmptyType2 test9() {
288         Container2[] ca = new Container2[10];
289         ca[3] = new Container2();
290         return ca[3].container0.et;
291     }
292 
293     @Run(test = "test9")
294     public void test9_verifier() {
295         EmptyType2 et = test9();
296         Asserts.assertEQ(et, null);
297     }
298 }