1 /*
  2  * Copyright (c) 2024, 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  /*
 25  * @test id=NullMarker32
 26  * @ignore
 27  * @requires vm.bits == 32
 28  * @library /test/lib
 29  * @modules java.base/jdk.internal.vm.annotation
 30  * @enablePreview
 31  * @compile FieldLayoutAnalyzer.java NullMarkersTest.java
 32  * @run main/othervm NullMarkersTest 0
 33  */
 34 
 35 /*
 36  * @test id=NullMarker64CompressedOops
 37  * @ignore
 38  * @requires vm.bits == 64
 39  * @library /test/lib
 40  * @modules java.base/jdk.internal.vm.annotation
 41  * @enablePreview
 42  * @compile FieldLayoutAnalyzer.java NullMarkersTest.java
 43  * @run main/othervm NullMarkersTest 1
 44  */
 45 
 46 /*
 47  * @test id=NullMarker64NoCompressedOops
 48  * @ignore
 49  * @requires vm.bits == 64
 50  * @library /test/lib
 51  * @modules java.base/jdk.internal.vm.annotation
 52  * @enablePreview
 53  * @compile FieldLayoutAnalyzer.java NullMarkersTest.java
 54  * @run main/othervm NullMarkersTest 2
 55  */
 56 
 57 /*
 58  * @test id=NullMarker64NoCompressedOopsNoCompressedKlassPointers
 59  * @ignore
 60  * @requires vm.bits == 64
 61  * @library /test/lib
 62  * @modules java.base/jdk.internal.vm.annotation
 63  * @enablePreview
 64  * @compile FieldLayoutAnalyzer.java NullMarkersTest.java
 65  * @run main/othervm NullMarkersTest 3
 66  */
 67 
 68 import java.util.ArrayList;
 69 import java.util.Collections;
 70 import java.util.List;
 71 
 72 import jdk.internal.vm.annotation.NullRestricted;
 73 import jdk.internal.vm.annotation.Strict;
 74 
 75 import java.lang.reflect.Method;
 76 import java.lang.reflect.Modifier;
 77 import jdk.test.lib.Asserts;
 78 import jdk.test.lib.process.OutputAnalyzer;
 79 import jdk.test.lib.process.ProcessTools;
 80 
 81 public class NullMarkersTest {
 82 
 83 
 84   static class TestRunner {
 85     public static void main(String[] args) throws Exception {
 86       Class testClass = Class.forName("NullMarkersTest");
 87       Asserts.assertNotNull(testClass);
 88       Method[] testMethods = testClass.getMethods();
 89       for (Method test : testMethods) {
 90         if (test.getName().startsWith("test_")) {
 91           Asserts.assertTrue(Modifier.isStatic(test.getModifiers()));
 92           Asserts.assertTrue(test.getReturnType().equals(Void.TYPE));
 93           System.out.println("Running " + test.getName());
 94           test.invoke(null);
 95         }
 96       }
 97     }
 98   }
 99 
100   static value class Value0 {
101     int i = 0;
102   }
103 
104   static class Container0 {
105     Value0 val;
106   }
107 
108   // Simple test with a single nullable flattenable field
109   static public void test_0() {
110     Container0 c = new Container0();
111   }
112 
113   static public void check_0(FieldLayoutAnalyzer fla) {
114     FieldLayoutAnalyzer.ClassLayout cl = fla.getClassLayoutFromName("NullMarkersTest$Container0");
115     FieldLayoutAnalyzer.FieldBlock f = cl.getFieldFromName("val", false);
116     Asserts.assertTrue(f.isFlat());
117     Asserts.assertTrue(f.hasNullMarker());
118   }
119 
120   static value class Value1 {
121     short s = 0;
122   }
123 
124   static class Container1 {
125     Value1 val0;
126     Value1 val1;
127     @Strict
128     @NullRestricted
129     Value1 val2 = new Value1();
130   }
131 
132   static public void test_1() {
133     Container1 c = new Container1();
134   }
135 
136   static public void check_1(FieldLayoutAnalyzer fla) {
137     FieldLayoutAnalyzer.ClassLayout cl = fla.getClassLayoutFromName("NullMarkersTest$Container1");
138     FieldLayoutAnalyzer.FieldBlock f0 = cl.getFieldFromName("val0", false);
139     Asserts.assertTrue(f0.isFlat());
140     Asserts.assertTrue(f0.hasNullMarker());
141     FieldLayoutAnalyzer.FieldBlock f1 = cl.getFieldFromName("val1", false);
142     Asserts.assertTrue(f1.isFlat());
143     Asserts.assertTrue(f1.hasNullMarker());
144     FieldLayoutAnalyzer.FieldBlock f2 = cl.getFieldFromName("val2", false);
145     Asserts.assertTrue(f2.isFlat());
146     Asserts.assertFalse(f2.hasNullMarker());
147   }
148 
149   static value class Value2 {
150     long l = 0;
151   }
152 
153   static class Container2a {
154     Value2 vala;
155   }
156 
157   static class Container2b extends Container2a {
158     Value2 valb;
159   }
160 
161   static public void test_2() {
162     Container2b c = new Container2b();
163   }
164 
165   static public void check_2(FieldLayoutAnalyzer fla) {
166     FieldLayoutAnalyzer.ClassLayout cla = fla.getClassLayoutFromName("NullMarkersTest$Container2a");
167     FieldLayoutAnalyzer.FieldBlock fa = cla.getFieldFromName("vala", false);
168     Asserts.assertTrue(fa.isFlat());
169     Asserts.assertTrue(fa.hasNullMarker());
170     FieldLayoutAnalyzer.ClassLayout clb = fla.getClassLayoutFromName("NullMarkersTest$Container2b");
171     FieldLayoutAnalyzer.FieldBlock fb = clb.getFieldFromName("valb", false);
172     Asserts.assertTrue(fb.isFlat());
173     Asserts.assertTrue(fb.hasNullMarker());
174   }
175 
176   static value class Value3 {
177     double d = 0.0d;
178   }
179 
180   static class Container3a {
181     @Strict
182     @NullRestricted
183     Value3 val0 = new Value3();
184     Value3 val1;
185   }
186 
187   static class Container3b extends Container3a {
188     Value3 val2;
189     @Strict
190     @NullRestricted
191     Value3 val3 = new Value3();
192   }
193 
194   static class Container3c extends Container3b {
195     Value3 val4;
196     Value3 val5;
197   }
198 
199   static public void test_3() {
200     Container3c c = new Container3c();
201   }
202 
203   static public void check_3(FieldLayoutAnalyzer fla) {
204     FieldLayoutAnalyzer.ClassLayout cla = fla.getClassLayoutFromName("NullMarkersTest$Container3a");
205     FieldLayoutAnalyzer.FieldBlock f0 = cla.getFieldFromName("val0", false);
206     Asserts.assertTrue(f0.isFlat());
207     Asserts.assertFalse(f0.hasNullMarker());
208     FieldLayoutAnalyzer.FieldBlock f1 = cla.getFieldFromName("val1", false);
209     Asserts.assertTrue(f1.isFlat());
210     Asserts.assertTrue(f1.hasNullMarker());
211     FieldLayoutAnalyzer.ClassLayout clb = fla.getClassLayoutFromName("NullMarkersTest$Container3b");
212     FieldLayoutAnalyzer.FieldBlock f2 = clb.getFieldFromName("val2", false);
213     Asserts.assertTrue(f2.isFlat());
214     Asserts.assertTrue(f2.hasNullMarker());
215     FieldLayoutAnalyzer.FieldBlock f3 = clb.getFieldFromName("val3", false);
216     Asserts.assertTrue(f3.isFlat());
217     Asserts.assertFalse(f3.hasNullMarker());
218     FieldLayoutAnalyzer.ClassLayout clc = fla.getClassLayoutFromName("NullMarkersTest$Container3c");
219     FieldLayoutAnalyzer.FieldBlock f4 = clc.getFieldFromName("val4", false);
220     Asserts.assertTrue(f4.isFlat());
221     Asserts.assertTrue(f4.hasNullMarker());
222     FieldLayoutAnalyzer.FieldBlock f5 = clc.getFieldFromName("val5", false);
223     Asserts.assertTrue(f5.isFlat());
224     Asserts.assertTrue(f5.hasNullMarker());
225   }
226 
227   @LooselyConsistentValue
228   static value class Value4 {
229     int i = 0;
230     byte b = 0;
231   }
232 
233   static class Container4 {
234     Value4 val0;
235     Value4 val1;
236   }
237 
238   static public void test_4() {
239     Container4 c = new Container4();
240   }
241 
242   static void check_4(FieldLayoutAnalyzer fla) {
243     FieldLayoutAnalyzer.ClassLayout cl = fla.getClassLayoutFromName("NullMarkersTest$Container4");
244     FieldLayoutAnalyzer.FieldBlock f0 = cl.getFieldFromName("val0", false);
245     Asserts.assertTrue(f0.isFlat());
246     Asserts.assertTrue(f0.hasNullMarker());
247     FieldLayoutAnalyzer.FieldBlock f1 = cl.getFieldFromName("val1", false);
248     Asserts.assertTrue(f1.isFlat());
249     Asserts.assertTrue(f1.hasNullMarker());
250   }
251 
252   @LooselyConsistentValue
253   static value class Value5a {
254     short s = 0;
255     byte b = 0;
256   }
257 
258   @LooselyConsistentValue
259   static value class Value5b {
260     @Strict
261     @NullRestricted
262     Value5a val0 = new Value5a();
263     @Strict
264     @NullRestricted
265     Value5a val1 = new Value5a();
266   }
267 
268   static class Container5 {
269     Value5a vala;
270     Value5b valb0;
271     Value5b valb1;
272   }
273 
274   static public void test_5() {
275     Container5 c = new Container5();
276   }
277 
278   static void check_5(FieldLayoutAnalyzer fla) {
279     FieldLayoutAnalyzer.ClassLayout cl = fla.getClassLayoutFromName("NullMarkersTest$Container5");
280     FieldLayoutAnalyzer.FieldBlock fa = cl.getFieldFromName("vala", false);
281     Asserts.assertTrue(fa.isFlat());
282     Asserts.assertTrue(fa.hasNullMarker());
283     FieldLayoutAnalyzer.FieldBlock fb0 = cl.getFieldFromName("valb0", false);
284     Asserts.assertTrue(fb0.isFlat());
285     Asserts.assertTrue(fb0.hasNullMarker());
286     FieldLayoutAnalyzer.FieldBlock fb1 = cl.getFieldFromName("valb1", false);
287     Asserts.assertTrue(fb1.isFlat());
288     Asserts.assertTrue(fb1.hasNullMarker());
289   }
290 
291   static ProcessBuilder exec(String compressedOopsArg, String compressedKlassPointersArg, String... args) throws Exception {
292     List<String> argsList = new ArrayList<>();
293     Collections.addAll(argsList, "--enable-preview");
294     Collections.addAll(argsList, "-XX:+UnlockDiagnosticVMOptions");
295     Collections.addAll(argsList, "-XX:+PrintFieldLayout");
296     Collections.addAll(argsList, "-Xshare:off");
297     if (compressedOopsArg != null) {
298       Collections.addAll(argsList, compressedOopsArg);
299     }
300     if (compressedKlassPointersArg != null) {
301       Collections.addAll(argsList, compressedKlassPointersArg);
302     }
303     Collections.addAll(argsList, "-Xmx256m");
304     Collections.addAll(argsList, "-XX:+UseNullableValueFlattening");
305     Collections.addAll(argsList, "-cp", System.getProperty("java.class.path") + System.getProperty("path.separator") + ".");
306     Collections.addAll(argsList, args);
307     return ProcessTools.createTestJavaProcessBuilder(argsList);
308   }
309 
310   public static void main(String[] args) throws Exception {
311     String compressedOopsArg;
312     String compressedKlassPointersArg;
313 
314     switch(args[0]) {
315       case "0": compressedOopsArg = null;
316                 compressedKlassPointersArg = null;
317                 break;
318       case "1": compressedOopsArg = "-XX:+UseCompressedOops";
319                 compressedKlassPointersArg =  "-XX:+UseCompressedClassPointers";
320                 break;
321       case "2": compressedOopsArg = "-XX:-UseCompressedOops";
322                 compressedKlassPointersArg = "-XX:+UseCompressedClassPointers";
323                 break;
324       case "3": compressedOopsArg = "-XX:-UseCompressedOops";
325                 compressedKlassPointersArg = "-XX:-UseCompressedClassPointers";
326                 break;
327       default: throw new RuntimeException("Unrecognized configuration");
328     }
329 
330     // Generate test classes
331     NullMarkersTest fat = new NullMarkersTest();
332 
333     // Execute the test runner in charge of loading all test classes
334     ProcessBuilder pb = exec(compressedOopsArg, compressedKlassPointersArg, "NullMarkersTest$TestRunner");
335     OutputAnalyzer out = new OutputAnalyzer(pb.start());
336 
337     if (out.getExitValue() != 0) {
338       System.out.print(out.getOutput());
339     }
340     Asserts.assertEquals(out.getExitValue(), 0, "Something went wrong while running the tests");
341 
342     // Get and parse the test output
343     FieldLayoutAnalyzer.LogOutput lo = new FieldLayoutAnalyzer.LogOutput(out.asLines());
344     FieldLayoutAnalyzer fla =  FieldLayoutAnalyzer.createFieldLayoutAnalyzer(lo);
345 
346     // Running tests verification method (check that tests produced the right configuration)
347     Class testClass = NullMarkersTest.class;
348       Method[] testMethods = testClass.getMethods();
349       for (Method test : testMethods) {
350         if (test.getName().startsWith("check_")) {
351           Asserts.assertTrue(Modifier.isStatic(test.getModifiers()));
352           Asserts.assertTrue(test.getReturnType().equals(Void.TYPE));
353           test.invoke(null, fla);
354         }
355       }
356 
357     // Verify that all layouts are correct
358     fla.check();
359   }
360 }