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 In Leyden repo, classes that are linked during the assembly phase can
 27  *       skip verification constraint checks. See JDK-8317269.
 28  *       NOTE: this feature is not included when JDK-8315737 is upstreamed to the mainline.
 29  * @requires vm.cds
 30  * @requires vm.cds.supports.aot.class.linking
 31  * @summary Test for verification of classes that are aot-linked
 32  * @library /test/jdk/lib/testlibrary
 33  *          /test/lib
 34  *          /test/hotspot/jtreg/runtime/cds/appcds
 35  *          /test/hotspot/jtreg/runtime/cds/appcds/test-classes
 36  * @build GoodOldClass BadOldClass BadOldClass2 BadNewClass BadNewClass2
 37  * @build AOTClassLinkingVerification
 38  * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox
 39  * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app1.jar
 40  *                 AOTClassLinkingVerificationApp
 41  *                 Unlinked UnlinkedSuper
 42  *                 BadOldClass
 43  *                 BadOldClass2
 44  *                 BadNewClass
 45  *                 BadNewClass2
 46  *                 GoodOldClass Vehicle Car
 47  *                 Util
 48  * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app2.jar
 49  *                 UnlinkedSub
 50  *                 Foo NotFoo
 51  * @run driver AOTClassLinkingVerification
 52  */
 53 
 54 import java.io.File;
 55 import java.lang.invoke.MethodHandles;
 56 import jdk.test.lib.helpers.ClassFileInstaller;
 57 import jdk.test.whitebox.WhiteBox;
 58 
 59 public class AOTClassLinkingVerification {
 60     static final String app1Jar = ClassFileInstaller.getJarPath("app1.jar");
 61     static final String app2Jar = ClassFileInstaller.getJarPath("app2.jar");
 62     static final String wbJar = TestCommon.getTestJar("WhiteBox.jar");
 63     static final String bootAppendWhiteBox = "-Xbootclasspath/a:" + wbJar;
 64     static final String mainClass = AOTClassLinkingVerificationApp.class.getName();
 65 
 66     public static void main(String[] args) throws Exception {
 67         // Dump without app2.jar so:
 68         //  - Unlinked can be resolved, but UnlinkedSuper UnlinkedSub cannot be resolved,
 69         //    so Unlinked cannot be verified at dump time.
 70         //  - BadOldClass2 can be resolved, but Foo and NotFoo cannot be resolved,
 71         //    so BadOldClass2 cannot be verified at dump time.
 72         //  - BadNewClass2 can be resolved, but Foo and NotFoo cannot be resolved,
 73         //    so BadNewClass2 cannot be verified at dump time.
 74         TestCommon.testDump(app1Jar, TestCommon.list("Unlinked",
 75                                                      "BadOldClass",
 76                                                      "BadOldClass2",
 77                                                      "BadNewClass",
 78                                                      "BadNewClass2",
 79                                                      "GoodOldClass"),
 80                             bootAppendWhiteBox,
 81                             "-XX:+AOTClassLinking",
 82                             "-XX:+ArchivePackages",
 83                             "-XX:+ArchiveProtectionDomains",
 84                             "-Xlog:cds+class=debug");
 85 
 86         TestCommon.run("-cp", app1Jar + File.pathSeparator + app2Jar,
 87                        "-XX:+UnlockDiagnosticVMOptions",
 88                        "-XX:+WhiteBoxAPI",
 89                        bootAppendWhiteBox,
 90                        "AOTClassLinkingVerificationApp", app1Jar)
 91             .assertNormalExit();
 92     }
 93 }
 94 
 95 class AOTClassLinkingVerificationApp {
 96     static WhiteBox wb = WhiteBox.getWhiteBox();
 97     static ClassLoader classLoader = AOTClassLinkingVerificationApp.class.getClassLoader();
 98     static File app1Jar;
 99 
100     public static void main(String[] args) throws Exception {
101         app1Jar = new File(args[0]);
102         assertNotShared(UnlinkedSub.class);
103         assertShared(UnlinkedSuper.class);
104         assertNotShared(Unlinked.class); // failed verification during dump time
105         assertNotShared(Foo.class);
106         assertNotShared(NotFoo.class);
107         String s = Unlinked.doit();
108         if (!s.equals("heyhey")) {
109             throw new RuntimeException("Unlinked.doit() returns wrong result: " + s);
110         }
111 
112         Class cls_BadOldClass = Class.forName("BadOldClass", false, classLoader);
113         assertNotShared(cls_BadOldClass); // failed verification during dump time
114         try {
115             cls_BadOldClass.newInstance();
116             throw new RuntimeException("BadOldClass cannot be verified");
117         } catch (VerifyError expected) {}
118 
119         Class cls_BadOldClass2 = Class.forName("BadOldClass2", false, classLoader);
120         assertNotShared(cls_BadOldClass2); // failed verification during dump time
121         try {
122             cls_BadOldClass2.newInstance();
123             throw new RuntimeException("BadOldClass2 cannot be verified");
124         } catch (VerifyError expected) {}
125 
126         Class cls_BadNewClass = Class.forName("BadNewClass", false, classLoader);
127         assertNotShared(cls_BadNewClass); // failed verification during dump time
128         try {
129             cls_BadNewClass.newInstance();
130             throw new RuntimeException("BadNewClass cannot be verified");
131         } catch (VerifyError expected) {}
132 
133         Class cls_BadNewClass2 = Class.forName("BadNewClass2", false, classLoader);
134         assertNotShared(cls_BadNewClass2); // failed verification during dump time
135         try {
136             cls_BadNewClass2.newInstance();
137             throw new RuntimeException("BadNewClass2 cannot be verified");
138         } catch (VerifyError expected) {}
139 
140 
141         // Although Vehicle and Car are not specified in the classlist, they are archived as
142         // they were used during dumptime verification of GoodOldClass
143         assertAlreadyLoaded("Vehicle");
144         assertAlreadyLoaded("Car");
145         assertAlreadyLoaded("GoodOldClass");
146 
147         assertShared(GoodOldClass.class);
148         assertShared(Vehicle.class);
149         assertShared(Car.class);
150 
151         GoodOldClass.doit(); // Should not fail
152     }
153 
154     static void assertShared(Class c) {
155         if (!wb.isSharedClass(c)) {
156             throw new RuntimeException("wb.isSharedClass(" + c.getName() + ") should be true");
157         }
158     }
159 
160     static void assertNotShared(Class c) {
161         if (wb.isSharedClass(c)) {
162             throw new RuntimeException("wb.isSharedClass(" + c.getName() + ") should be false");
163         }
164     }
165 
166     static void assertAlreadyLoaded(String className) throws Exception {
167         byte[] data = Util.getClassFileFromJar(app1Jar, className);
168         try {
169             MethodHandles.lookup().defineClass(data);
170         } catch (LinkageError e) {
171             if (e.getMessage().contains("duplicate class definition for " + className)) {
172                 return;
173             } else {
174                 throw e;
175             }
176         }
177         throw new RuntimeException(className + " must have already been loaded");
178     }
179 }
180 
181 
182 class Unlinked {
183     static String doit() {
184         UnlinkedSuper sup = new UnlinkedSub();
185         return sup.doit();
186     }
187 }
188 
189 abstract class UnlinkedSuper {
190     abstract String doit();
191 }
192 
193 class UnlinkedSub extends UnlinkedSuper {
194     String doit() {
195         return "heyhey";
196     }
197 }
198 
199 class Foo {}
200 class NotFoo {}
201 
202 class Vehicle {}
203 class Car extends Vehicle {}