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.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         {
 66           Tester tester = new Tester();
 67           tester.run(new String[] {"AOT"} );
 68         }
 69     }
 70 
 71     static class Tester extends CDSAppTester {
 72         public Tester() {
 73             super(mainClass);
 74         }
 75 
 76         @Override
 77         public String classpath(RunMode runMode) {
 78             return appJar;
 79         }
 80 
 81         @Override
 82         public String[] vmArgs(RunMode runMode) {
 83             return new String[] {
 84                 "-Xlog:cds+resolve=trace",
 85             };
 86         }
 87 
 88         @Override
 89         public String[] appCommandLine(RunMode runMode) {
 90             return new String[] {
 91                 mainClass, runMode.name()
 92             };
 93         }
 94 
 95         @Override
 96         public void checkExecution(OutputAnalyzer out, RunMode runMode) {
 97             if (isDumping(runMode)) {
 98                 out.shouldNotMatch("cds,resolve.*archived field.*TestApp.Foo => TestApp.Foo.ShouldBeExcluded.f:I");
 99             }
100         }
101     }
102 }
103 
104 class TestApp {
105     static volatile Object custInstance;
106     static volatile Object custArrayInstance;
107 
108     public static void main(String args[]) throws Exception {
109         // In new workflow, classes from custom loaders are passed from the preimage
110         // to the final image. See ClassPrelinker::record_unregistered_klasses().
111         custInstance = initFromCustomLoader();
112         custArrayInstance = Array.newInstance(custInstance.getClass(), 0);
113         System.out.println(custArrayInstance);
114         System.out.println("Counter = " + Foo.hotSpot());
115     }
116 
117     static Object initFromCustomLoader() throws Exception {
118         String path = "cust.jar";
119         URL url = new File(path).toURI().toURL();
120         URL[] urls = new URL[] {url};
121         URLClassLoader urlClassLoader =
122             new URLClassLoader("MyLoader", urls, null);
123         Class c = Class.forName("CustyWithLoop", true, urlClassLoader);
124         return c.newInstance();
125     }
126 
127     static class MyInvocationHandler implements InvocationHandler {
128         volatile static int cnt;
129 
130         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
131             long start = System.currentTimeMillis();
132             while (System.currentTimeMillis() - start < 20) {
133                 cnt += 2;
134                 for (int i = 0; i < 1000; i++) {
135                     int n = cnt - 2;
136                     if (n < 2) {
137                         n = 2;
138                     }
139                     cnt += (i + cnt) % n + cnt % 2;
140                 }
141             }
142             return Integer.valueOf(cnt);
143         }
144     }
145 
146     static class Foo {
147         volatile static int counter;
148         static Class c = ShouldBeExcluded.class;
149 
150         static Map mapProxy = (Map) Proxy.newProxyInstance(
151             Foo.class.getClassLoader(),
152             new Class[] { Map.class },
153             new MyInvocationHandler());
154 
155         static int hotSpot() {
156             ShouldBeExcluded s = new ShouldBeExcluded();
157             Bar b = new Bar();
158 
159             long start = System.currentTimeMillis();
160             while (System.currentTimeMillis() - start < 1000) {
161                 lambdaHotSpot();
162                 s.hotSpot2();
163                 b.hotSpot3();
164 
165                 // In JDK mainline, generated proxy classes are excluded from the AOT cache.
166                 // In Leyden/premain, generated proxy classes included. The following code should
167                 // work with either repos.
168                 Integer i = (Integer)mapProxy.get(null);
169                 counter += i.intValue();
170 
171                 if (custInstance != null) {
172                     // Classes loaded by custom loaders are included included in the AOT cache
173                     // but their array classes are excluded.
174                     counter += custInstance.equals(null) ? 1 : 2;
175                 }
176 
177                 if (custArrayInstance != null) {
178                     if ((counter % 3) == 0) {
179                         counter += (custArrayInstance instanceof String) ? 0 : 1;
180                     } else {
181                         counter += (custArrayInstance instanceof Object) ? 0 : 1;
182                     }
183                 }
184             }
185 
186             return counter + s.m() + s.f + b.m() + b.f;
187         }
188 
189         static void f() {
190             if (counter % 2 == 1) {
191                 counter ++;
192             }
193         }
194 
195         // Generated Lambda classes should be excluded from CDS preimage.
196         static void lambdaHotSpot() {
197             long start = System.currentTimeMillis();
198             while (System.currentTimeMillis() - start < 20) {
199                 doit(() -> counter ++ );
200             }
201         }
202 
203         static void doit(Runnable r) {
204             r.run();
205         }
206 
207         // All subclasses of jdk.jfr.Event are excluded from the CDS archive.
208         static class ShouldBeExcluded extends jdk.jfr.Event {
209             int f = (int)(System.currentTimeMillis()) + 123;
210             int m() {
211                 return f + 456;
212             }
213 
214             void hotSpot2() {
215                 long start = System.currentTimeMillis();
216                 while (System.currentTimeMillis() - start < 20) {
217                     for (int i = 0; i < 50000; i++) {
218                         counter += i;
219                     }
220                     f();
221                 }
222             }
223         }
224 
225         static class Bar {
226             int f = (int)(System.currentTimeMillis()) + 123;
227             int m() {
228                 return f + 456;
229             }
230 
231             void hotSpot3() {
232                 long start = System.currentTimeMillis();
233                 while (System.currentTimeMillis() - start < 20) {
234                     for (int i = 0; i < 50000; i++) {
235                         counter += i;
236                     }
237                     f();
238                 }
239             }
240         }
241     }
242 }
243