1 /*
  2  * Copyright (c) 2023, 2024, 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                             "-Xlog:cds+class=debug");
 83 
 84         TestCommon.run("-cp", app1Jar + File.pathSeparator + app2Jar,
 85                        "-XX:+UnlockDiagnosticVMOptions",
 86                        "-XX:+WhiteBoxAPI",
 87                        bootAppendWhiteBox,
 88                        "AOTClassLinkingVerificationApp", app1Jar)
 89             .assertNormalExit();
 90     }
 91 }
 92 
 93 class AOTClassLinkingVerificationApp {
 94     static WhiteBox wb = WhiteBox.getWhiteBox();
 95     static ClassLoader classLoader = AOTClassLinkingVerificationApp.class.getClassLoader();
 96     static File app1Jar;
 97 
 98     public static void main(String[] args) throws Exception {
 99         app1Jar = new File(args[0]);
100         assertNotShared(UnlinkedSub.class);
101         assertShared(UnlinkedSuper.class);
102         assertShared(Unlinked.class);
103         assertNotShared(Foo.class);
104         assertNotShared(NotFoo.class);
105         String s = Unlinked.doit();
106         if (!s.equals("heyhey")) {
107             throw new RuntimeException("Unlinked.doit() returns wrong result: " + s);
108         }
109 
110         Class cls_BadOldClass = Class.forName("BadOldClass", false, classLoader);
111         assertShared(cls_BadOldClass);
112         try {
113             cls_BadOldClass.newInstance();
114             throw new RuntimeException("BadOldClass cannot be verified");
115         } catch (VerifyError expected) {}
116 
117         Class cls_BadOldClass2 = Class.forName("BadOldClass2", false, classLoader);
118         assertShared(cls_BadOldClass2);
119         try {
120             cls_BadOldClass2.newInstance();
121             throw new RuntimeException("BadOldClass2 cannot be verified");
122         } catch (VerifyError expected) {}
123 
124         Class cls_BadNewClass = Class.forName("BadNewClass", false, classLoader);
125         assertShared(cls_BadNewClass);
126         try {
127             cls_BadNewClass.newInstance();
128             throw new RuntimeException("BadNewClass cannot be verified");
129         } catch (VerifyError expected) {}
130 
131         Class cls_BadNewClass2 = Class.forName("BadNewClass2", false, classLoader);
132         assertShared(cls_BadNewClass2);
133         try {
134             cls_BadNewClass2.newInstance();
135             throw new RuntimeException("BadNewClass2 cannot be verified");
136         } catch (VerifyError expected) {}
137 
138 
139         // Although Vehicle and Car are not specified in the classlist, they are archived as
140         // they were used during dumptime verification of GoodOldClass
141         assertAlreadyLoaded("Vehicle");
142         assertAlreadyLoaded("Car");
143         assertAlreadyLoaded("GoodOldClass");
144 
145         assertShared(GoodOldClass.class);
146         assertShared(Vehicle.class);
147         assertShared(Car.class);
148 
149         GoodOldClass.doit(); // Should not fail
150     }
151 
152     static void assertShared(Class c) {
153         if (!wb.isSharedClass(c)) {
154             throw new RuntimeException("wb.isSharedClass(" + c.getName() + ") should be true");
155         }
156     }
157 
158     static void assertNotShared(Class c) {
159         if (wb.isSharedClass(c)) {
160             throw new RuntimeException("wb.isSharedClass(" + c.getName() + ") should be false");
161         }
162     }
163 
164     static void assertAlreadyLoaded(String className) throws Exception {
165         byte[] data = Util.getClassFileFromJar(app1Jar, className);
166         try {
167             MethodHandles.lookup().defineClass(data);
168         } catch (LinkageError e) {
169             if (e.getMessage().contains("duplicate class definition for " + className)) {
170                 return;
171             } else {
172                 throw e;
173             }
174         }
175         throw new RuntimeException(className + " must have already been loaded");
176     }
177 }
178 
179 
180 class Unlinked {
181     static String doit() {
182         UnlinkedSuper sup = new UnlinkedSub();
183         return sup.doit();
184     }
185 }
186 
187 abstract class UnlinkedSuper {
188     abstract String doit();
189 }
190 
191 class UnlinkedSub extends UnlinkedSuper {
192     String doit() {
193         return "heyhey";
194     }
195 }
196 
197 class Foo {}
198 class NotFoo {}
199 
200 class Vehicle {}
201 class Car extends Vehicle {}