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