Skip to content

Commit 5e50a58

Browse files
committed
8356838: AOT incorrectly sets a cached class's loader type to boot
Reviewed-by: ccheung, iveresov
1 parent b66ab8e commit 5e50a58

8 files changed

Lines changed: 181 additions & 27 deletions

File tree

src/hotspot/share/cds/aotClassLocation.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,3 +1035,38 @@ bool AOTClassLocationConfig::validate(bool has_aot_linked_classes, bool* has_ext
10351035
}
10361036
return success;
10371037
}
1038+
1039+
void AOTClassLocationConfig::print() {
1040+
if (CDSConfig::is_dumping_archive()) {
1041+
tty->print_cr("AOTClassLocationConfig::_dumptime_instance = %p", _dumptime_instance);
1042+
if (_dumptime_instance != nullptr) {
1043+
_dumptime_instance->print_on(tty);
1044+
}
1045+
}
1046+
if (CDSConfig::is_using_archive()) {
1047+
tty->print_cr("AOTClassLocationConfig::_runtime_instance = %p", _runtime_instance);
1048+
if (_runtime_instance != nullptr) {
1049+
_runtime_instance->print_on(tty);
1050+
}
1051+
}
1052+
}
1053+
1054+
void AOTClassLocationConfig::print_on(outputStream* st) const {
1055+
int n = class_locations()->length();
1056+
for (int i = 0; i < n; i++) {
1057+
const AOTClassLocation* cs = class_location_at(i);
1058+
const char* path;
1059+
if (i == 0) {
1060+
path = ClassLoader::get_jrt_entry()->name();
1061+
} else {
1062+
path = cs->path();
1063+
}
1064+
st->print_cr("[%d] = %s", i, path);
1065+
if (i == boot_cp_end_index() && i < n) {
1066+
st->print_cr("--- end of boot");
1067+
}
1068+
if (i == app_cp_end_index() && i < n) {
1069+
st->print_cr("--- end of app");
1070+
}
1071+
}
1072+
}

src/hotspot/share/cds/aotClassLocation.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "utilities/growableArray.hpp"
3232
#include "utilities/globalDefinitions.hpp"
3333
#include "utilities/macros.hpp"
34+
#include "utilities/ostream.hpp"
3435

3536
class AllClassLocationStreams;
3637
class ClassLocationStream;
@@ -201,6 +202,9 @@ class AOTClassLocationConfig : public CHeapObj<mtClassShared> {
201202
void print_dumptime_classpath(LogStream& ls, int index_start, int index_limit,
202203
bool do_substitute, size_t remove_prefix_len,
203204
const char* prepend, size_t prepend_len) const;
205+
206+
void print_on(outputStream* st) const;
207+
204208
public:
205209
static AOTClassLocationConfig* dumptime() {
206210
assert(_dumptime_instance != nullptr, "can only be called when dumping an AOT cache");
@@ -268,6 +272,8 @@ class AOTClassLocationConfig : public CHeapObj<mtClassShared> {
268272
bool validate(bool has_aot_linked_classes, bool* has_extra_module_paths) const;
269273

270274
bool is_valid_classpath_index(int classpath_index, InstanceKlass* ik);
275+
276+
static void print();
271277
};
272278

273279

src/hotspot/share/classfile/classLoader.cpp

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,10 +1200,20 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik,
12001200
return;
12011201
}
12021202

1203+
if (!SystemDictionaryShared::is_builtin_loader(ik->class_loader_data())) {
1204+
// A class loaded by a user-defined classloader.
1205+
assert(ik->shared_classpath_index() < 0, "not assigned yet");
1206+
ik->set_shared_classpath_index(UNREGISTERED_INDEX);
1207+
SystemDictionaryShared::set_shared_class_misc_info(ik, (ClassFileStream*)stream);
1208+
return;
1209+
}
1210+
12031211
assert(has_jrt_entry(), "CDS dumping does not support exploded JDK build");
12041212

12051213
ResourceMark rm(current);
12061214
int classpath_index = -1;
1215+
bool found_invalid = false;
1216+
12071217
PackageEntry* pkg_entry = ik->package();
12081218

12091219
if (!AOTClassLocationConfig::dumptime_is_ready()) {
@@ -1218,7 +1228,6 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik,
12181228
// must be valid since the class has been successfully parsed.
12191229
const char* path = ClassLoader::uri_to_path(src);
12201230
assert(path != nullptr, "sanity");
1221-
bool found_invalid = false;
12221231
AOTClassLocationConfig::dumptime_iterate([&] (AOTClassLocation* cl) {
12231232
int i = cl->index();
12241233
// for index 0 and the stream->source() is the modules image or has the jrt: protocol.
@@ -1245,8 +1254,6 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik,
12451254
if (loader != nullptr) {
12461255
// Probably loaded by jdk/internal/loader/ClassLoaders$BootClassLoader. Don't archive
12471256
// such classes.
1248-
ik->set_shared_classpath_index(-1);
1249-
ik->set_shared_class_loader_type(ClassLoader::BOOT_LOADER);
12501257
found_invalid = true;
12511258
} else {
12521259
classpath_index = i;
@@ -1262,31 +1269,22 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik,
12621269
}
12631270
}
12641271
if (classpath_index >= 0 || found_invalid) {
1265-
return false; // quit iterating
1272+
return false; // Break the AOTClassLocationConfig::dumptime_iterate() loop.
12661273
} else {
12671274
return true; // Keep iterating
12681275
}
12691276
});
1277+
}
12701278

