1 /* 2 * Copyright (c) 2022, 2023, 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 * @bug 8214781 8293187 28 * @summary Test for the -XX:ArchiveHeapTestClass flag 29 * @requires vm.debug == true & vm.cds.write.archived.java.heap 30 * @modules java.base/sun.invoke.util java.logging 31 * @library /test/jdk/lib/testlibrary /test/lib 32 * /test/hotspot/jtreg/runtime/cds/appcds 33 * /test/hotspot/jtreg/runtime/cds/appcds/test-classes 34 * @build ArchiveHeapTestClass Hello pkg.ClassInPackage 35 * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar boot.jar 36 * CDSTestClassA CDSTestClassA$XX CDSTestClassA$YY 37 * CDSTestClassB CDSTestClassC CDSTestClassD 38 * CDSTestClassE CDSTestClassF CDSTestClassG 39 * pkg.ClassInPackage 40 * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar Hello 41 * @run driver ArchiveHeapTestClass 42 */ 43 44 import jdk.test.lib.Platform; 45 import jdk.test.lib.helpers.ClassFileInstaller; 46 import jdk.test.lib.process.OutputAnalyzer; 47 48 public class ArchiveHeapTestClass { 49 static final String bootJar = ClassFileInstaller.getJarPath("boot.jar"); 50 static final String appJar = ClassFileInstaller.getJarPath("app.jar"); 51 static final String[] appClassList = {"Hello"}; 52 53 static final String CDSTestClassA_name = CDSTestClassA.class.getName(); 54 static final String CDSTestClassB_name = CDSTestClassB.class.getName(); 55 static final String CDSTestClassC_name = CDSTestClassC.class.getName(); 56 static final String CDSTestClassD_name = CDSTestClassD.class.getName(); 57 static final String CDSTestClassE_name = CDSTestClassE.class.getName(); 58 static final String CDSTestClassF_name = CDSTestClassF.class.getName(); 59 static final String CDSTestClassG_name = CDSTestClassG.class.getName(); 60 static final String ClassInPackage_name = pkg.ClassInPackage.class.getName().replace('.', '/'); 61 static final String ARCHIVE_TEST_FIELD_NAME = "archivedObjects"; 62 63 public static void main(String[] args) throws Exception { 64 testDebugBuild(); 65 } 66 67 static OutputAnalyzer dumpHelloOnly(String... extraOpts) throws Exception { 68 return TestCommon.dump(appJar, appClassList, extraOpts); 69 } 70 71 static OutputAnalyzer dumpBootAndHello(String bootClass, String... extraOpts) throws Exception { 72 String classlist[] = TestCommon.concat(appClassList, bootClass); 73 extraOpts = TestCommon.concat(extraOpts, 74 "-Xbootclasspath/a:" + bootJar, 75 "-XX:ArchiveHeapTestClass=" + bootClass, 76 "-Xlog:cds+heap"); 77 return TestCommon.dump(appJar, classlist, extraOpts); 78 } 79 80 static int caseNum = 0; 81 static void testCase(String s) { 82 System.out.println("=================================================="); 83 System.out.println(" Test " + (++caseNum) + ": " + s); 84 } 85 86 static void mustContain(OutputAnalyzer output, String... expectStrs) throws Exception { 87 for (String s : expectStrs) { 88 output.shouldContain(s); 89 } 90 } 91 92 static void mustFail(OutputAnalyzer output, String... expectStrs) throws Exception { 93 mustContain(output, expectStrs); 94 output.shouldNotHaveExitValue(0); 95 } 96 97 static void mustSucceed(OutputAnalyzer output, String... expectStrs) throws Exception { 98 mustContain(output, expectStrs); 99 output.shouldHaveExitValue(0); 100 } 101 102 static void testDebugBuild() throws Exception { 103 OutputAnalyzer output; 104 105 testCase("Simple positive case"); 106 output = dumpBootAndHello(CDSTestClassA_name); 107 mustSucceed(output, CDSTestClassA.getOutput()); // make sure <clinit> is executed 108 output.shouldMatch("warning.*cds.*Loading ArchiveHeapTestClass " + CDSTestClassA_name); 109 output.shouldMatch("warning.*cds.*Initializing ArchiveHeapTestClass " + CDSTestClassA_name); 110 output.shouldContain("Archived field " + CDSTestClassA_name + "::" + ARCHIVE_TEST_FIELD_NAME); 111 output.shouldMatch("Archived object klass CDSTestClassA .*\\[LCDSTestClassA;"); 112 output.shouldMatch("Archived object klass CDSTestClassA .*CDSTestClassA\\$YY"); 113 114 TestCommon.run("-Xbootclasspath/a:" + bootJar, "-cp", appJar, "-Xlog:cds+heap", CDSTestClassA_name) 115 .assertNormalExit(CDSTestClassA.getOutput(), 116 "resolve subgraph " + CDSTestClassA_name); 117 118 testCase("Class doesn't exist"); 119 output = dumpHelloOnly("-XX:ArchiveHeapTestClass=NoSuchClass"); 120 mustFail(output, "Fail to initialize archive heap: NoSuchClass cannot be loaded"); 121 122 testCase("Class doesn't exist (objarray)"); 123 output = dumpHelloOnly("-XX:ArchiveHeapTestClass=[LNoSuchClass;"); 124 mustFail(output, "Fail to initialize archive heap: [LNoSuchClass; cannot be loaded"); 125 126 testCase("Not an instance klass"); 127 output = dumpHelloOnly("-XX:ArchiveHeapTestClass=[Ljava/lang/Object;"); 128 mustFail(output, "Fail to initialize archive heap: [Ljava/lang/Object; is not an instance class"); 129 130 testCase("Not in boot loader"); 131 output = dumpHelloOnly("-XX:ArchiveHeapTestClass=Hello"); 132 mustFail(output, "Fail to initialize archive heap: Hello cannot be loaded by the boot loader"); 133 134 testCase("Not from unnamed module"); 135 output = dumpHelloOnly("-XX:ArchiveHeapTestClass=java/lang/Object"); 136 mustFail(output, "ArchiveHeapTestClass java/lang/Object is not in unnamed module"); 137 138 testCase("Not from unnamed package"); 139 output = dumpBootAndHello(ClassInPackage_name); 140 mustFail(output, "ArchiveHeapTestClass pkg/ClassInPackage is not in unnamed package"); 141 142 testCase("Field not found"); 143 output = dumpBootAndHello(CDSTestClassB_name); 144 mustFail(output, "Unable to find the static T_OBJECT field CDSTestClassB::archivedObjects"); 145 146 testCase("Not a static field"); 147 output = dumpBootAndHello(CDSTestClassC_name); 148 mustFail(output, "Unable to find the static T_OBJECT field CDSTestClassC::archivedObjects"); 149 150 testCase("Not a T_OBJECT field"); 151 output = dumpBootAndHello(CDSTestClassD_name); 152 mustFail(output, "Unable to find the static T_OBJECT field CDSTestClassD::archivedObjects"); 153 154 testCase("Use a disallowed class: in unnamed module but not in unname package"); 155 output = dumpBootAndHello(CDSTestClassE_name); 156 mustFail(output, "Class pkg.ClassInPackage not allowed in archive heap"); 157 158 testCase("Use a disallowed class: not in java.base module"); 159 output = dumpBootAndHello(CDSTestClassF_name); 160 mustFail(output, "Class java.util.logging.Level not allowed in archive heap"); 161 162 if (false) { // JDK-8293187 163 testCase("sun.invoke.util.Wrapper"); 164 output = dumpBootAndHello(CDSTestClassG_name); 165 mustSucceed(output); 166 } 167 } 168 } 169 170 class CDSTestClassA { 171 static final String output = "CDSTestClassA.<clinit> was executed"; 172 static Object[] archivedObjects; 173 static { 174 archivedObjects = new Object[5]; 175 archivedObjects[0] = output; 176 archivedObjects[1] = new CDSTestClassA[0]; 177 archivedObjects[2] = new YY(); 178 archivedObjects[3] = new int[0]; 179 archivedObjects[4] = new int[2][2]; 180 System.out.println(output); 181 System.out.println("CDSTestClassA module = " + CDSTestClassA.class.getModule()); 182 System.out.println("CDSTestClassA package = " + CDSTestClassA.class.getPackage()); 183 System.out.println("CDSTestClassA[] module = " + archivedObjects[1].getClass().getModule()); 184 System.out.println("CDSTestClassA[] package = " + archivedObjects[1].getClass().getPackage()); 185 } 186 187 static String getOutput() { 188 return output; 189 } 190 191 public static void main(String args[]) { 192 if (CDSTestClassA.class.getModule().isNamed()) { 193 throw new RuntimeException("CDSTestClassA must be in unnamed module"); 194 } 195 if (CDSTestClassA.class.getPackage() != null) { 196 throw new RuntimeException("CDSTestClassA must be in null package"); 197 } 198 if (archivedObjects[1].getClass().getModule().isNamed()) { 199 throw new RuntimeException("CDSTestClassA[] must be in unnamed module"); 200 } 201 if (archivedObjects[1].getClass().getPackage() != null) { 202 throw new RuntimeException("CDSTestClassA[] must be in null package"); 203 } 204 XX.doit(); 205 YY.doit(); 206 } 207 208 // This is an inner class that has NOT been archived. 209 static class XX { 210 static void doit() { 211 System.out.println("XX module = " + XX.class.getModule()); 212 System.out.println("XX package = " + XX.class.getPackage()); 213 214 if (XX.class.getModule().isNamed()) { 215 throw new RuntimeException("XX must be in unnamed module"); 216 } 217 if (XX.class.getPackage() != null) { 218 throw new RuntimeException("XX must be in null package"); 219 } 220 } 221 } 222 223 // This is an inner class that HAS been archived. 224 static class YY { 225 static void doit() { 226 System.out.println("YY module = " + YY.class.getModule()); 227 System.out.println("YY package = " + YY.class.getPackage()); 228 229 if (YY.class.getModule().isNamed()) { 230 throw new RuntimeException("YY must be in unnamed module"); 231 } 232 if (YY.class.getPackage() != null) { 233 throw new RuntimeException("YY must be in null package"); 234 } 235 } 236 } 237 } 238 239 class CDSTestClassB { 240 // No field named "archivedObjects" 241 } 242 243 class CDSTestClassC { 244 Object[] archivedObjects; // Not a static field 245 } 246 247 class CDSTestClassD { 248 static int archivedObjects; // Not an int field 249 } 250 251 class CDSTestClassE { 252 static Object[] archivedObjects; 253 static { 254 // Not in unnamed package of unnamed module 255 archivedObjects = new Object[1]; 256 archivedObjects[0] = new pkg.ClassInPackage(); 257 } 258 } 259 260 class CDSTestClassF { 261 static Object[] archivedObjects; 262 static { 263 // Not in java.base 264 archivedObjects = new Object[1]; 265 archivedObjects[0] = java.util.logging.Level.OFF; 266 } 267 } 268 269 class CDSTestClassG { 270 static Object[] archivedObjects; 271 static { 272 // Not in java.base 273 archivedObjects = new Object[1]; 274 archivedObjects[0] = sun.invoke.util.Wrapper.BOOLEAN; 275 } 276 }