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