1 /*
2 * Copyright (c) 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
27 * @summary interation between static archive and dynamic archive related to
28 * regenerated lambda form invoker classes.
29 * @requires vm.cds
30 * @requires vm.cds.supports.aot.class.linking
31 * @library /test/lib
32 * @build RegeneratedClassesInDynamicArchive
33 * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar TestApp CachedInDynamic MyInterface
34 * @run driver RegeneratedClassesInDynamicArchive
35 */
36
37 import java.util.List;
38 import java.util.stream.Collectors;
39
40 import jdk.test.lib.cds.CDSTestUtils;
41 import jdk.test.lib.helpers.ClassFileInstaller;
42 import jdk.test.lib.process.OutputAnalyzer;
43 import jdk.test.lib.process.ProcessTools;
44
45 public class RegeneratedClassesInDynamicArchive {
46 static String appJar = ClassFileInstaller.getJarPath("app.jar");
47 static String aotConfigFile = "app.aotconfig";
48 static String aotCacheFile = "app.aot";
49 static String appClass = TestApp.class.getName();
50 static String dynamicArchive = "dynamic.jsa";
51 static String helloMsg = "hello, world";
52 static String outputWithDynamicArchive = "Hello 1 in 20.0";
53
54 public static void main(String[] args) throws Exception {
55 //----------------------------------------------------------------------
56 printTestCase("Training Run For Base Archive");
57 ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
58 "-XX:AOTMode=record",
59 "-XX:AOTConfiguration=" + aotConfigFile,
60 "-Xlog:aot=debug",
61 "-cp", appJar, appClass, "base");
62
63 OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "train");
64 out.shouldContain("AOTConfiguration recorded: " + aotConfigFile);
65 out.shouldContain(helloMsg);
66 out.shouldNotContain(outputWithDynamicArchive);
67 out.shouldHaveExitValue(0);
68
69 test(false);
70 test(true);
71 }
72
73 static void test(boolean aotClassLinkingForBaseArchive) throws Exception {
74 ProcessBuilder pb;
75 OutputAnalyzer out;
76 String usingAOTClassesMsg = "Using AOT-linked classes: true (static archive: has aot-linked classes)";
77 String regenerateMsg = "Regenerate MethodHandle Holder classes...done";
78
79 //----------------------------------------------------------------------
80 printTestCase("Assembly Phase");
81 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
82 "-XX:AOTMode=create",
83 "-XX:AOTConfiguration=" + aotConfigFile,
84 "-XX:AOTCache=" + aotCacheFile,
85 "-XX:" + (aotClassLinkingForBaseArchive ? "+" : "-") + "AOTClassLinking",
86 "-Xlog:aot=debug,aot+class=debug",
87 "-cp", appJar);
88 out = CDSTestUtils.executeAndLog(pb, "asm");
89 out.shouldContain("AOTCache creation is complete");
90 out.shouldMatch("aot,class.* TestApp");
91 out.shouldNotMatch("aot,class.* CachedInDynamic");
92 out.shouldHaveExitValue(0);
93
94 //----------------------------------------------------------------------
95 printTestCase("Production Run with AOTCache, to Generate " + dynamicArchive);
96 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
97 "-XX:AOTCache=" + aotCacheFile,
98 "-Xlog:aot=debug,aot+class=debug,class+load",
99 "-XX:ArchiveClassesAtExit=" + dynamicArchive,
100 "-cp", appJar, appClass, "top");
101 out = CDSTestUtils.executeAndLog(pb, "prod");
102 if (aotClassLinkingForBaseArchive) {
103 out.shouldContain(usingAOTClassesMsg);
104 out.shouldNotContain(regenerateMsg);
105 } else {
106 out.shouldNotContain(usingAOTClassesMsg);
107 out.shouldContain(regenerateMsg);
108 }
109 out.shouldContain("Opened AOT cache app.aot.");
110 out.shouldContain(helloMsg);
111 out.shouldContain(outputWithDynamicArchive);
112 out.shouldHaveExitValue(0);
113
114 //----------------------------------------------------------------------
115 printTestCase("Production Run with " + dynamicArchive);
116 pb = ProcessTools.createLimitedTestJavaProcessBuilder(
117 "-XX:SharedArchiveFile=" + dynamicArchive,
118 "-Xlog:aot,class+load",
119 "-cp", appJar, appClass, "top");
120 out = CDSTestUtils.executeAndLog(pb, "prod");
121 out.shouldContain(helloMsg);
122 out.shouldContain(outputWithDynamicArchive);
123 out.shouldContain("CachedInDynamic source: shared objects file (top)");
124 out.shouldHaveExitValue(0);
125 }
126
127 static int testNum = 0;
128 static void printTestCase(String s) {
129 System.out.println("vvvvvvv TEST CASE " + testNum + ": " + s + ": starts here vvvvvvv");
130 testNum++;
131 }
132 }
133
134 class TestApp {
135 public static void main(String args[]) {
136 // Run a few lambdas -- if -XX:+AOTClassLinking is enabled,
137 // these will be cached. with AOT-resolved references to the lambda form invoker
138 // classes. These references should still work when dynamic.jsa is loaded.
139 var words = List.of("hello", "fuzzy", "world");
140 var greeting = words.stream()
141 .filter(w -> !w.contains("z"))
142 .collect(Collectors.joining(", "));
143 System.out.println(greeting); // hello, world
144 if (args[0].equals("top")) {
145 CachedInDynamic.func();
146 }
147 }
148 }
149
150 interface MyInterface {
151 public Object doit(Object a, long b, Object c, int d, double e);
152
153
154 }
155
156 class CachedInDynamic {
157 static void func() {
158 System.out.println("CachedInDynamic.func(): start");
159 MyInterface m = (a, b, c, d, e) -> {
160 return "" + a + b + c + d + e;
161 };
162 System.out.println(m.doit("Hello ", 1, " in ", 2, 0.0));
163 System.out.println("CachedInDynamic.func(): done");
164 }
165 }