1 /*
2 * Copyright (c) 2023, 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 Test how various AOT optimizations handle classes that are excluded from the AOT cache.
28 * @requires vm.cds.write.archived.java.heap
29 * @library /test/jdk/lib/testlibrary /test/lib
30 * /test/hotspot/jtreg/runtime/cds/appcds/aotCache/test-classes
31 * @build ExcludedClasses CustyWithLoop
32 * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
33 * TestApp
34 * TestApp$Foo
35 * TestApp$Foo$Bar
36 * TestApp$Foo$ShouldBeExcluded
37 * TestApp$Foo$ShouldBeExcludedChild
38 * TestApp$Foo$Taz
39 * TestApp$MyInvocationHandler
40 * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar
41 * CustyWithLoop
42 * @run driver ExcludedClasses
43 */
44
45 import java.io.File;
46 import java.lang.reflect.Array;
47 import java.lang.reflect.InvocationHandler;
48 import java.lang.reflect.Method;
49 import java.lang.reflect.Proxy;
50 import java.net.URL;
51 import java.net.URLClassLoader;
52 import java.security.ProtectionDomain;
53 import java.util.Map;
54
55 import jdk.jfr.Event;
56 import jdk.test.lib.cds.CDSAppTester;
57 import jdk.test.lib.helpers.ClassFileInstaller;
58 import jdk.test.lib.process.OutputAnalyzer;
59
60 public class ExcludedClasses {
61 static final String appJar = ClassFileInstaller.getJarPath("app.jar");
62 static final String mainClass = "TestApp";
63
64 public static void main(String[] args) throws Exception {
65 Tester tester = new Tester();
66 tester.runAOTWorkflow("AOT", "--two-step-training");
67 }
68
69 static class Tester extends CDSAppTester {
70 public Tester() {
71 super(mainClass);
72 }
73
74 @Override
75 public String classpath(RunMode runMode) {
76 return appJar;
77 }
78
79 @Override
80 public String[] vmArgs(RunMode runMode) {
81 return new String[] {
82 "-Xlog:aot=debug",
83 "-Xlog:aot+class=debug",
84 "-Xlog:aot+resolve=trace",
85 "-Xlog:aot+verification=trace",
86 "-Xlog:class+load",
87 };
88 }
89
90 @Override
91 public String[] appCommandLine(RunMode runMode) {
92 return new String[] {
93 mainClass, runMode.name()
94 };
95 }
96
97 @Override
98 public void checkExecution(OutputAnalyzer out, RunMode runMode) {
99 if (runMode == RunMode.ASSEMBLY) {
100 out.shouldNotMatch("aot,resolve.*archived field.*TestApp.Foo => TestApp.Foo.ShouldBeExcluded.f:I");
101 } else if (runMode == RunMode.PRODUCTION) {
102 out.shouldContain("jdk.jfr.Event source: jrt:/jdk.jfr");
103 out.shouldMatch("TestApp[$]Foo[$]ShouldBeExcluded source: .*/app.jar");
104 out.shouldMatch("TestApp[$]Foo[$]ShouldBeExcludedChild source: .*/app.jar");
105 }
106 }
107 }
108 }
109
110 class TestApp {
111 static volatile Object custInstance;
112 static volatile Object custArrayInstance;
113
114 public static void main(String args[]) throws Exception {
115 // In AOT workflow, classes from custom loaders are passed from the preimage
116 // to the final image. See FinalImageRecipes::record_all_classes().
117 custInstance = initFromCustomLoader();
118 custArrayInstance = Array.newInstance(custInstance.getClass(), 0);
119 System.out.println(custArrayInstance);
120 System.out.println("Counter = " + Foo.hotSpot());
121 }
122
123 static Object initFromCustomLoader() throws Exception {
124 String path = "cust.jar";
125 URL url = new File(path).toURI().toURL();
126 URL[] urls = new URL[] {url};
127 URLClassLoader urlClassLoader =
128 new URLClassLoader("MyLoader", urls, null);
129 Class c = Class.forName("CustyWithLoop", true, urlClassLoader);
130 return c.newInstance();
131 }
132
133 static class MyInvocationHandler implements InvocationHandler {
134 volatile static int cnt;
135
136 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
137 long start = System.currentTimeMillis();
138 while (System.currentTimeMillis() - start < 20) {
139 cnt += 2;
140 for (int i = 0; i < 1000; i++) {
141 int n = cnt - 2;
142 if (n < 2) {
143 n = 2;
144 }
145 cnt += (i + cnt) % n + cnt % 2;
146 }
147 }
148 return Integer.valueOf(cnt);
149 }
150 }
151
152 static class Foo {
153 volatile static int counter;
154 static Class c = ShouldBeExcluded.class;
155
156 static Map mapProxy = (Map) Proxy.newProxyInstance(
157 Foo.class.getClassLoader(),
158 new Class[] { Map.class },
159 new MyInvocationHandler());
160
161 static int hotSpot() {
162 ShouldBeExcluded s = new ShouldBeExcluded();
163 Bar b = new Bar();
164
165 long start = System.currentTimeMillis();
166 while (System.currentTimeMillis() - start < 1000) {
167 lambdaHotSpot();
168 s.hotSpot2();
169 b.hotSpot3();
170 Taz.hotSpot4();
171
172 // In JDK mainline, generated proxy classes are excluded from the AOT cache.
173 // In Leyden/premain, generated proxy classes included. The following code should
174 // work with either repos.
175 Integer i = (Integer)mapProxy.get(null);
176 counter += i.intValue();
177
178 if (custInstance != null) {
179 // Classes loaded by custom loaders are included in the AOT cache
180 // but their array classes are excluded.
181 counter += custInstance.equals(null) ? 1 : 2;
182 }
183
184 if (custArrayInstance != null) {
185 if ((counter % 3) == 0) {
186 counter += (custArrayInstance instanceof String) ? 0 : 1;
187 } else {
188 counter += (custArrayInstance instanceof Object) ? 0 : 1;
189 }
190 }
191 }
192
193 return counter + s.m() + s.f + b.m() + b.f;
194 }
195
196 static void f() {
197 if (counter % 2 == 1) {
198 counter ++;
199 }
200 }
201
202 // Generated Lambda classes should be excluded from CDS preimage.
203 static void lambdaHotSpot() {
204 long start = System.currentTimeMillis();
205 while (System.currentTimeMillis() - start < 20) {
206 doit(() -> counter ++ );
207 }
208 }
209
210 static void doit(Runnable r) {
211 r.run();
212 }
213
214 // All subclasses of jdk.jfr.Event are excluded from the CDS archive.
215 static class ShouldBeExcluded extends jdk.jfr.Event {
216 int f = (int)(System.currentTimeMillis()) + 123;
217 int m() {
218 return f + 456;
219 }
220
221 void hotSpot2() {
222 long start = System.currentTimeMillis();
223 while (System.currentTimeMillis() - start < 20) {
224 for (int i = 0; i < 50000; i++) {
225 counter += i;
226 }
227 f();
228 }
229 }
230 int func() {
231 return 1;
232 }
233 }
234
235 static class ShouldBeExcludedChild extends ShouldBeExcluded {
236 @Override
237 int func() {
238 return 2;
239 }
240 }
241
242 static class Bar {
243 int f = (int)(System.currentTimeMillis()) + 123;
244 int m() {
245 return f + 456;
246 }
247
248 void hotSpot3() {
249 long start = System.currentTimeMillis();
250 while (System.currentTimeMillis() - start < 20) {
251 for (int i = 0; i < 50000; i++) {
252 counter += i;
253 }
254 f();
255 }
256 }
257 }
258
259 static class Taz {
260 static ShouldBeExcluded m() {
261 // Taz should be excluded from the AOT cache because it has a verification constraint that
262 // "ShouldBeExcludedChild must be a subtype of ShouldBeExcluded", but ShouldBeExcluded is
263 // excluded from the AOT cache.
264 return new ShouldBeExcludedChild();
265 }
266 static void hotSpot4() {
267 long start = System.currentTimeMillis();
268 while (System.currentTimeMillis() - start < 20) {
269 for (int i = 0; i < 50000; i++) {
270 counter += i;
271 }
272 f();
273 }
274 }
275 }
276 }
277 }
278