1 /* 2 * Copyright (c) 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 // AOT-linked classes are loaded during VM bootstrap by the C++ class AOTLinkedClassBulkLoader. 26 // Make sure that the Module, Package, CodeSource and ProtectionDomain of these classes are 27 // set up properly. 28 29 /* 30 * @test id=static 31 * @requires vm.cds.supports.aot.class.linking 32 * @library /test/jdk/lib/testlibrary /test/lib 33 * @build InitiatingLoaderTester 34 * @build BulkLoaderTest 35 * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar BulkLoaderTestApp.jar BulkLoaderTestApp MyUtil InitiatingLoaderTester 36 * @run driver BulkLoaderTest STATIC 37 */ 38 39 /* 40 * @test id=dynamic 41 * @requires vm.cds.supports.aot.class.linking 42 * @library /test/jdk/lib/testlibrary /test/lib 43 * @build InitiatingLoaderTester 44 * @build BulkLoaderTest 45 * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar BulkLoaderTestApp.jar BulkLoaderTestApp MyUtil InitiatingLoaderTester 46 * @run driver BulkLoaderTest DYNAMIC 47 */ 48 49 import java.io.File; 50 import java.lang.StackWalker.StackFrame; 51 import java.util.List; 52 import java.util.regex.Matcher; 53 import java.util.regex.Pattern; 54 import java.util.stream.Collectors; 55 import java.util.Set; 56 import jdk.test.lib.cds.CDSAppTester; 57 import jdk.test.lib.helpers.ClassFileInstaller; 58 import jdk.test.lib.process.OutputAnalyzer; 59 60 public class BulkLoaderTest { 61 static final String appJar = ClassFileInstaller.getJarPath("BulkLoaderTestApp.jar"); 62 static final String mainClass = "BulkLoaderTestApp"; 63 64 public static void main(String[] args) throws Exception { 65 test(args, true); 66 if (args[0].equals("STATIC")) { 67 test(args, false); 68 } 69 } 70 71 static void test(String[] args, boolean archivePackageOopssAndProtectionDomains) throws Exception { 72 Tester t = new Tester(archivePackageOopssAndProtectionDomains); 73 74 // Run with archived FMG loaded 75 t.run(args); 76 77 // Run with an extra classpath -- archived FMG can still load. 78 { 79 String extraVmArgs[] = { 80 "-cp", 81 appJar + File.pathSeparator + "foobar.jar" 82 }; 83 OutputAnalyzer out = t.productionRun(extraVmArgs); 84 out.shouldHaveExitValue(0); 85 } 86 87 // Run without archived FMG -- fail to load 88 { 89 String extraVmArgs[] = { 90 "-Xshare:on", 91 "-Xlog:cds", 92 "-Djdk.module.showModuleResolution=true" 93 }; 94 t.setCheckExitValue(false); 95 OutputAnalyzer out = t.productionRun(extraVmArgs); 96 out.shouldHaveExitValue(1); 97 out.shouldContain("CDS archive has aot-linked classes. It cannot be used when archived full module graph is not used."); 98 t.setCheckExitValue(true); 99 } 100 } 101 102 static class Tester extends CDSAppTester { 103 boolean archivePackageOopssAndProtectionDomains; 104 public Tester(boolean archivePackageOopssAndProtectionDomains) { 105 super(mainClass); 106 this.archivePackageOopssAndProtectionDomains = archivePackageOopssAndProtectionDomains; 107 } 108 109 @Override 110 public String classpath(RunMode runMode) { 111 return appJar; 112 } 113 114 @Override 115 public String[] vmArgs(RunMode runMode) { 116 String which = archivePackageOopssAndProtectionDomains ? "+" : "-"; 117 return new String[] { 118 "-Xlog:cds,cds+aot+load", 119 "-XX:+AOTClassLinking", 120 "-XX:" + which + "ArchivePackages", 121 "-XX:" + which + "ArchiveProtectionDomains" 122 }; 123 } 124 125 @Override 126 public String[] appCommandLine(RunMode runMode) { 127 return new String[] { 128 mainClass, 129 }; 130 } 131 } 132 } 133 134 class BulkLoaderTestApp { 135 static String allPerms = "null.*<no principals>.*java.security.Permissions.*,*java.security.AllPermission.*<all permissions>.*<all actions>"; 136 137 public static void main(String args[]) throws Exception { 138 checkClasses(); 139 checkInitiatingLoader(); 140 } 141 142 // Check the ClassLoader/Module/Package/ProtectionDomain/CodeSource of classes that are aot-linked 143 static void checkClasses() throws Exception { 144 check(String.class, 145 "null", // loader 146 "module java.base", 147 "package java.lang", 148 "null", 149 allPerms); 150 151 check(Class.forName("sun.util.logging.internal.LoggingProviderImpl"), 152 "null", 153 "module java.logging", 154 "package sun.util.logging.internal", 155 "null", 156 allPerms); 157 158 159 check(javax.tools.FileObject.class, 160 "^jdk.internal.loader.ClassLoaders[$]PlatformClassLoader@", 161 "module java.compiler", 162 "package javax.tools", 163 "jrt:/java.compiler <no signer certificates>", 164 "jdk.internal.loader.ClassLoaders[$]PlatformClassLoader.*<no principals>.*java.security.Permissions.*" 165 + "java.lang.RuntimePermission.*accessSystemModules"); 166 167 check(BulkLoaderTestApp.class, 168 "jdk.internal.loader.ClassLoaders[$]AppClassLoader@", 169 "^unnamed module @", 170 "package ", 171 "file:.*BulkLoaderTestApp.jar <no signer certificates>", 172 "jdk.internal.loader.ClassLoaders[$]AppClassLoader.*<no principals>.*java.security.Permissions.*" 173 + "java.io.FilePermission.*BulkLoaderTestApp.jar.*read"); 174 175 check(Class.forName("com.sun.tools.javac.Main"), 176 "jdk.internal.loader.ClassLoaders[$]AppClassLoader@", 177 "module jdk.compiler", 178 "package com.sun.tools.javac", 179 "jrt:/jdk.compiler <no signer certificates>", 180 "jdk.internal.loader.ClassLoaders[$]AppClassLoader.*<no principals>.*java.security.Permissions.*" 181 + "java.lang.RuntimePermission.*accessSystemModules"); 182 183 /* 184 FIXME - not working with -XX:+AOTInvokeDynamicLinking 185 186 doit(() -> { 187 Class<?> lambdaClass = MyUtil.getCallerClass(1); 188 check(lambdaClass, 189 "jdk.internal.loader.ClassLoaders[$]AppClassLoader@", 190 "unnamed module", 191 "package ", 192 "file:.*BulkLoaderTestApp.jar <no signer certificates>", 193 "jdk.internal.loader.ClassLoaders[$]AppClassLoader.*<no principals>.*java.security.Permissions.*" 194 + "java.io.FilePermission.*BulkLoaderTestApp.jar.*read"); 195 196 }); 197 */ 198 } 199 200 static void check(Class c, String loader, String module, String pkg, String codeSource, String protectionDomain) { 201 System.out.println("===================================================================="); 202 System.out.println(c.getName() + ", loader = " + c.getClassLoader()); 203 System.out.println(c.getName() + ", module = " + c.getModule()); 204 System.out.println(c.getName() + ", package = " + c.getPackage()); 205 System.out.println(c.getName() + ", CS = " + c.getProtectionDomain().getCodeSource()); 206 System.out.println(c.getName() + ", PD = " + c.getProtectionDomain()); 207 208 expectMatch("" + c.getClassLoader(), loader); 209 expectMatch("" + c.getModule(), module); 210 expectSame("" + c.getPackage(), pkg); 211 expectMatch("" + c.getProtectionDomain().getCodeSource(), codeSource); 212 expectMatch("" + c.getProtectionDomain(), protectionDomain); 213 } 214 215 static void expectSame(String a, String b) { 216 if (!a.equals(b)) { 217 throw new RuntimeException("Expected \"" + b + "\" but got \"" + a + "\""); 218 } 219 } 220 static void expectMatch(String string, String pattern) { 221 Matcher matcher = Pattern.compile(pattern, Pattern.DOTALL).matcher(string); 222 if (!matcher.find()) { 223 throw new RuntimeException("Expected pattern \"" + pattern + "\" but got \"" + string + "\""); 224 } 225 } 226 227 static void doit(Runnable t) { 228 t.run(); 229 } 230 231 static void checkInitiatingLoader() throws Exception { 232 try { 233 InitiatingLoaderTester.tryAccess(); 234 } catch (IllegalAccessError t) { 235 if (t.getMessage().contains("cannot access class jdk.internal.misc.Unsafe (in module java.base)")) { 236 System.out.println("Expected exception:"); 237 t.printStackTrace(System.out); 238 // Class.forName() should still work. We just can't resolve it in CP entries. 239 Class<?> c = Class.forName("jdk.internal.misc.Unsafe"); 240 System.out.println("App loader can still resolve by name: " + c); 241 return; 242 } 243 throw new RuntimeException("Unexpected exception", t); 244 } 245 246 throw new RuntimeException("Should not have succeeded"); 247 } 248 } 249 250 class MyUtil { 251 // depth is 0-based -- i.e., depth==0 returns the class of the immediate caller of getCallerClass 252 static Class<?> getCallerClass(int depth) { 253 // Need to add the frame of the getCallerClass -- so the immediate caller (depth==0) of this method 254 // is at stack.get(1) == stack.get(depth+1); 255 StackWalker walker = StackWalker.getInstance( 256 Set.of(StackWalker.Option.RETAIN_CLASS_REFERENCE, 257 StackWalker.Option.SHOW_HIDDEN_FRAMES)); 258 List<StackFrame> stack = walker.walk(s -> s.limit(depth+2).collect(Collectors.toList())); 259 return stack.get(depth+1).getDeclaringClass(); 260 } 261 }