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  * @comment work around JDK-8345635
 30  * @requires !vm.jvmci.enabled
 31  * @library /test/jdk/lib/testlibrary /test/lib
 32  *          /test/hotspot/jtreg/runtime/cds/appcds/aotCache/test-classes
 33  * @build ExcludedClasses CustyWithLoop
 34  * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
 35  *                 TestApp
 36  *                 TestApp$Foo
 37  *                 TestApp$Foo$Bar
 38  *                 TestApp$Foo$ShouldBeExcluded
 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.InvocationHandler;
 47 import java.lang.reflect.Method;
 48 import java.lang.reflect.Proxy;
 49 import java.net.URL;
 50 import java.net.URLClassLoader;
 51 import java.security.ProtectionDomain;
 52 import java.util.Map;
 53 
 54 import jdk.jfr.Event;
 55 import jdk.test.lib.cds.CDSAppTester;
 56 import jdk.test.lib.helpers.ClassFileInstaller;
 57 import jdk.test.lib.process.OutputAnalyzer;
 58 
 59 public class ExcludedClasses {
 60     static final String appJar = ClassFileInstaller.getJarPath("app.jar");
 61     static final String mainClass = "TestApp";
 62 
 63     public static void main(String[] args) throws Exception {
 64         {
 65           Tester tester = new Tester();
 66           tester.run(new String[] {"AOT"} );
 67         }
 68         {
 69           Tester tester = new Tester();
 70           tester.run(new String[] {"LEYDEN"} );
 71         }
 72     }
 73 
 74     static class Tester extends CDSAppTester {
 75         public Tester() {
 76             super(mainClass);;
 77         }
 78 
 79         @Override
 80         public String classpath(RunMode runMode) {
 81             return appJar;
 82         }
 83 
 84         @Override
 85         public String[] vmArgs(RunMode runMode) {
 86             return new String[] {
 87                 "-Xlog:cds+resolve=trace",
 88             };
 89         }
 90 
 91         @Override
 92         public String[] appCommandLine(RunMode runMode) {
 93             return new String[] {
 94                 mainClass, runMode.name()
 95             };
 96         }
 97 
 98         @Override
 99         public void checkExecution(OutputAnalyzer out, RunMode runMode) {
100             switch (runMode) {
101             case RunMode.TRAINING:
102             case RunMode.TRAINING0:
103             case RunMode.DUMP_STATIC:
104                 out.shouldNotMatch("cds,resolve.*archived field.*TestApp.Foo => TestApp.Foo.ShouldBeExcluded.f:I");
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 new workflow, classes from custom loaders are passed from the preimage
116         // to the final image. See ClassPrelinker::record_unregistered_klasses().
117         custInstance = initFromCustomLoader();
118         custArrayInstance = java.lang.reflect.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 
171                 // In JDK mainline, generated proxy classes are excluded from the AOT cache.
172                 // In Leyden/premain, generated proxy classes included. The following code should
173                 // work with either repos.
174                 Integer i = (Integer)mapProxy.get(null);
175                 counter += i.intValue();
176 
177                 if (custInstance != null) {
178                     // Classes loaded by custom loaders are included included in the AOT cache
179                     // but their array classes are excluded.
180                     counter += custInstance.equals(null) ? 1 : 2;
181                 }
182 
183                 if (custArrayInstance != null) {
184                     if ((counter % 3) == 0) {
185                         counter += (custArrayInstance instanceof String) ? 0 : 1;
186                     } else {
187                         counter += (custArrayInstance instanceof Object) ? 0 : 1;
188                     }
189                 }
190             }
191 
192             return counter + s.m() + s.f + b.m() + b.f;
193         }
194 
195         static void f() {
196             if (counter % 2 == 1) {
197                 counter ++;
198             }
199         }
200 
201         // Generated Lambda classes should be excluded from CDS preimage.
202         static void lambdaHotSpot() {
203             long start = System.currentTimeMillis();
204             while (System.currentTimeMillis() - start < 20) {
205                 doit(() -> {
206                         counter ++;
207                     });
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         }
232 
233         static class Bar {
234             int f = (int)(System.currentTimeMillis()) + 123;
235             int m() {
236                 return f + 456;
237             }
238 
239             void hotSpot3() {
240                 long start = System.currentTimeMillis();
241                 while (System.currentTimeMillis() - start < 20) {
242                     for (int i = 0; i < 50000; i++) {
243                         counter += i;
244                     }
245                     f();
246                 }
247             }
248         }
249     }
250 }
251