1 /* 2 * Copyright (c) 2022, 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 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.base/jdk.internal.misc 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 CDSTestClassG$MyEnum 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 import jdk.internal.misc.CDS; 48 49 public class ArchiveHeapTestClass { 50 static final String bootJar = ClassFileInstaller.getJarPath("boot.jar"); 51 static final String appJar = ClassFileInstaller.getJarPath("app.jar"); 52 static final String[] appClassList = {"Hello"}; 53 54 static final String CDSTestClassA_name = CDSTestClassA.class.getName(); 55 static final String CDSTestClassB_name = CDSTestClassB.class.getName(); 56 static final String CDSTestClassC_name = CDSTestClassC.class.getName(); 57 static final String CDSTestClassD_name = CDSTestClassD.class.getName(); 58 static final String CDSTestClassE_name = CDSTestClassE.class.getName(); 59 static final String CDSTestClassF_name = CDSTestClassF.class.getName(); 60 static final String CDSTestClassG_name = CDSTestClassG.class.getName(); 61 static final String ClassInPackage_name = pkg.ClassInPackage.class.getName().replace('.', '/'); 62 static final String ARCHIVE_TEST_FIELD_NAME = "archivedObjects"; 63 64 public static void main(String[] args) throws Exception { 65 testDebugBuild(); 66 } 67 68 static OutputAnalyzer dumpHelloOnly(String... extraOpts) throws Exception { 69 return TestCommon.dump(appJar, appClassList, extraOpts); 70 } 71 72 static OutputAnalyzer dumpBootAndHello(String bootClass, String... extraOpts) throws Exception { 73 String classlist[] = TestCommon.concat(appClassList, bootClass); 74 extraOpts = TestCommon.concat(extraOpts, 75 "-Xbootclasspath/a:" + bootJar, 76 "-XX:ArchiveHeapTestClass=" + bootClass, 77 "-Xlog:cds+heap"); 78 return TestCommon.dump(appJar, classlist, extraOpts); 79 } 80 81 static int caseNum = 0; 82 static void testCase(String s) { 83 System.out.println("=================================================="); 84 System.out.println(" Test " + (++caseNum) + ": " + s); 85 } 86 87 static void mustContain(OutputAnalyzer output, String... expectStrs) throws Exception { 88 for (String s : expectStrs) { 89 output.shouldContain(s); 90 } 91 } 92 93 static void mustFail(OutputAnalyzer output, String... expectStrs) throws Exception { 94 mustContain(output, expectStrs); 95 output.shouldNotHaveExitValue(0); 96 } 97 98 static void mustSucceed(OutputAnalyzer output, String... expectStrs) throws Exception { 99 mustContain(output, expectStrs); 100 output.shouldHaveExitValue(0); 101 } 102 103 static void testDebugBuild() throws Exception { 104 OutputAnalyzer output; 105 106 testCase("sun.invoke.util.Wrapper"); 107 output = dumpBootAndHello(CDSTestClassG_name, 108 "--add-exports", "java.base/sun.invoke.util=ALL-UNNAMED", 109 "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED"); 110 mustSucceed(output); 111 112 TestCommon.run("-Xbootclasspath/a:" + bootJar, "-cp", appJar, "-Xlog:cds+heap", 113 "--add-exports", "java.base/sun.invoke.util=ALL-UNNAMED", 114 "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED", 115 CDSTestClassG_name) 116 .assertNormalExit("resolve subgraph " + CDSTestClassG_name, 117 "Initialized from CDS"); 118 119 testCase("Simple positive case"); 120 output = dumpBootAndHello(CDSTestClassA_name); 121 mustSucceed(output, CDSTestClassA.getOutput()); // make sure <clinit> is executed 122 output.shouldMatch("warning.*cds.*Loading ArchiveHeapTestClass " + CDSTestClassA_name); 123 output.shouldMatch("warning.*cds.*Initializing ArchiveHeapTestClass " + CDSTestClassA_name); 124 output.shouldContain("Archived field " + CDSTestClassA_name + "::" + ARCHIVE_TEST_FIELD_NAME); 125 output.shouldMatch("Archived object klass CDSTestClassA .*\\[LCDSTestClassA;"); 126 output.shouldMatch("Archived object klass CDSTestClassA .*CDSTestClassA\\$YY"); 127 128 TestCommon.run("-Xbootclasspath/a:" + bootJar, "-cp", appJar, "-Xlog:cds+heap", CDSTestClassA_name) 129 .assertNormalExit(CDSTestClassA.getOutput(), 130 "resolve subgraph " + CDSTestClassA_name); 131 132 testCase("Class doesn't exist"); 133 output = dumpHelloOnly("-XX:ArchiveHeapTestClass=NoSuchClass"); 134 mustFail(output, "Fail to initialize archive heap: NoSuchClass cannot be loaded"); 135 136 testCase("Class doesn't exist (objarray)"); 137 output = dumpHelloOnly("-XX:ArchiveHeapTestClass=[LNoSuchClass;"); 138 mustFail(output, "Fail to initialize archive heap: [LNoSuchClass; cannot be loaded"); 139 140 testCase("Not an instance klass"); 141 output = dumpHelloOnly("-XX:ArchiveHeapTestClass=[Ljava/lang/Object;"); 142 mustFail(output, "Fail to initialize archive heap: [Ljava/lang/Object; is not an instance class"); 143 144 testCase("Not in boot loader"); 145 output = dumpHelloOnly("-XX:ArchiveHeapTestClass=Hello"); 146 mustFail(output, "Fail to initialize archive heap: Hello cannot be loaded by the boot loader"); 147 148 testCase("Not from unnamed module"); 149 output = dumpHelloOnly("-XX:ArchiveHeapTestClass=java/lang/Object"); 150 mustFail(output, "ArchiveHeapTestClass java/lang/Object is not in unnamed module"); 151 152 testCase("Not from unnamed package"); 153 output = dumpBootAndHello(ClassInPackage_name); 154 mustFail(output, "ArchiveHeapTestClass pkg/ClassInPackage is not in unnamed package"); 155 156 testCase("Field not found"); 157 output = dumpBootAndHello(CDSTestClassB_name); 158 mustFail(output, "Unable to find the static T_OBJECT field CDSTestClassB::archivedObjects"); 159 160 testCase("Not a static field"); 161 output = dumpBootAndHello(CDSTestClassC_name); 162 mustFail(output, "Unable to find the static T_OBJECT field CDSTestClassC::archivedObjects"); 163 164 testCase("Not a T_OBJECT field"); 165 output = dumpBootAndHello(CDSTestClassD_name); 166 mustFail(output, "Unable to find the static T_OBJECT field CDSTestClassD::archivedObjects"); 167 168 testCase("Use a disallowed class: in unnamed module but not in unname package"); 169 output = dumpBootAndHello(CDSTestClassE_name); 170 mustFail(output, "Class pkg.ClassInPackage not allowed in archive heap"); 171 172 testCase("Use a disallowed class: not in java.base module"); 173 output = dumpBootAndHello(CDSTestClassF_name); 174 mustFail(output, "Class java.util.logging.Level not allowed in archive heap"); 175 176 if (false) { // JDK-8293187 177 testCase("sun.invoke.util.Wrapper"); 178 output = dumpBootAndHello(CDSTestClassG_name); 179 mustSucceed(output); 180 } 181 } 182 } 183 184 class CDSTestClassA { 185 static final String output = "CDSTestClassA.<clinit> was executed"; 186 static Object[] archivedObjects; 187 static { 188 archivedObjects = new Object[5]; 189 archivedObjects[0] = output; 190 archivedObjects[1] = new CDSTestClassA[0]; 191 archivedObjects[2] = new YY(); 192 archivedObjects[3] = new int[0]; 193 archivedObjects[4] = new int[2][2]; 194 System.out.println(output); 195 System.out.println("CDSTestClassA module = " + CDSTestClassA.class.getModule()); 196 System.out.println("CDSTestClassA package = " + CDSTestClassA.class.getPackage()); 197 System.out.println("CDSTestClassA[] module = " + archivedObjects[1].getClass().getModule()); 198 System.out.println("CDSTestClassA[] package = " + archivedObjects[1].getClass().getPackage()); 199 } 200 201 static String getOutput() { 202 return output; 203 } 204 205 public static void main(String args[]) { 206 if (CDSTestClassA.class.getModule().isNamed()) { 207 throw new RuntimeException("CDSTestClassA must be in unnamed module"); 208 } 209 if (CDSTestClassA.class.getPackage() != null) { 210 throw new RuntimeException("CDSTestClassA must be in null package"); 211 } 212 if (archivedObjects[1].getClass().getModule().isNamed()) { 213 throw new RuntimeException("CDSTestClassA[] must be in unnamed module"); 214 } 215 if (archivedObjects[1].getClass().getPackage() != null) { 216 throw new RuntimeException("CDSTestClassA[] must be in null package"); 217 } 218 XX.doit(); 219 YY.doit(); 220 } 221 222 // This is an inner class that has NOT been archived. 223 static class XX { 224 static void doit() { 225 System.out.println("XX module = " + XX.class.getModule()); 226 System.out.println("XX package = " + XX.class.getPackage()); 227 228 if (XX.class.getModule().isNamed()) { 229 throw new RuntimeException("XX must be in unnamed module"); 230 } 231 if (XX.class.getPackage() != null) { 232 throw new RuntimeException("XX must be in null package"); 233 } 234 } 235 } 236 237 // This is an inner class that HAS been archived. 238 static class YY { 239 static void doit() { 240 System.out.println("YY module = " + YY.class.getModule()); 241 System.out.println("YY package = " + YY.class.getPackage()); 242 243 if (YY.class.getModule().isNamed()) { 244 throw new RuntimeException("YY must be in unnamed module"); 245 } 246 if (YY.class.getPackage() != null) { 247 throw new RuntimeException("YY must be in null package"); 248 } 249 } 250 } 251 } 252 253 class CDSTestClassB { 254 // No field named "archivedObjects" 255 } 256 257 class CDSTestClassC { 258 Object[] archivedObjects; // Not a static field 259 } 260 261 class CDSTestClassD { 262 static int archivedObjects; // Not an int field 263 } 264 265 class CDSTestClassE { 266 static Object[] archivedObjects; 267 static { 268 // Not in unnamed package of unnamed module 269 archivedObjects = new Object[1]; 270 archivedObjects[0] = new pkg.ClassInPackage(); 271 } 272 } 273 274 class CDSTestClassF { 275 static Object[] archivedObjects; 276 static { 277 // Not in java.base 278 archivedObjects = new Object[1]; 279 archivedObjects[0] = java.util.logging.Level.OFF; 280 } 281 } 282 283 class CDSTestClassG { 284 static Object[] archivedObjects; 285 static { 286 CDS.initializeFromArchive(CDSTestClassG.class); 287 if (archivedObjects == null) { 288 archivedObjects = new Object[4]; 289 archivedObjects[0] = sun.invoke.util.Wrapper.BOOLEAN; 290 archivedObjects[1] = sun.invoke.util.Wrapper.INT.zero(); 291 archivedObjects[2] = sun.invoke.util.Wrapper.DOUBLE.zero(); 292 archivedObjects[3] = MyEnum.DUMMY1; 293 } else { 294 System.out.println("Initialized from CDS"); 295 } 296 } 297 298 public static void main(String args[]) { 299 if (archivedObjects[0] != sun.invoke.util.Wrapper.BOOLEAN) { 300 throw new RuntimeException("Huh 1"); 301 } 302 303 if (archivedObjects[1] != sun.invoke.util.Wrapper.INT.zero()) { 304 throw new RuntimeException("Huh 2"); 305 } 306 307 Object MY_INT_ZERO = (Integer)(int)0; 308 if (archivedObjects[1] != MY_INT_ZERO) { 309 throw new RuntimeException("Huh 3"); 310 } 311 312 if (archivedObjects[2] != sun.invoke.util.Wrapper.DOUBLE.zero()) { 313 throw new RuntimeException("Huh 4"); 314 } 315 316 if (false) { 317 Object MY_DOUBLE_ZERO = (Double)(double)0; 318 if (archivedObjects[1] != MY_DOUBLE_ZERO) { 319 throw new RuntimeException("Huh 5"); 320 } 321 } 322 323 if (MyEnum.BOOLEAN != true) { 324 throw new RuntimeException("Huh 10.1"); 325 } 326 if (MyEnum.BYTE != -128) { 327 throw new RuntimeException("Huh 10.2"); 328 } 329 if (MyEnum.CHAR != 'c') { 330 throw new RuntimeException("Huh 10.3"); 331 } 332 if (MyEnum.SHORT != -12345) { 333 throw new RuntimeException("Huh 10.4"); 334 } 335 if (MyEnum.INT != -123456) { 336 throw new RuntimeException("Huh 10.5"); 337 } 338 if (MyEnum.LONG != 0x1234567890L) { 339 throw new RuntimeException("Huh 10.6"); 340 } 341 if (MyEnum.LONG2 != -0x1234567890L) { 342 throw new RuntimeException("Huh 10.7"); 343 } 344 if (MyEnum.FLOAT != 567891.0f) { 345 throw new RuntimeException("Huh 10.8"); 346 } 347 if (MyEnum.DOUBLE != 12345678905678.890) { 348 throw new RuntimeException("Huh 10.9"); 349 } 350 351 System.out.println("Success!"); 352 } 353 354 enum MyEnum { 355 DUMMY1, 356 DUMMY2; 357 358 static final boolean BOOLEAN = true; 359 static final byte BYTE = -128; 360 static final short SHORT = -12345; 361 static final char CHAR = 'c'; 362 static final int INT = -123456; 363 static final long LONG = 0x1234567890L; 364 static final long LONG2 = -0x1234567890L; 365 static final float FLOAT = 567891.0f; 366 static final double DOUBLE = 12345678905678.890; 367 } 368 }