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 }