1 /*
  2  * Copyright (c) 2024, 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 /*
 25  * @test PreloadCircularityTest
 26  * @modules java.base/jdk.internal.vm.annotation
 27  *          java.base/jdk.internal.value
 28  * @library /test/lib
 29  * @requires vm.flagless
 30  * @enablePreview
 31  * @compile PreloadCircularityTest.java
 32  * @run main PreloadCircularityTest
 33  */
 34 
 35 import java.lang.reflect.Field;
 36 import java.lang.reflect.Method;
 37 import java.util.ArrayList;
 38 import java.util.Collections;
 39 import java.util.List;
 40 
 41 import javax.management.relation.RelationServiceNotRegisteredException;
 42 
 43 import jdk.internal.value.ValueClass;
 44 import jdk.internal.vm.annotation.LooselyConsistentValue;
 45 import jdk.internal.vm.annotation.NullRestricted;
 46 
 47 import jdk.test.lib.Asserts;
 48 import jdk.test.lib.Utils;
 49 import jdk.test.lib.process.OutputAnalyzer;
 50 import jdk.test.lib.process.ProcessTools;
 51 
 52 public class PreloadCircularityTest {
 53 
 54     // Testing preload due to non-static fields
 55 
 56     static value class Class0a {
 57         @NullRestricted
 58         Class0b vb = new Class0b();
 59     }
 60 
 61     static value class Class0b {
 62         @NullRestricted
 63         Class0c vc = new Class0c();
 64     }
 65 
 66     static value class Class0c {
 67         int i = 0;
 68     }
 69 
 70     void test_0() throws Exception {
 71         OutputAnalyzer out = tryLoadClassAndCheckNullRestricted("PreloadCircularityTest$Class0a",
 72                                                                 "PreloadCircularityTest$Class0b",
 73                                                                 "vc",
 74                                                                 true);
 75 
 76         out.shouldHaveExitValue(0);
 77         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class0b during loading of class PreloadCircularityTest$Class0a. Cause: field type in LoadableDescriptors attribute");
 78         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class0c during loading of class PreloadCircularityTest$Class0b. Cause: field type in LoadableDescriptors attribute");
 79         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class0c during loading of class PreloadCircularityTest$Class0b (cause: field type in LoadableDescriptors attribute) succeeded");
 80         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class0b during loading of class PreloadCircularityTest$Class0a (cause: field type in LoadableDescriptors attribute) succeeded");
 81         out.shouldNotContain("the annotation is ignored");
 82         out.shouldNotContain("failed");
 83     }
 84 
 85     static value class Class1a {
 86         @NullRestricted
 87         Class1b vb = new Class1b();
 88     }
 89 
 90     static value class Class1b {
 91         Class1c vc = new Class1c();
 92     }
 93 
 94     static value class Class1c {
 95         int i = 0;
 96     }
 97 
 98     void test_1() throws Exception {
 99         OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class1a");
100         out.shouldHaveExitValue(0);
101         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class1b during loading of class PreloadCircularityTest$Class1a. Cause: field type in LoadableDescriptors attribute");
102         out.shouldContain("Preloading of class PreloadCircularityTest$Class1c during loading of class PreloadCircularityTest$Class1b. Cause: field type in LoadableDescriptors attribute");
103         out.shouldContain("Preloading of class PreloadCircularityTest$Class1c during loading of class PreloadCircularityTest$Class1b (cause: field type in LoadableDescriptors attribute) succeeded");
104         out.shouldContain("Preloading of class PreloadCircularityTest$Class1b during loading of class PreloadCircularityTest$Class1a (cause: field type in LoadableDescriptors attribute) succeeded");
105         out.shouldNotContain("the annotation is ignored");
106         out.shouldNotContain("failed");
107     }
108 
109     static value class Class2a {
110         @NullRestricted
111         Class2b vb = new Class2b();
112     }
113 
114     static value class Class2b {
115         @NullRestricted
116         Class2c vc = new Class2c();
117     }
118 
119     static value class Class2c {
120         @NullRestricted
121         Class2b vb = new Class2b();
122     }
123 
124     void test_2() throws Exception {
125         OutputAnalyzer out = tryLoadClassAndCheckNullRestricted("PreloadCircularityTest$Class2a",
126                                                                 "PreloadCircularityTest$Class2c",
127                                                                 "vb",
128                                                                 false);
129         out.shouldHaveExitValue(0);
130         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class2b during loading of class PreloadCircularityTest$Class2a. Cause: field type in LoadableDescriptors attribute");
131         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class2c during loading of class PreloadCircularityTest$Class2b. Cause: field type in LoadableDescriptors attribute");
132         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class2b during loading of class PreloadCircularityTest$Class2c. Cause: field type in LoadableDescriptors attribute");
133         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class2b during loading of class PreloadCircularityTest$Class2c (cause: field type in LoadableDescriptors attribute) failed : java/lang/ClassCircularityError");
134         out.shouldContain("[warning][class,preload] After preloading of class PreloadCircularityTest$Class2b during loading of class PreloadCircularityTest$Class2c failed,field was annotated with @NullRestricted but class is unknown, the annotation is ignored");
135         out.shouldContain("[info   ][class,preload] Preloading of class PreloadCircularityTest$Class2c during loading of class PreloadCircularityTest$Class2b (cause: field type in LoadableDescriptors attribute) succeeded");
136         out.shouldContain("[info   ][class,preload] Preloading of class PreloadCircularityTest$Class2b during loading of class PreloadCircularityTest$Class2a (cause: field type in LoadableDescriptors attribute) succeeded");
137     }
138 
139     static value class Class3a {
140         @NullRestricted
141         Class3b vb = new Class3b();
142     }
143 
144     static value class Class3b {
145         @NullRestricted
146         Class3c vc = new Class3c();
147     }
148 
149     static value class Class3c {
150         Class3b vb = new Class3b();
151     }
152 
153     void test_3() throws Exception {
154         OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class3a");
155         out.shouldHaveExitValue(0);
156 
157         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class3b during loading of class PreloadCircularityTest$Class3a. Cause: field type in LoadableDescriptors attribute");
158         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class3c during loading of class PreloadCircularityTest$Class3b. Cause: field type in LoadableDescriptors attribute");
159         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class3b during loading of class PreloadCircularityTest$Class3c. Cause: field type in LoadableDescriptors attribute");
160         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class3b during loading of class PreloadCircularityTest$Class3c (cause: field type in LoadableDescriptors attribute) failed : java/lang/ClassCircularityError");
161         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class3c during loading of class PreloadCircularityTest$Class3b (cause: field type in LoadableDescriptors attribute) succeeded");
162         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class3b during loading of class PreloadCircularityTest$Class3a (cause: field type in LoadableDescriptors attribute) succeeded");
163         out.shouldNotContain("the annotation is ignored");
164     }
165 
166     static value class Class4a {
167         @NullRestricted
168         Class4b vb = new Class4b();
169     }
170 
171     static value class Class4b {
172         @NullRestricted
173         Class4a va = new Class4a();
174     }
175 
176     void test_4() throws Exception {
177         OutputAnalyzer out = tryLoadClassAndCheckNullRestricted("PreloadCircularityTest$Class4a",
178                                                                 "PreloadCircularityTest$Class4b",
179                                                                 "va",
180                                                                 false);
181         out.shouldHaveExitValue(0);
182 
183         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class4b during loading of class PreloadCircularityTest$Class4a. Cause: field type in LoadableDescriptors attribute");
184         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class4a during loading of class PreloadCircularityTest$Class4b. Cause: field type in LoadableDescriptors attribute");
185         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class4a during loading of class PreloadCircularityTest$Class4b (cause: field type in LoadableDescriptors attribute) failed : java/lang/ClassCircularityError");
186         out.shouldContain("[warning][class,preload] After preloading of class PreloadCircularityTest$Class4a during loading of class PreloadCircularityTest$Class4b failed,field was annotated with @NullRestricted but class is unknown, the annotation is ignored");
187         out.shouldContain("[info   ][class,preload] Preloading of class PreloadCircularityTest$Class4b during loading of class PreloadCircularityTest$Class4a (cause: field type in LoadableDescriptors attribute) succeeded");
188     }
189 
190     static value class Class5a {
191         Class5b vb = new Class5b();
192 
193         @NullRestricted
194         Class5c vc = new Class5c();
195     }
196 
197     static value class Class5b {
198         @NullRestricted
199         Class5d vd = new Class5d();
200     }
201 
202     static value class Class5c {
203         Class5b vb = new Class5b();
204     }
205 
206     static value class Class5d {
207         @NullRestricted
208         Class5b vb = new Class5b();
209     }
210 
211     void test_5() throws Exception {
212         OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class5a");
213         out.shouldHaveExitValue(0);
214         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class5b during loading of class PreloadCircularityTest$Class5a. Cause: field type in LoadableDescriptors attribute");
215         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class5d during loading of class PreloadCircularityTest$Class5b. Cause: field type in LoadableDescriptors attribute");
216         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class5b during loading of class PreloadCircularityTest$Class5d. Cause: field type in LoadableDescriptors attribute");
217         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class5b during loading of class PreloadCircularityTest$Class5d (cause: field type in LoadableDescriptors attribute) failed : java/lang/ClassCircularityError");
218         out.shouldContain("[warning][class,preload] After preloading of class PreloadCircularityTest$Class5b during loading of class PreloadCircularityTest$Class5d failed,field was annotated with @NullRestricted but class is unknown, the annotation is ignored");
219         out.shouldContain("[info   ][class,preload] Preloading of class PreloadCircularityTest$Class5d during loading of class PreloadCircularityTest$Class5b (cause: field type in LoadableDescriptors attribute) succeeded");
220         out.shouldContain("[info   ][class,preload] Preloading of class PreloadCircularityTest$Class5b during loading of class PreloadCircularityTest$Class5a (cause: field type in LoadableDescriptors attribute) succeeded");
221         out.shouldContain("[info   ][class,preload] Preloading of class PreloadCircularityTest$Class5c during loading of class PreloadCircularityTest$Class5a. Cause: field type in LoadableDescriptors attribute");
222         out.shouldContain("[info   ][class,preload] Preloading of class PreloadCircularityTest$Class5b during loading of class PreloadCircularityTest$Class5c. Cause: field type in LoadableDescriptors attribute");
223         out.shouldContain("[info   ][class,preload] Preloading of class PreloadCircularityTest$Class5b during loading of class PreloadCircularityTest$Class5c (cause: field type in LoadableDescriptors attribute) succeeded");
224         out.shouldContain("[info   ][class,preload] Preloading of class PreloadCircularityTest$Class5c during loading of class PreloadCircularityTest$Class5a (cause: field type in LoadableDescriptors attribute) succeeded");
225     }
226 
227     static value class Class6a {
228         @NullRestricted
229         Class6b vb = new Class6b();
230     }
231 
232     static value class Class6b {
233         Class6c vc = new Class6c();
234 
235         @NullRestricted
236         Class6d vd = new Class6d();
237     }
238 
239     static value class Class6c {
240         int i = 0;
241     }
242 
243     static value class Class6d {
244         @NullRestricted
245         Class6b vb = new Class6b();
246     }
247 
248     void test_6() throws Exception {
249         OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class6a");
250         out.shouldHaveExitValue(0);
251 
252         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class6b during loading of class PreloadCircularityTest$Class6a. Cause: field type in LoadableDescriptors attribute");
253         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class6c during loading of class PreloadCircularityTest$Class6b. Cause: field type in LoadableDescriptors attribute");
254         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class6c during loading of class PreloadCircularityTest$Class6b (cause: field type in LoadableDescriptors attribute) succeeded");
255         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class6d during loading of class PreloadCircularityTest$Class6b. Cause: field type in LoadableDescriptors attribute");
256         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class6b during loading of class PreloadCircularityTest$Class6d. Cause: field type in LoadableDescriptors attribute");
257         out.shouldContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class6b during loading of class PreloadCircularityTest$Class6d (cause: field type in LoadableDescriptors attribute) failed : java/lang/ClassCircularityError");
258         out.shouldContain("[warning][class,preload] After preloading of class PreloadCircularityTest$Class6b during loading of class PreloadCircularityTest$Class6d failed,field was annotated with @NullRestricted but class is unknown, the annotation is ignored");
259         out.shouldContain("[info   ][class,preload] Preloading of class PreloadCircularityTest$Class6d during loading of class PreloadCircularityTest$Class6b (cause: field type in LoadableDescriptors attribute) succeeded");
260         out.shouldContain("[info   ][class,preload] Preloading of class PreloadCircularityTest$Class6b during loading of class PreloadCircularityTest$Class6a (cause: field type in LoadableDescriptors attribute) succeeded");
261     }
262 
263     static value class Class7a {
264         @NullRestricted
265         Class7a va = new Class7a();
266     }
267 
268     void test_7() throws Exception {
269         OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class7a");
270         out.shouldHaveExitValue(0);
271         out.shouldNotContain("[warning][class,preload] During loading of class PreloadCircularityTest$Class7a, class PreloadCircularityTest$Class7a is unknown, but a field of this type was annotated with @NullRestricted, the annotation is ignored");
272     }
273 
274     static value class Class8a {
275         Class8a va = new Class8a();
276     }
277 
278     void test_8() throws Exception {
279         OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class8a");
280         out.shouldHaveExitValue(0);
281         out.shouldNotContain("[info][class,preload] Preloading of class PreloadCircularityTest$Class8a during loading of class PreloadCircularityTest$Class8a. Cause: field type in LoadableDescriptors attribute");
282     }
283 
284     static value class Class9a {
285         @NullRestricted
286         Class9b vb = new Class9b();
287     }
288 
289     static class Class9b { }
290 
291     void test_9() throws Exception {
292         OutputAnalyzer out = tryLoadingClass("PreloadCircularityTest$Class9a");
293         out.shouldHaveExitValue(0);
294         out.shouldContain("[warning][class,preload] During loading of class PreloadCircularityTest$Class9a, class PreloadCircularityTest$Class9b is unknown, but a field of this type was annotated with @NullRestricted, the annotation is ignored");
295     }
296 
297     public static class TestHelper {
298         public static void main(String[] args) {
299             try {
300                 switch (args[0]) {
301                     case "load":
302                         Class.forName(args[1]);
303                         break;
304                     case "isNullRestricted":
305                         Class.forName(args[1]);
306 
307                         Class fieldClass = Class.forName(args[2]);
308                         Field field = fieldClass.getDeclaredField(args[3]);
309                         Boolean isNullRestricted = Boolean.parseBoolean(args[4]);
310                         Asserts.assertEquals(ValueClass.isNullRestrictedField(field), isNullRestricted);
311                         break;
312                     default:
313                         throw new RuntimeException("Unknown mode: " + args[0]);
314               }
315             } catch (Throwable ex) {
316                 ex.printStackTrace();
317                 System.exit(1);
318             }
319             System.exit(0);
320         }
321     }
322 
323     static OutputAnalyzer tryLoadingClass(String className) throws Exception {
324         ProcessBuilder pb = exec("PreloadCircularityTest$TestHelper",
325                                  "load",
326                                  className);
327 
328         OutputAnalyzer out = new OutputAnalyzer(pb.start());
329         System.out.println(out.getOutput());
330         return out;
331     }
332 
333     static OutputAnalyzer tryLoadClassAndCheckNullRestricted(String loadClass, String fieldClass, String field, boolean isNullRestricted) throws Exception {
334         ProcessBuilder pb = exec("PreloadCircularityTest$TestHelper",
335                                  "isNullRestricted",
336                                  loadClass,
337                                  fieldClass,
338                                  field,
339                                  Boolean.toString(isNullRestricted));
340 
341         OutputAnalyzer out = new OutputAnalyzer(pb.start());
342         System.out.println(out.getOutput());
343         return out;
344     }
345 
346     static ProcessBuilder exec(String... args) throws Exception {
347         List<String> argsList = new ArrayList<>();
348         Collections.addAll(argsList, "--enable-preview");
349         Collections.addAll(argsList, "--add-exports", "java.base/jdk.internal.value=ALL-UNNAMED");
350         Collections.addAll(argsList, "-Dtest.class.path=" + System.getProperty("test.class.path", "."));
351         Collections.addAll(argsList, "-Xlog:class+preload=info");
352         Collections.addAll(argsList, args);
353         return ProcessTools.createTestJavaProcessBuilder(argsList);
354     }
355 
356     public static void main(String[] args) throws Exception {
357         System.out.println("Creating tests");
358         PreloadCircularityTest tests = new PreloadCircularityTest();
359         Class c = tests.getClass();
360         System.out.println("Iterating over test methods");
361         boolean hasFailedTest = false;
362         StringBuilder sb = new StringBuilder("Following tests have failed: ");
363         for (Method m : c.getDeclaredMethods()) {
364             if (m.getName().startsWith("test_")) {
365                 boolean failed = false;
366                 try {
367                     System.out.println("Running " + m.getName());
368                     m.invoke(tests);
369                 } catch (Throwable t) {
370                     t.printStackTrace();
371                     failed = true;
372                 }
373                 System.out.println("Test " + m.getName() + " : " + (failed ? "FAILED" : "PASSED"));
374                 hasFailedTest = failed ? true : hasFailedTest;
375                 if (failed) sb.append(m.getName()).append(", ");
376             }
377         }
378         if (hasFailedTest) {
379             throw new RuntimeException(sb.toString());
380         }
381     }
382 }