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("check_verification_constraint: TestApp$Foo$Taz: TestApp$Foo$ShouldBeExcludedChild must be subclass of TestApp$Foo$ShouldBeExcluded"); 103 out.shouldContain("jdk.jfr.Event source: jrt:/jdk.jfr"); 104 out.shouldMatch("TestApp[$]Foo[$]ShouldBeExcluded source: .*/app.jar"); 105 out.shouldMatch("TestApp[$]Foo[$]ShouldBeExcludedChild source: .*/app.jar"); 106 } 107 } 108 } 109 } 110 111 class TestApp { 112 static volatile Object custInstance; 113 static volatile Object custArrayInstance; 114 115 public static void main(String args[]) throws Exception { 116 // In AOT workflow, classes from custom loaders are passed from the preimage 117 // to the final image. See FinalImageRecipes::record_all_classes(). 118 custInstance = initFromCustomLoader(); 119 custArrayInstance = Array.newInstance(custInstance.getClass(), 0); 120 System.out.println(custArrayInstance); 121 System.out.println("Counter = " + Foo.hotSpot()); 122 } 123 124 static Object initFromCustomLoader() throws Exception { 125 String path = "cust.jar"; 126 URL url = new File(path).toURI().toURL(); 127 URL[] urls = new URL[] {url}; 128 URLClassLoader urlClassLoader = 129 new URLClassLoader("MyLoader", urls, null); 130 Class c = Class.forName("CustyWithLoop", true, urlClassLoader); 131 return c.newInstance(); 132 } 133 134 static class MyInvocationHandler implements InvocationHandler { 135 volatile static int cnt; 136 137 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 138 long start = System.currentTimeMillis(); 139 while (System.currentTimeMillis() - start < 20) { 140 cnt += 2; 141 for (int i = 0; i < 1000; i++) { 142 int n = cnt - 2; 143 if (n < 2) { 144 n = 2; 145 } 146 cnt += (i + cnt) % n + cnt % 2; 147 } 148 } 149 return Integer.valueOf(cnt); 150 } 151 } 152 153 static class Foo { 154 volatile static int counter; 155 static Class c = ShouldBeExcluded.class; 156 157 static Map mapProxy = (Map) Proxy.newProxyInstance( 158 Foo.class.getClassLoader(), 159 new Class[] { Map.class }, 160 new MyInvocationHandler()); 161 162 static int hotSpot() { 163 ShouldBeExcluded s = new ShouldBeExcluded(); 164 Bar b = new Bar(); 165 166 long start = System.currentTimeMillis(); 167 while (System.currentTimeMillis() - start < 1000) { 168 lambdaHotSpot(); 169 s.hotSpot2(); 170 b.hotSpot3(); 171 Taz.hotSpot4(); 172 173 // In JDK mainline, generated proxy classes are excluded from the AOT cache. 174 // In Leyden/premain, generated proxy classes included. The following code should 175 // work with either repos. 176 Integer i = (Integer)mapProxy.get(null); 177 counter += i.intValue(); 178 179 if (custInstance != null) { 180 // Classes loaded by custom loaders are included in the AOT cache 181 // but their array classes are excluded. 182 counter += custInstance.equals(null) ? 1 : 2; 183 } 184 185 if (custArrayInstance != null) { 186 if ((counter % 3) == 0) { 187 counter += (custArrayInstance instanceof String) ? 0 : 1; 188 } else { 189 counter += (custArrayInstance instanceof Object) ? 0 : 1; 190 } 191 } 192 } 193 194 return counter + s.m() + s.f + b.m() + b.f; 195 } 196 197 static void f() { 198 if (counter % 2 == 1) { 199 counter ++; 200 } 201 } 202 203 // Generated Lambda classes should be excluded from CDS preimage. 204 static void lambdaHotSpot() { 205 long start = System.currentTimeMillis(); 206 while (System.currentTimeMillis() - start < 20) { 207 doit(() -> counter ++ ); 208 } 209 } 210 211 static void doit(Runnable r) { 212 r.run(); 213 } 214 215 // All subclasses of jdk.jfr.Event are excluded from the CDS archive. 216 static class ShouldBeExcluded extends jdk.jfr.Event { 217 int f = (int)(System.currentTimeMillis()) + 123; 218 int m() { 219 return f + 456; 220 } 221 222 void hotSpot2() { 223 long start = System.currentTimeMillis(); 224 while (System.currentTimeMillis() - start < 20) { 225 for (int i = 0; i < 50000; i++) { 226 counter += i; 227 } 228 f(); 229 } 230 } 231 int func() { 232 return 1; 233 } 234 } 235 236 static class ShouldBeExcludedChild extends ShouldBeExcluded { 237 @Override 238 int func() { 239 return 2; 240 } 241 } 242 243 static class Bar { 244 int f = (int)(System.currentTimeMillis()) + 123; 245 int m() { 246 return f + 456; 247 } 248 249 void hotSpot3() { 250 long start = System.currentTimeMillis(); 251 while (System.currentTimeMillis() - start < 20) { 252 for (int i = 0; i < 50000; i++) { 253 counter += i; 254 } 255 f(); 256 } 257 } 258 } 259 260 static class Taz { 261 static ShouldBeExcluded m() { 262 // When verifying this method, we need to check the constraint that 263 // ShouldBeExcluded must be a supertype of ShouldBeExcludedChild. This information 264 // is checked by SystemDictionaryShared::check_verification_constraints() when the Taz 265 // class is linked during the production run. 266 // 267 // Because ShouldBeExcluded is excluded from the AOT archive, it must be loaded 268 // dynamically from app.jar inside SystemDictionaryShared::check_verification_constraints(). 269 // This must happen after the app class loader has been fully restored from the AOT cache. 270 return new ShouldBeExcludedChild(); 271 } 272 static void hotSpot4() { 273 long start = System.currentTimeMillis(); 274 while (System.currentTimeMillis() - start < 20) { 275 for (int i = 0; i < 50000; i++) { 276 counter += i; 277 } 278 f(); 279 } 280 } 281 } 282 } 283 } 284