From bbec3c0730df4578cee5cbc9dab40810ff093966 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Thu, 24 Apr 2025 00:00:36 +0000 Subject: [PATCH] 8354558: -XX:AOTMode=record crashes with boot loader package-info class Reviewed-by: ccheung, matsaave --- src/hotspot/share/classfile/classLoader.cpp | 20 +++-- .../cds/appcds/aotCache/PackageInfoClass.java | 73 +++++++++++++++++++ test/lib/jdk/test/lib/cds/CDSAppTester.java | 2 +- .../jdk/test/lib/cds/SimpleCDSAppTester.java | 33 +++++++-- 4 files changed, 114 insertions(+), 14 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/PackageInfoClass.java diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index a7d6cc39614..d7660647fb2 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -1219,6 +1219,7 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik, // must be valid since the class has been successfully parsed. const char* path = ClassLoader::uri_to_path(src); assert(path != nullptr, "sanity"); + bool found_invalid = false; AOTClassLocationConfig::dumptime_iterate([&] (AOTClassLocation* cl) { int i = cl->index(); // for index 0 and the stream->source() is the modules image or has the jrt: protocol. @@ -1242,10 +1243,15 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik, classpath_index = i; } else { if (cl->from_boot_classpath()) { - // The class must be from boot loader append path which consists of - // -Xbootclasspath/a and jvmti appended entries. - assert(loader == nullptr, "sanity"); - classpath_index = i; + if (loader != nullptr) { + // Probably loaded by jdk/internal/loader/ClassLoaders$BootClassLoader. Don't archive + // such classes. + ik->set_shared_classpath_index(-1); + ik->set_shared_class_loader_type(ClassLoader::BOOT_LOADER); + found_invalid = true; + } else { + classpath_index = i; + } } } } else { @@ -1256,13 +1262,17 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik, } } } - if (classpath_index >= 0) { + if (classpath_index >= 0 || found_invalid) { return false; // quit iterating } else { return true; // Keep iterating } }); + if (found_invalid) { + return; + } + // No path entry found for this class: most likely a shared class loaded by the // user defined classloader. if (classpath_index < 0 && !SystemDictionaryShared::is_builtin_loader(ik->class_loader_data())) { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/PackageInfoClass.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/PackageInfoClass.java new file mode 100644 index 00000000000..0fff74236f1 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/PackageInfoClass.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/* + * @test + * @summary AOT cache handling for package-info class loaded by jdk/internal/loader/ClassLoaders$BootClassLoader + * @bug 8354558 + * @requires vm.cds.supports.aot.class.linking + * @comment work around JDK-8345635 + * @requires !vm.jvmci.enabled + * @library /test/lib /test/jdk/java/lang/Package/bootclasspath/boot + * @build PackageInfoClass foo.Foo foo.MyAnnotation foo.package-info + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar boot.jar foo.Foo foo.package-info foo.MyAnnotation + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar PackageInfoClassApp + * @run driver PackageInfoClass AOT + */ + +import java.lang.annotation.Annotation; +import java.util.Arrays; +import jdk.test.lib.cds.CDSAppTester.RunMode; +import jdk.test.lib.cds.SimpleCDSAppTester; +import jdk.test.lib.process.OutputAnalyzer; + +public class PackageInfoClass { + public static void main(String... args) throws Exception { + SimpleCDSAppTester.of("PackageInfoClass") + .classpath("app.jar") + .addVmArgs("-Xbootclasspath/a:boot.jar") + .appCommandLine("PackageInfoClassApp") + .setAssemblyChecker((OutputAnalyzer out, RunMode runMode) -> { + if (runMode == RunMode.TRAINING) { + out.shouldContain("Skipping foo/package-info: Unsupported location"); + } + }) + .runAOTWorkflow(); + } +} + +class PackageInfoClassApp { + public static void main(String[] args) throws Exception { + // This code is taken from test/jdk/java/lang/Package/bootclasspath/GetPackageFromBootClassPath.java + Class c = Class.forName("foo.Foo", false, null); + Package p = c.getPackage(); + Annotation[] annotations = p.getAnnotations(); + Class annType = Class.forName("foo.MyAnnotation", false, null); + if (annotations.length != 1 || + annotations[0].annotationType() != annType) { + throw new RuntimeException("Expected foo.MyAnnotation but got " + + Arrays.toString(annotations)); + } + } +} diff --git a/test/lib/jdk/test/lib/cds/CDSAppTester.java b/test/lib/jdk/test/lib/cds/CDSAppTester.java index f08cbf0e7e2..6ebf73f5f08 100644 --- a/test/lib/jdk/test/lib/cds/CDSAppTester.java +++ b/test/lib/jdk/test/lib/cds/CDSAppTester.java @@ -86,7 +86,7 @@ abstract public class CDSAppTester { } public enum RunMode { - TRAINING, // -XX:DumpLoadedClassList OR {-XX:AOTMode=create -XX:AOTConfiguration} + TRAINING, // -XX:DumpLoadedClassList OR {-XX:AOTMode=record -XX:AOTConfiguration} DUMP_STATIC, // -Xshare:dump DUMP_DYNAMIC, // -XX:ArchiveClassesArExit ASSEMBLY, // JEP 483 (assembly phase, app logic not executed) diff --git a/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.java b/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.java index 33d984ed505..16666676a2f 100644 --- a/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.java +++ b/test/lib/jdk/test/lib/cds/SimpleCDSAppTester.java @@ -24,11 +24,13 @@ package jdk.test.lib.cds; import java.io.File; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import jdk.test.lib.cds.CDSAppTester.RunMode; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.StringArrayUtils; -import java.util.function.Consumer; - /* * A simpler way to use CDSAppTester. Example: * @@ -49,8 +51,8 @@ import java.util.function.Consumer; */ public class SimpleCDSAppTester { private String name; - private Consumer assemblyChecker; - private Consumer productionChecker; + private BiConsumer assemblyChecker; + private BiConsumer productionChecker; private String classpath; private String modulepath; private String[] appCommandLine; @@ -98,16 +100,31 @@ public class SimpleCDSAppTester { return this; } - public SimpleCDSAppTester setAssemblyChecker(Consumer checker) { + public SimpleCDSAppTester setAssemblyChecker(BiConsumer checker) { this.assemblyChecker = checker; return this; } - public SimpleCDSAppTester setProductionChecker(Consumer checker) { + public SimpleCDSAppTester setProductionChecker(BiConsumer checker) { this.productionChecker = checker; return this; } + + public SimpleCDSAppTester setAssemblyChecker(Consumer checker) { + this.assemblyChecker = (OutputAnalyzer out, RunMode runMode) -> { + checker.accept(out); + }; + return this; + } + + public SimpleCDSAppTester setProductionChecker(Consumer checker) { + this.productionChecker = (OutputAnalyzer out, RunMode runMode) -> { + checker.accept(out); + }; + return this; + } + class Tester extends CDSAppTester { public Tester(String name) { super(name); @@ -137,11 +154,11 @@ public class SimpleCDSAppTester { public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { if (isDumping(runMode) && runMode != RunMode.TRAINING) { if (assemblyChecker != null) { - assemblyChecker.accept(out); + assemblyChecker.accept(out, runMode); } } else if (runMode.isProductionRun()) { if (productionChecker != null) { - productionChecker.accept(out); + productionChecker.accept(out, runMode); } } }