1271-
if (found_invalid) {
1272-
return;
1273-
}
1274-
1275-
// No path entry found for this class: most likely a shared class loaded by the
1276-
// user defined classloader.
1277-
if (classpath_index < 0 && !SystemDictionaryShared::is_builtin_loader(ik->class_loader_data())) {
1278-
assert(ik->shared_classpath_index() < 0, "not assigned yet");
1279-
ik->set_shared_classpath_index(UNREGISTERED_INDEX);
1280-
SystemDictionaryShared::set_shared_class_misc_info(ik, (ClassFileStream*)stream);
1281-
return;
1282-
}
1279+
if (found_invalid) {
1280+
assert(classpath_index == -1, "sanity");
12831281
}
12841282

12851283
const char* const class_name = ik->name()->as_C_string();
12861284
const char* const file_name = file_name_for_class_name(class_name,
12871285
ik->name()->utf8_length());
12881286
assert(file_name != nullptr, "invariant");
1289-
ClassLoaderExt::record_result(checked_cast<s2>(classpath_index), ik, redefined);
1287+
ClassLoaderExt::record_result_for_builtin_loader(checked_cast<s2>(classpath_index), ik, redefined);
12901288
}
12911289

12921290
void ClassLoader::record_hidden_class(InstanceKlass* ik) {

src/hotspot/share/classfile/classLoaderExt.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,20 @@ int ClassLoaderExt::compare_module_names(const char** p1, const char** p2) {
6767
return strcmp(*p1, *p2);
6868
}
6969

70-
void ClassLoaderExt::record_result(s2 classpath_index, InstanceKlass* result, bool redefined) {
70+
void ClassLoaderExt::record_result_for_builtin_loader(s2 classpath_index, InstanceKlass* result, bool redefined) {
7171
assert(CDSConfig::is_dumping_archive(), "sanity");
7272

73-
// We need to remember where the class comes from during dumping.
7473
oop loader = result->class_loader();
75-
s2 classloader_type = ClassLoader::BOOT_LOADER;
74+
s2 classloader_type;
7675
if (SystemDictionary::is_system_class_loader(loader)) {
7776
classloader_type = ClassLoader::APP_LOADER;
7877
AOTClassLocationConfig::dumptime_set_has_app_classes();
7978
} else if (SystemDictionary::is_platform_class_loader(loader)) {
8079
classloader_type = ClassLoader::PLATFORM_LOADER;
8180
AOTClassLocationConfig::dumptime_set_has_platform_classes();
81+
} else {
82+
precond(loader == nullptr);
83+
classloader_type = ClassLoader::BOOT_LOADER;
8284
}
8385

8486
if (CDSConfig::is_dumping_preimage_static_archive() || CDSConfig::is_dumping_dynamic_archive()) {

src/hotspot/share/classfile/classLoaderExt.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class ClassLoaderExt: public ClassLoader { // AllStatic
4040
static void append_boot_classpath(ClassPathEntry* new_entry);
4141

4242
static int compare_module_names(const char** p1, const char** p2);
43-
static void record_result(s2 classpath_index, InstanceKlass* result, bool redefined);
43+
static void record_result_for_builtin_loader(s2 classpath_index, InstanceKlass* result, bool redefined);
4444
#endif // INCLUDE_CDS
4545
};
4646

test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCacheSupportForCustomLoaders.java

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
/*
2626
* @test
2727
* @summary Test AOT cache support for array classes in custom class loaders.
28-
* @bug 8353298
28+
* @bug 8353298 8356838
2929
* @requires vm.cds.supports.aot.class.linking
3030
* @comment work around JDK-8345635
3131
* @requires !vm.jvmci.enabled
@@ -37,20 +37,36 @@
3737
* @run driver AOTCacheSupportForCustomLoaders AOT
3838
*/
3939

40+
import java.lang.module.Configuration;
41+
import java.lang.module.ModuleFinder;
4042
import java.net.URL;
4143
import java.net.URLClassLoader;
4244
import java.io.File;
45+
import java.nio.file.Path;
46+
import java.nio.file.Paths;
47+
import java.util.Set;
48+
import jdk.test.lib.cds.CDSJarUtils;
49+
import jdk.test.lib.cds.CDSModulePackager;
4350
import jdk.test.lib.cds.SimpleCDSAppTester;
4451
import jdk.test.lib.process.OutputAnalyzer;
4552

4653
public class AOTCacheSupportForCustomLoaders {
54+
static final Path SRC = Paths.get(System.getProperty("test.src")).resolve("modules");
55+
4756
public static void main(String... args) throws Exception {
57+
CDSModulePackager modulePackager = new CDSModulePackager(SRC);
58+
String modulePath = modulePackager.getOutputDir().toString();
59+
modulePackager.createModularJar("com.test");
60+
4861
SimpleCDSAppTester.of("AOTCacheSupportForCustomLoaders")
4962
.classpath("app.jar")
50-
.addVmArgs("-Xlog:cds+class=debug", "-Xlog:cds")
51-
.appCommandLine("AppWithCustomLoaders")
63+
.addVmArgs("-Xlog:cds+class=debug", "-Xlog:cds",
64+
"--module-path=" + modulePath,
65+
"--add-modules=com.test")
66+
.appCommandLine("AppWithCustomLoaders", modulePath)
5267
.setAssemblyChecker((OutputAnalyzer out) -> {
5368
out.shouldMatch("cds,class.*unreg AppWithCustomLoaders[$]MyLoadeeA")
69+
.shouldMatch("cds,class.*unreg com.test.Foo")
5470
.shouldMatch("cds,class.*array \\[LAppWithCustomLoaders[$]MyLoadeeA;")
5571
.shouldNotMatch("cds,class.* ReturnIntegerAsString");
5672
})
@@ -67,11 +83,21 @@ public static void main(String args[]) throws Exception {
6783
URL[] urls = new URL[] {custJar.toURI().toURL()};
6884
MyLoader loader = new MyLoader(urls, AppWithCustomLoaders.class.getClassLoader());
6985

70-
// Test 1: array class of MyLoadeeA (JDK-8353298)
86+
test1(loader);
87+
test2(loader);
88+
test3(args[0]);
89+
90+
// TODO: more test cases JDK-8354557
91+
}
92+
93+
// Test 1: array class of MyLoadeeA (JDK-8353298)
94+
static void test1(MyLoader loader) throws Exception {
7195
Class klass = loader.loadClass("AppWithCustomLoaders$MyLoadeeA");
7296
klass.newInstance();
97+
}
7398

74-
// Test 2: VerificationType::is_reference_assignable_from() cannot be skipped (JDK-8356407)
99+
// Test 2: VerificationType::is_reference_assignable_from() cannot be skipped (JDK-8356407)
100+
static void test2(MyLoader loader) throws Exception {
75101
try {
76102
Class bad = loader.loadClass("ReturnIntegerAsString");
77103
Object o = bad.newInstance(); // force verification
@@ -80,8 +106,41 @@ public static void main(String args[]) throws Exception {
80106
} catch (VerifyError ve) {
81107
System.out.println("Expected: " + ve);
82108
}
109+
}
83110

84-
// TODO: more test cases JDK-8354557
111+
// Test 3: custom loader defines a class from the exact location as a class defined in the boot layer.
112+
static void test3(String modulePath) throws Exception {
113+
Class<?> c0 = Class.forName("com.test.Foo");
114+
System.out.println(c0);
115+
System.out.println(System.identityHashCode(c0.getModule()));
116+
System.out.println(c0.getModule().getName());
117+
System.out.println(c0.getClassLoader());
118+
119+
// Regression test for JDK-8356838
120+
//
121+
// We create a new layer that loads the com.test module from the modulePath into
122+
// a different class loader.
123+
ModuleFinder finder = ModuleFinder.of(Paths.get(modulePath));
124+
ModuleLayer parent = ModuleLayer.boot();
125+
Configuration cf = parent.configuration().resolve(finder, ModuleFinder.of(), Set.of("com.test"));
126+
ClassLoader scl = ClassLoader.getSystemClassLoader();
127+
ModuleLayer layer = parent.defineModulesWithOneLoader(cf, scl);
128+
Class<?> c1 = layer.findLoader("com.test").loadClass("com.test.Foo");
129+
130+
System.out.println(c1);
131+
System.out.println(System.identityHashCode(c1.getModule()));
132+
System.out.println(c1.getModule().getName());
133+
System.out.println(c1.getClassLoader());
134+
135+
if (!c1.getModule().getName().equals("com.test")) {
136+
throw new RuntimeException("Unexpected module: " + c1.getModule());
137+
}
138+
if (c1.getModule() == c0.getModule()) {
139+
throw new RuntimeException("Unexpected module: " + c1.getModule());
140+
}
141+
if (c1.getClassLoader() == c0.getClassLoader()) {
142+
throw new RuntimeException("Unexpected class loader: " + c1.getClassLoader());
143+
}
85144
}
86145

87146
public static class MyLoader extends URLClassLoader {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (c) 2025, 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+
package com.test;
26+
27+
public class Foo {}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (c) 2025, 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+
module com.test {
26+
exports com.test;
27+
}

0 commit comments

Comments
 (0)