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 /*
26 * @test id=static
27 * @summary Dump time resolution of constant pool entries (Static CDS archive).
28 * @requires vm.cds
29 * @requires vm.cds.supports.aot.class.linking
30 * @requires vm.compMode != "Xcomp"
31 * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes/
32 * @build OldProvider OldClass OldConsumer StringConcatTestOld
33 * @build ResolvedConstants
34 * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
35 * ResolvedConstantsApp ResolvedConstantsFoo ResolvedConstantsBar
36 * MyInterface InterfaceWithClinit NormalClass
37 * OldProvider OldClass OldConsumer SubOfOldClass
38 * StringConcatTest StringConcatTestOld
39 * @run driver ResolvedConstants STATIC
40 */
41
42 /*
43 * @test id=dynamic
44 * @summary Dump time resolution of constant pool entries (Dynamic CDS archive)
45 * @requires vm.cds
46 * @requires vm.cds.supports.aot.class.linking
47 * @requires vm.compMode != "Xcomp"
48 * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes/
49 * @build OldProvider OldClass OldConsumer StringConcatTestOld
50 * @build ResolvedConstants
51 * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
52 * ResolvedConstantsApp ResolvedConstantsFoo ResolvedConstantsBar
53 * MyInterface InterfaceWithClinit NormalClass
54 * OldProvider OldClass OldConsumer SubOfOldClass
55 * StringConcatTest StringConcatTestOld
56 * @build jdk.test.whitebox.WhiteBox
57 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
58 * @run main/othervm -Dcds.app.tester.workflow=DYNAMIC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. ResolvedConstants DYNAMIC
59 */
60
61 /*
62 * @test id=aot
63 * @summary Dump time resolution of constant pool entries (AOT workflow).
64 * @requires vm.cds
65 * @requires vm.cds.supports.aot.class.linking
66 * @requires vm.compMode != "Xcomp"
67 * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes/
68 * @build OldProvider OldClass OldConsumer StringConcatTestOld
69 * @build ResolvedConstants
70 * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
71 * ResolvedConstantsApp ResolvedConstantsFoo ResolvedConstantsBar
72 * MyInterface InterfaceWithClinit NormalClass
73 * OldProvider OldClass OldConsumer SubOfOldClass
74 * StringConcatTest StringConcatTestOld
75 * @run driver ResolvedConstants AOT --two-step-training
76 */
77 import java.util.function.Consumer;
78 import jdk.test.lib.cds.CDSOptions;
79 import jdk.test.lib.cds.CDSTestUtils;
80 import jdk.test.lib.cds.SimpleCDSAppTester;
81 import jdk.test.lib.helpers.ClassFileInstaller;
82 import jdk.test.lib.process.OutputAnalyzer;
83
84 public class ResolvedConstants {
85 static final String classList = "ResolvedConstants.classlist";
86 static final String appJar = ClassFileInstaller.getJarPath("app.jar");
87 static final String mainClass = ResolvedConstantsApp.class.getName();
88
89 static boolean aotClassLinking;
90 public static void main(String[] args) throws Exception {
91 test(args, false);
92 if (!args[0].equals("DYNAMIC")) {
93 test(args, true);
94 }
95 }
96
97 static void test(String[] args, boolean testMode) throws Exception {
98 aotClassLinking = testMode;
99
100 SimpleCDSAppTester.of("ResolvedConstantsApp" + (aotClassLinking ? "1" : "0"))
101 .addVmArgs(aotClassLinking ? "-XX:+AOTClassLinking" : "-XX:-AOTClassLinking",
102 "-Xlog:aot+resolve=trace",
103 "-Xlog:aot+class=debug",
104 "-Xlog:cds+class=debug")
105 .classpath(appJar)
106 .appCommandLine(mainClass)
107 .setAssemblyChecker((OutputAnalyzer out) -> {
108 checkAssemblyOutput(args, out);
109 })
110 .setProductionChecker((OutputAnalyzer out) -> {
111 out.shouldContain("Hello ResolvedConstantsApp");
112 })
113 .run(args);
114 }
115
116 static void checkAssemblyOutput(String args[], OutputAnalyzer out) {
117 testGroup("Class References", out)
118 // Always resolve reference when a class references itself
119 .shouldMatch(ALWAYS("klass.* ResolvedConstantsApp app => ResolvedConstantsApp app"))
120
121 // Always resolve reference when a class references a super class
122 .shouldMatch(ALWAYS("klass.* ResolvedConstantsApp app => java/lang/Object boot"))
123 .shouldMatch(ALWAYS("klass.* ResolvedConstantsBar app => ResolvedConstantsFoo app"))
124
125 // Always resolve reference when a class references a super interface
126 .shouldMatch(ALWAYS("klass.* ResolvedConstantsApp app => java/lang/Runnable boot"))
127
128 // Without -XX:+AOTClassLinking:
129 // java/lang/System is in the boot loader but ResolvedConstantsApp is loaded by the app loader.
130 // Even though System is in the vmClasses list, when ResolvedConstantsApp looks up
131 // "java/lang/System" in its ConstantPool, the app loader may not have resolved the System
132 // class yet (i.e., there's no initiaited class entry for System in the app loader's dictionary)
133 .shouldMatch(AOTLINK_ONLY("klass.* ResolvedConstantsApp .*java/lang/System"));
134
135 testGroup("Field References", out)
136 // Always resolve references to fields in the current class or super class(es)
137 .shouldMatch(ALWAYS("field.* ResolvedConstantsBar => ResolvedConstantsBar.b:I"))
138 .shouldMatch(ALWAYS("field.* ResolvedConstantsBar => ResolvedConstantsBar.a:I"))
139 .shouldMatch(ALWAYS("field.* ResolvedConstantsBar => ResolvedConstantsFoo.a:I"))
140 .shouldMatch(ALWAYS("field.* ResolvedConstantsFoo => ResolvedConstantsFoo.a:I"))
141 .shouldMatch(ALWAYS("field.* ResolvedConstantsApp => ResolvedConstantsApp.static_i:I"))
142
143 // Resolve field references to child classes ONLY when using -XX:+AOTClassLinking
144 .shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsFoo => ResolvedConstantsBar.static_b:I"))
145 .shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsFoo => ResolvedConstantsBar.a:I"))
146 .shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsFoo => ResolvedConstantsBar.b:I"))
147
148 // Resolve field references to unrelated classes ONLY when using -XX:+AOTClassLinking
149 .shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsApp => ResolvedConstantsBar.static_b:I"))
150 .shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsApp => ResolvedConstantsBar.a:I"))
151 .shouldMatch(AOTLINK_ONLY("field.* ResolvedConstantsApp => ResolvedConstantsBar.b:I"));
152
153 if (args[0].equals("DYNAMIC")) {
154 // AOT resolution of CP methods/indy references is not implemeted
155 return;
156 }
157
158 testGroup("Method References", out)
159 // Should resolve references to own constructor
160 .shouldMatch(ALWAYS("method.* ResolvedConstantsApp ResolvedConstantsApp.<init>:"))
161 // Should resolve references to super constructor
162 .shouldMatch(ALWAYS("method.* ResolvedConstantsApp java/lang/Object.<init>:"))
163
164 // Should resolve interface methods in VM classes
165 .shouldMatch(ALWAYS("interface method .* ResolvedConstantsApp java/lang/Runnable.run:"))
166
167 // Should resolve references to own non-static method (private or public)
168 .shouldMatch(ALWAYS("method.*: ResolvedConstantsBar ResolvedConstantsBar.doBar:"))
169 .shouldMatch(ALWAYS("method.*: ResolvedConstantsApp ResolvedConstantsApp.privateInstanceCall:"))
170 .shouldMatch(ALWAYS("method.*: ResolvedConstantsApp ResolvedConstantsApp.publicInstanceCall:"))
171
172 // Should resolve references to static method
173 .shouldMatch(ALWAYS("method.*: ResolvedConstantsApp ResolvedConstantsApp.staticCall:"))
174
175 // Should resolve references to method in super type
176 .shouldMatch(ALWAYS("method.*: ResolvedConstantsBar ResolvedConstantsFoo.doBar:"))
177
178 // Without -XX:+AOTClassLinking App class cannot resolve references to methods in boot classes:
179 // When the app class loader tries to resolve a class X that's normally loaded by
180 // the boot loader, it's possible for the app class loader to get a different copy of
181 // X (by using MethodHandles.Lookup.defineClass(), etc). Therefore, let's be on
182 // the side of safety and revert all such references.
183 .shouldMatch(AOTLINK_ONLY("method.*: ResolvedConstantsApp java/io/PrintStream.println:"))
184 .shouldMatch(AOTLINK_ONLY("method.*: ResolvedConstantsBar java/lang/Class.getName:"))
185
186 // Resolve method references to child classes ONLY when using -XX:+AOTClassLinking
187 .shouldMatch(AOTLINK_ONLY("method.* ResolvedConstantsFoo ResolvedConstantsBar.static_doit"))
188 .shouldMatch(AOTLINK_ONLY("method.* ResolvedConstantsFoo ResolvedConstantsBar.doit2"))
189
190 // Resolve methods in unrelated classes ONLY when using -XX:+AOTClassLinking
191 .shouldMatch(AOTLINK_ONLY("method.*: ResolvedConstantsApp ResolvedConstantsBar.static_doit:"))
192 .shouldMatch(AOTLINK_ONLY("method.*: ResolvedConstantsApp ResolvedConstantsBar.doit:"))
193
194 // End ---
195 ;
196
197
198 // Indy References ---
199 if (aotClassLinking) {
200 testGroup("Indy References", out)
201 .shouldContain("Cannot aot-resolve Lambda proxy of interface type InterfaceWithClinit")
202 .shouldMatch("klasses.* app *NormalClass[$][$]Lambda/.* hidden aot-linked inited")
203 .shouldMatch("archived indy *CP entry.*StringConcatTest .* => java/lang/invoke/StringConcatFactory.makeConcatWithConstants");
204 }
205 }
206
207 static String ALWAYS(String s) {
208 return ",resolve.*archived " + s;
209 }
210
211 static String AOTLINK_ONLY(String s) {
212 if (aotClassLinking) {
213 return ALWAYS(s);
214 } else {
215 return ",resolve.*reverted " + s;
216 }
217 }
218
219 static OutputAnalyzer testGroup(String name, OutputAnalyzer out) {
220 System.out.println("Checking for: " + name);
221 return out;
222 }
223 }
224
225 class ResolvedConstantsApp implements Runnable {
226 public static void main(String args[]) {
227 System.out.println("Hello ResolvedConstantsApp");
228 ResolvedConstantsApp app = new ResolvedConstantsApp();
229 ResolvedConstantsApp.staticCall();
230 ResolvedConstantsApp.static_i ++;
231 app.privateInstanceCall();
232 app.publicInstanceCall();
233 Object a = app;
234 ((Runnable)a).run();
235
236 ResolvedConstantsBar.static_b += 10;
237 ResolvedConstantsBar.static_doit();
238 ResolvedConstantsFoo foo = new ResolvedConstantsFoo();
239 ResolvedConstantsBar bar = new ResolvedConstantsBar();
240 bar.a ++;
241 bar.b ++;
242 bar.doit();
243
244 testLambda();
245 StringConcatTest.test();
246 StringConcatTestOld.main(null);
247 }
248 private static int static_i = 10;
249 private static void staticCall() {}
250 private void privateInstanceCall() {}
251 public void publicInstanceCall() {}
252
253 public void run() {}
254
255 static void testLambda() {
256 // The functional type used in the Lambda is an excluded class
257 OldProvider op = () -> {
258 return null;
259 };
260
261 // A captured value is an instance of an excluded Class
262 OldClass c = new OldClass();
263 Runnable r = () -> {
264 System.out.println("Test 1 " + c);
265 };
266 r.run();
267
268 // The functional interface accepts an argument that's an excluded class
269 MyInterface i = (o) -> {
270 System.out.println("Test 2 " + o);
271 };
272 i.dispatch(c);
273
274 // Method reference to old class
275 OldConsumer oldConsumer = new OldConsumer();
276 Consumer<String> wrapper = oldConsumer::consumeString;
277 wrapper.accept("Hello");
278
279 // Lambda of interfaces that have <clinit> are not archived.
280 InterfaceWithClinit i2 = () -> {
281 System.out.println("Test 3");
282 };
283 i2.dispatch();
284
285 // These two classes have almost identical source code, but
286 // only NormalClass should have its lambdas pre-resolved.
287 // SubOfOldClass is "old" -- it should be excluded from the AOT cache,
288 // so none of its lambda proxies should be cached
289 NormalClass.testLambda(); // Lambda proxy should be cached
290 SubOfOldClass.testLambda(); // Lambda proxy shouldn't be cached
291 }
292 }
293
294 class StringConcatTest {
295 static void test() {
296 System.out.println("StringConcatTest <concat> " + new StringConcatTest()); // concat should be aot-resolved
297 }
298 }
299
300 /* see StringConcatTestOld.jasm
301
302 class StringConcatTestOld {
303 public static void main(String args[]) {
304 // concat should be aot-resolved => the MethodType refers to an old class
305 System.out.println("StringConcatTestOld <concat> " + new OldConsumer());
306 }
307 }
308 */
309
310 class NormalClass {
311 static void testLambda() {
312 Runnable r = () -> {
313 System.out.println("NormalClass testLambda");
314 };
315 r.run();
316 }
317 }
318
319 class SubOfOldClass extends OldClass {
320 static void testLambda() {
321 Runnable r = () -> {
322 System.out.println("SubOfOldClass testLambda");
323 };
324 r.run();
325 }
326 }
327
328 interface MyInterface {
329 void dispatch(OldClass c);
330 }
331
332 interface InterfaceWithClinit {
333 static final long X = System.currentTimeMillis();
334 void dispatch();
335 default long dummy() { return X; }
336 }
337
338 class ResolvedConstantsFoo {
339 int a = 1;
340 void doit() {
341 }
342
343 void doBar(ResolvedConstantsBar bar) {
344 ResolvedConstantsBar.static_b += 1;
345 ResolvedConstantsBar.static_doit();
346
347 bar.a ++;
348 bar.b ++;
349 bar.doit2();
350 }
351 }
352
353 class ResolvedConstantsBar extends ResolvedConstantsFoo {
354 public static int static_b = 10;
355 int b = 2;
356 public static void static_doit() {
357 }
358
359 void doit() {
360 System.out.println("Hello ResolvedConstantsBar and " + ResolvedConstantsFoo.class.getName());
361 System.out.println("a = " + a);
362 System.out.println("a = " + ((ResolvedConstantsFoo)this).a);
363 System.out.println("b = " + b);
364
365 doBar(this);
366
367 ((ResolvedConstantsFoo)this).doBar(this);
368 }
369
370 void doit2() {
371
372 }
373 }