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:cds=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:cds=debug,cds+class=debug",
 87             "-cp", appJar);
 88         out = CDSTestUtils.executeAndLog(pb, "asm");
 89         out.shouldContain("Dumping shared data to file:");
 90         out.shouldMatch("cds,class.* TestApp");
 91         out.shouldNotMatch("cds,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:cds=debug,cds+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:cds,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 }