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