Skip to content

Commit 7f16a08

Browse files
committedJan 23, 2025
8348240: Remove SystemDictionaryShared::lookup_super_for_unregistered_class()
Reviewed-by: ccheung, coleenp
1 parent 48ece07 commit 7f16a08

File tree

10 files changed

+231
-153
lines changed

10 files changed

+231
-153
lines changed
 

‎src/hotspot/share/cds/classListParser.cpp

+31-44
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "jvm.h"
4343
#include "logging/log.hpp"
4444
#include "logging/logTag.hpp"
45+
#include "memory/oopFactory.hpp"
4546
#include "memory/resourceArea.hpp"
4647
#include "oops/constantPool.inline.hpp"
4748
#include "runtime/atomic.hpp"
@@ -111,6 +112,12 @@ ClassListParser::~ClassListParser() {
111112
_instance = nullptr;
112113
}
113114

115+
void ClassListParser::parse_classlist(const char* classlist_path, ParseMode parse_mode, TRAPS) {
116+
UnregisteredClasses::initialize(CHECK);
117+
ClassListParser parser(classlist_path, parse_mode);
118+
parser.parse(THREAD);
119+
}
120+
114121
void ClassListParser::parse(TRAPS) {
115122
for (; !_input_stream.done(); _input_stream.next()) {
116123
_line = _input_stream.current_line();
@@ -387,6 +394,19 @@ bool ClassListParser::parse_uint_option(const char* option_name, int* value) {
387394
return false;
388395
}
389396

397+
objArrayOop ClassListParser::get_specified_interfaces(TRAPS) {
398+
const int n = _interfaces->length();
399+
if (n == 0) {
400+
return nullptr;
401+
} else {
402+
objArrayOop array = oopFactory::new_objArray(vmClasses::Class_klass(), n, CHECK_NULL);
403+
for (int i = 0; i < n; i++) {
404+
array->obj_at_put(i, lookup_class_by_id(_interfaces->at(i))->java_mirror());
405+
}
406+
return array;
407+
}
408+
}
409+
390410
void ClassListParser::print_specified_interfaces() {
391411
const int n = _interfaces->length();
392412
jio_fprintf(defaultStream::error_stream(), "Currently specified interfaces[%d] = {\n", n);
@@ -514,7 +534,17 @@ InstanceKlass* ClassListParser::load_class_from_source(Symbol* class_name, TRAPS
514534

515535
ResourceMark rm;
516536
char * source_path = os::strdup_check_oom(ClassLoader::uri_to_path(_source));
517-
InstanceKlass* k = UnregisteredClasses::load_class(class_name, source_path, CHECK_NULL);
537+
InstanceKlass* specified_super = lookup_class_by_id(_super);
538+
Handle super_class(THREAD, specified_super->java_mirror());
539+
objArrayOop r = get_specified_interfaces(CHECK_NULL);
540+
objArrayHandle interfaces(THREAD, r);
541+
InstanceKlass* k = UnregisteredClasses::load_class(class_name, source_path,
542+
super_class, interfaces, CHECK_NULL);
543+
if (k->java_super() != specified_super) {
544+
error("The specified super class %s (id %d) does not match actual super class %s",
545+
specified_super->external_name(), _super,
546+
k->java_super()->external_name());
547+
}
518548
if (k->local_interfaces()->length() != _interfaces->length()) {
519549
print_specified_interfaces();
520550
print_actual_interfaces(k);
@@ -734,49 +764,6 @@ InstanceKlass* ClassListParser::lookup_class_by_id(int id) {
734764
return *klass_ptr;
735765
}
736766

737-
738-
InstanceKlass* ClassListParser::lookup_super_for_current_class(Symbol* super_name) {
739-
if (!is_loading_from_source()) {
740-
return nullptr;
741-
}
742-
743-
InstanceKlass* k = lookup_class_by_id(super());
744-
if (super_name != k->name()) {
745-
error("The specified super class %s (id %d) does not match actual super class %s",
746-
k->name()->as_klass_external_name(), super(),
747-
super_name->as_klass_external_name());
748-
}
749-
return k;
750-
}
751-
752-
InstanceKlass* ClassListParser::lookup_interface_for_current_class(Symbol* interface_name) {
753-
if (!is_loading_from_source()) {
754-
return nullptr;
755-
}
756-
757-
const int n = _interfaces->length();
758-
if (n == 0) {
759-
error("Class %s implements the interface %s, but no interface has been specified in the input line",
760-
_class_name, interface_name->as_klass_external_name());
761-
ShouldNotReachHere();
762-
}
763-
764-
int i;
765-
for (i=0; i<n; i++) {
766-
InstanceKlass* k = lookup_class_by_id(_interfaces->at(i));
767-
if (interface_name == k->name()) {
768-
return k;
769-
}
770-
}
771-
772-
// interface_name is not specified by the "interfaces:" keyword.
773-
print_specified_interfaces();
774-
error("The interface %s implemented by class %s does not match any of the specified interface IDs",
775-
interface_name->as_klass_external_name(), _class_name);
776-
ShouldNotReachHere();
777-
return nullptr;
778-
}
779-
780767
InstanceKlass* ClassListParser::find_builtin_class_helper(JavaThread* current, Symbol* class_name_symbol, oop class_loader_oop) {
781768
Handle class_loader(current, class_loader_oop);
782769
return SystemDictionary::find_instance_klass(current, class_name_symbol, class_loader);

‎src/hotspot/share/cds/classListParser.hpp

+3-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -137,12 +137,10 @@ class ClassListParser : public StackObj {
137137
void print_diagnostic_info(outputStream* st, const char* msg, ...) ATTRIBUTE_PRINTF(3, 0);
138138
void constant_pool_resolution_warning(const char* msg, ...) ATTRIBUTE_PRINTF(2, 0);
139139
void error(const char* msg, ...) ATTRIBUTE_PRINTF(2, 0);
140+
objArrayOop get_specified_interfaces(TRAPS);
140141

141142
public:
142-
static void parse_classlist(const char* classlist_path, ParseMode parse_mode, TRAPS) {
143-
ClassListParser parser(classlist_path, parse_mode);
144-
parser.parse(THREAD);
145-
}
143+
static void parse_classlist(const char* classlist_path, ParseMode parse_mode, TRAPS);
146144

147145
static bool is_parsing_thread();
148146
static ClassListParser* instance() {
@@ -201,12 +199,6 @@ class ClassListParser : public StackObj {
201199
}
202200

203201
bool is_loading_from_source();
204-
205-
// Look up the super or interface of the current class being loaded
206-
// (in this->load_current_class()).
207-
InstanceKlass* lookup_super_for_current_class(Symbol* super_name);
208-
InstanceKlass* lookup_interface_for_current_class(Symbol* interface_name);
209-
210202
static void populate_cds_indy_info(const constantPoolHandle &pool, int cp_index, CDSIndyInfo* cii, TRAPS);
211203
};
212204
#endif // SHARE_CDS_CLASSLISTPARSER_HPP

‎src/hotspot/share/cds/unregisteredClasses.cpp

+42-36
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
#include "classfile/classLoaderExt.hpp"
2929
#include "classfile/javaClasses.inline.hpp"
3030
#include "classfile/symbolTable.hpp"
31-
#include "classfile/systemDictionaryShared.hpp"
31+
#include "classfile/systemDictionary.hpp"
3232
#include "classfile/vmSymbols.hpp"
3333
#include "memory/oopFactory.hpp"
3434
#include "memory/resourceArea.hpp"
@@ -38,76 +38,82 @@
3838
#include "runtime/javaCalls.hpp"
3939
#include "services/threadService.hpp"
4040

41+
InstanceKlass* UnregisteredClasses::_UnregisteredClassLoader_klass = nullptr;
42+
43+
void UnregisteredClasses::initialize(TRAPS) {
44+
if (_UnregisteredClassLoader_klass == nullptr) {
45+
// no need for synchronization as this function is called single-threaded.
46+
Symbol* klass_name = SymbolTable::new_symbol("jdk/internal/misc/CDS$UnregisteredClassLoader");
47+
Klass* k = SystemDictionary::resolve_or_fail(klass_name, true, CHECK);
48+
_UnregisteredClassLoader_klass = InstanceKlass::cast(k);
49+
}
50+
}
51+
4152
// Load the class of the given name from the location given by path. The path is specified by
4253
// the "source:" in the class list file (see classListParser.cpp), and can be a directory or
4354
// a JAR file.
44-
InstanceKlass* UnregisteredClasses::load_class(Symbol* name, const char* path, TRAPS) {
55+
InstanceKlass* UnregisteredClasses::load_class(Symbol* name, const char* path,
56+
Handle super_class, objArrayHandle interfaces, TRAPS) {
4557
assert(name != nullptr, "invariant");
4658
assert(CDSConfig::is_dumping_static_archive(), "this function is only used with -Xshare:dump");
4759

4860
PerfClassTraceTime vmtimer(ClassLoader::perf_app_classload_time(),
4961
THREAD->get_thread_stat()->perf_timers_addr(),
5062
PerfClassTraceTime::CLASS_LOAD);
5163

64+
// Call CDS$UnregisteredClassLoader::load(String name, Class<?> superClass, Class<?>[] interfaces)
65+
Symbol* methodName = SymbolTable::new_symbol("load");
66+
Symbol* methodSignature = SymbolTable::new_symbol("(Ljava/lang/String;Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/Class;");
5267
Symbol* path_symbol = SymbolTable::new_symbol(path);
53-
Symbol* findClass = SymbolTable::new_symbol("findClass");
54-
Handle url_classloader = get_url_classloader(path_symbol, CHECK_NULL);
68+
Handle classloader = get_classloader(path_symbol, CHECK_NULL);
5569
Handle ext_class_name = java_lang_String::externalize_classname(name, CHECK_NULL);
5670

5771
JavaValue result(T_OBJECT);
58-
JavaCallArguments args(2);
59-
args.set_receiver(url_classloader);
72+
JavaCallArguments args(3);
73+
args.set_receiver(classloader);
6074
args.push_oop(ext_class_name);
75+
args.push_oop(super_class);
76+
args.push_oop(interfaces);
6177
JavaCalls::call_virtual(&result,
62-
vmClasses::URLClassLoader_klass(),
63-
findClass,
64-
vmSymbols::string_class_signature(),
78+
UnregisteredClassLoader_klass(),
79+
methodName,
80+
methodSignature,
6581
&args,
6682
CHECK_NULL);
6783
assert(result.get_type() == T_OBJECT, "just checking");
6884
oop obj = result.get_oop();
6985
return InstanceKlass::cast(java_lang_Class::as_Klass(obj));
7086
}
7187

72-
class URLClassLoaderTable : public ResourceHashtable<
88+
class UnregisteredClasses::ClassLoaderTable : public ResourceHashtable<
7389
Symbol*, OopHandle,
7490
137, // prime number
7591
AnyObj::C_HEAP> {};
7692

77-
static URLClassLoaderTable* _url_classloader_table = nullptr;
93+
static UnregisteredClasses::ClassLoaderTable* _classloader_table = nullptr;
7894

79-
Handle UnregisteredClasses::create_url_classloader(Symbol* path, TRAPS) {
95+
Handle UnregisteredClasses::create_classloader(Symbol* path, TRAPS) {
8096
ResourceMark rm(THREAD);
8197
JavaValue result(T_OBJECT);
8298
Handle path_string = java_lang_String::create_from_str(path->as_C_string(), CHECK_NH);
83-
JavaCalls::call_static(&result,
84-
vmClasses::jdk_internal_loader_ClassLoaders_klass(),
85-
vmSymbols::toFileURL_name(),
86-
vmSymbols::toFileURL_signature(),
87-
path_string, CHECK_NH);
88-
assert(result.get_type() == T_OBJECT, "just checking");
89-
oop url_h = result.get_oop();
90-
objArrayHandle urls = oopFactory::new_objArray_handle(vmClasses::URL_klass(), 1, CHECK_NH);
91-
urls->obj_at_put(0, url_h);
92-
93-
Handle url_classloader = JavaCalls::construct_new_instance(
94-
vmClasses::URLClassLoader_klass(),
95-
vmSymbols::url_array_classloader_void_signature(),
96-
urls, Handle(), CHECK_NH);
97-
return url_classloader;
99+
Handle classloader = JavaCalls::construct_new_instance(
100+
UnregisteredClassLoader_klass(),
101+
vmSymbols::string_void_signature(),
102+
path_string, CHECK_NH);
103+
return classloader;
98104
}
99105

100-
Handle UnregisteredClasses::get_url_classloader(Symbol* path, TRAPS) {
101-
if (_url_classloader_table == nullptr) {
102-
_url_classloader_table = new (mtClass)URLClassLoaderTable();
106+
Handle UnregisteredClasses::get_classloader(Symbol* path, TRAPS) {
107+
if (_classloader_table == nullptr) {
108+
_classloader_table = new (mtClass)ClassLoaderTable();
103109
}
104-
OopHandle* url_classloader_ptr = _url_classloader_table->get(path);
105-
if (url_classloader_ptr != nullptr) {
106-
return Handle(THREAD, (*url_classloader_ptr).resolve());
110+
OopHandle* classloader_ptr = _classloader_table->get(path);
111+
if (classloader_ptr != nullptr) {
112+
return Handle(THREAD, (*classloader_ptr).resolve());
107113
} else {
108-
Handle url_classloader = create_url_classloader(path, CHECK_NH);
109-
_url_classloader_table->put(path, OopHandle(Universe::vm_global(), url_classloader()));
114+
Handle classloader = create_classloader(path, CHECK_NH);
115+
_classloader_table->put(path, OopHandle(Universe::vm_global(), classloader()));
110116
path->increment_refcount();
111-
return url_classloader;
117+
return classloader;
112118
}
113119
}

‎src/hotspot/share/cds/unregisteredClasses.hpp

+19-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -25,15 +25,30 @@
2525
#ifndef SHARE_CDS_UNREGISTEREDCLASSES_HPP
2626
#define SHARE_CDS_UNREGISTEREDCLASSES_HPP
2727

28+
#include "memory/allStatic.hpp"
2829
#include "runtime/handles.hpp"
2930

31+
class InstanceKlass;
32+
class Symbol;
33+
3034
class UnregisteredClasses: AllStatic {
3135
public:
32-
static InstanceKlass* load_class(Symbol* h_name, const char* path, TRAPS);
36+
static InstanceKlass* load_class(Symbol* h_name, const char* path,
37+
Handle super_class, objArrayHandle interfaces,
38+
TRAPS);
39+
static void initialize(TRAPS);
40+
static InstanceKlass* UnregisteredClassLoader_klass() {
41+
return _UnregisteredClassLoader_klass;
42+
}
43+
44+
class ClassLoaderTable;
3345

3446
private:
35-
static Handle create_url_classloader(Symbol* path, TRAPS);
36-
static Handle get_url_classloader(Symbol* path, TRAPS);
47+
// Don't put this in vmClasses as it's used only with CDS dumping.
48+
static InstanceKlass* _UnregisteredClassLoader_klass;
49+
50+
static Handle create_classloader(Symbol* path, TRAPS);
51+
static Handle get_classloader(Symbol* path, TRAPS);
3752
};
3853

3954
#endif // SHARE_CDS_UNREGISTEREDCLASSES_HPP

‎src/hotspot/share/classfile/systemDictionary.cpp

-10
Original file line numberDiff line numberDiff line change
@@ -423,16 +423,6 @@ InstanceKlass* SystemDictionary::resolve_with_circularity_detection(Symbol* clas
423423

424424
assert(next_name != nullptr, "null superclass for resolving");
425425
assert(!Signature::is_array(next_name), "invalid superclass name");
426-
#if INCLUDE_CDS
427-
if (CDSConfig::is_dumping_static_archive()) {
428-
// Special processing for handling UNREGISTERED shared classes.
429-
InstanceKlass* k = SystemDictionaryShared::lookup_super_for_unregistered_class(class_name,
430-
next_name, is_superclass);
431-
if (k) {
432-
return k;
433-
}
434-
}
435-
#endif // INCLUDE_CDS
436426

437427
// If class_name is already loaded, just return the superclass or superinterface.
438428
// Make sure there's a placeholder for the class_name before resolving.

‎src/hotspot/share/classfile/systemDictionaryShared.cpp

+7-39
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "cds/heapShared.hpp"
3636
#include "cds/metaspaceShared.hpp"
3737
#include "cds/runTimeClassInfo.hpp"
38+
#include "cds/unregisteredClasses.hpp"
3839
#include "classfile/classFileStream.hpp"
3940
#include "classfile/classLoader.hpp"
4041
#include "classfile/classLoaderData.inline.hpp"
@@ -352,6 +353,12 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
352353
}
353354
}
354355

356+
if (k == UnregisteredClasses::UnregisteredClassLoader_klass()) {
357+
ResourceMark rm;
358+
log_info(cds)("Skipping %s: used only when dumping CDS archive", k->name()->as_C_string());
359+
return true;
360+
}
361+
355362
return false; // false == k should NOT be excluded
356363
}
357364

@@ -470,45 +477,6 @@ bool SystemDictionaryShared::add_unregistered_class(Thread* current, InstanceKla
470477
return (klass == *v);
471478
}
472479

473-
// This function is called to lookup the super/interfaces of shared classes for
474-
// unregistered loaders. E.g., SharedClass in the below example
475-
// where "super:" (and optionally "interface:") have been specified.
476-
//
477-
// java/lang/Object id: 0
478-
// Interface id: 2 super: 0 source: cust.jar
479-
// SharedClass id: 4 super: 0 interfaces: 2 source: cust.jar
480-
InstanceKlass* SystemDictionaryShared::lookup_super_for_unregistered_class(
481-
Symbol* class_name, Symbol* super_name, bool is_superclass) {
482-
483-
assert(CDSConfig::is_dumping_static_archive(), "only when static dumping");
484-
485-
if (!ClassListParser::is_parsing_thread()) {
486-
// Unregistered classes can be created only by ClassListParser::_parsing_thread.
487-
488-
return nullptr;
489-
}
490-
491-
ClassListParser* parser = ClassListParser::instance();
492-
if (parser == nullptr) {
493-
// We're still loading the well-known classes, before the ClassListParser is created.
494-
return nullptr;
495-
}
496-
if (class_name->equals(parser->current_class_name())) {
497-
// When this function is called, all the numbered super and interface types
498-
// must have already been loaded. Hence this function is never recursively called.
499-
if (is_superclass) {
500-
return parser->lookup_super_for_current_class(super_name);
501-
} else {
502-
return parser->lookup_interface_for_current_class(super_name);
503-
}
504-
} else {
505-
// The VM is not trying to resolve a super type of parser->current_class_name().
506-
// Instead, it's resolving an error class (because parser->current_class_name() has
507-
// failed parsing or verification). Don't do anything here.
508-
return nullptr;
509-
}
510-
}
511-
512480
void SystemDictionaryShared::set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs) {
513481
assert(CDSConfig::is_dumping_archive(), "sanity");
514482
assert(!is_builtin(k), "must be unregistered class");

‎src/hotspot/share/classfile/vmClassMacros.hpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -138,7 +138,6 @@
138138
/* support for CDS */ \
139139
do_klass(ByteArrayInputStream_klass, java_io_ByteArrayInputStream ) \
140140
do_klass(URL_klass, java_net_URL ) \
141-
do_klass(URLClassLoader_klass, java_net_URLClassLoader ) \
142141
do_klass(Enum_klass, java_lang_Enum ) \
143142
do_klass(Jar_Manifest_klass, java_util_jar_Manifest ) \
144143
do_klass(jdk_internal_loader_BuiltinClassLoader_klass,jdk_internal_loader_BuiltinClassLoader ) \

‎src/hotspot/share/classfile/vmSymbols.hpp

-2
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ class SerializeClosure;
124124
template(java_security_ProtectionDomain, "java/security/ProtectionDomain") \
125125
template(java_security_SecureClassLoader, "java/security/SecureClassLoader") \
126126
template(java_net_URL, "java/net/URL") \
127-
template(java_net_URLClassLoader, "java/net/URLClassLoader") \
128127
template(java_util_jar_Manifest, "java/util/jar/Manifest") \
129128
template(java_io_ByteArrayInputStream, "java/io/ByteArrayInputStream") \
130129
template(java_io_Serializable, "java/io/Serializable") \
@@ -739,7 +738,6 @@ class SerializeClosure;
739738
template(runtimeSetup, "runtimeSetup") \
740739
template(toFileURL_name, "toFileURL") \
741740
template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \
742-
template(url_array_classloader_void_signature, "([Ljava/net/URL;Ljava/lang/ClassLoader;)V") \
743741
\
744742
/* jcmd Thread.dump_to_file */ \
745743
template(jdk_internal_vm_ThreadDumper, "jdk/internal/vm/ThreadDumper") \

‎src/java.base/share/classes/jdk/internal/misc/CDS.java

+113-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -31,6 +31,10 @@
3131
import java.io.InputStream;
3232
import java.io.IOException;
3333
import java.io.PrintStream;
34+
import java.net.URL;
35+
import java.net.URLClassLoader;
36+
import java.nio.file.InvalidPathException;
37+
import java.nio.file.Path;
3438
import java.util.Arrays;
3539
import java.util.ArrayList;
3640
import java.util.List;
@@ -337,4 +341,112 @@ private static String dumpSharedArchive(boolean isStatic, String fileName) throw
337341
System.out.println("The process was attached by jcmd and dumped a " + (isStatic ? "static" : "dynamic") + " archive " + archiveFilePath);
338342
return archiveFilePath;
339343
}
344+
345+
/**
346+
* This class is used only by native JVM code at CDS dump time for loading
347+
* "unregistered classes", which are archived classes that are intended to
348+
* be loaded by custom class loaders during runtime.
349+
* See src/hotspot/share/cds/unregisteredClasses.cpp.
350+
*/
351+
private static class UnregisteredClassLoader extends URLClassLoader {
352+
private String currentClassName;
353+
private Class<?> currentSuperClass;
354+
private Class<?>[] currentInterfaces;
355+
356+
/**
357+
* Used only by native code. Construct an UnregisteredClassLoader for loading
358+
* unregistered classes from the specified file. If the file doesn't exist,
359+
* the exception will be caughted by native code which will print a warning message and continue.
360+
*
361+
* @param fileName path of the the JAR file to load unregistered classes from.
362+
*/
363+
private UnregisteredClassLoader(String fileName) throws InvalidPathException, IOException {
364+
super(toURLArray(fileName), /*parent*/null);
365+
currentClassName = null;
366+
currentSuperClass = null;
367+
currentInterfaces = null;
368+
}
369+
370+
private static URL[] toURLArray(String fileName) throws InvalidPathException, IOException {
371+
if (!((new File(fileName)).exists())) {
372+
throw new IOException("No such file: " + fileName);
373+
}
374+
return new URL[] {
375+
// Use an intermediate File object to construct a URI/URL without
376+
// authority component as URLClassPath can't handle URLs with a UNC
377+
// server name in the authority component.
378+
Path.of(fileName).toRealPath().toFile().toURI().toURL()
379+
};
380+
}
381+
382+
383+
/**
384+
* Load the class of the given <code>/name<code> from the JAR file that was given to
385+
* the constructor of the current UnregisteredClassLoader instance. This class must be
386+
* a direct subclass of <code>superClass</code>. This class must be declared to implement
387+
* the specified <code>interfaces</code>.
388+
* <p>
389+
* This method must be called in a single threaded context. It will never be recursed (thus
390+
* the asserts)
391+
*
392+
* @param name the name of the class to be loaded.
393+
* @param superClass must not be null. The named class must have a super class.
394+
* @param interfaces could be null if the named class does not implement any interfaces.
395+
*/
396+
private Class<?> load(String name, Class<?> superClass, Class<?>[] interfaces)
397+
throws ClassNotFoundException
398+
{
399+
assert currentClassName == null;
400+
assert currentSuperClass == null;
401+
assert currentInterfaces == null;
402+
403+
try {
404+
currentClassName = name;
405+
currentSuperClass = superClass;
406+
currentInterfaces = interfaces;
407+
408+
return findClass(name);
409+
} finally {
410+
currentClassName = null;
411+
currentSuperClass = null;
412+
currentInterfaces = null;
413+
}
414+
}
415+
416+
/**
417+
* This method must be called from inside the <code>load()</code> method. The <code>/name<code>
418+
* can be only:
419+
* <ul>
420+
* <li> the <code>name</code> parameter for <code>load()</code>
421+
* <li> the name of the <code>superClass</code> parameter for <code>load()</code>
422+
* <li> the name of one of the interfaces in <code>interfaces</code> parameter for <code>load()</code>
423+
* <ul>
424+
*
425+
* For all other cases, a <code>ClassNotFoundException</code> will be thrown.
426+
*/
427+
protected Class<?> findClass(final String name)
428+
throws ClassNotFoundException
429+
{
430+
Objects.requireNonNull(currentClassName);
431+
Objects.requireNonNull(currentSuperClass);
432+
433+
if (name.equals(currentClassName)) {
434+
// Note: the following call will call back to <code>this.findClass(name)</code> to
435+
// resolve the super types of the named class.
436+
return super.findClass(name);
437+
}
438+
if (name.equals(currentSuperClass.getName())) {
439+
return currentSuperClass;
440+
}
441+
if (currentInterfaces != null) {
442+
for (Class<?> c : currentInterfaces) {
443+
if (name.equals(c.getName())) {
444+
return c;
445+
}
446+
}
447+
}
448+
449+
throw new ClassNotFoundException(name);
450+
}
451+
}
340452
}

‎test/hotspot/jtreg/runtime/cds/appcds/customLoader/ClassListFormatE.java

+15-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -49,14 +49,15 @@ public static void main(String[] args) throws Throwable {
4949
//----------------------------------------------------------------------
5050
// TESTGROUP E: super class and interfaces
5151
//----------------------------------------------------------------------
52-
dumpShouldFail(
52+
dumpShouldPass(
5353
"TESTCASE E1: missing interfaces: keyword",
5454
appJar, classlist(
5555
"Hello",
5656
"java/lang/Object id: 1",
5757
"CustomLoadee2 id: 1 super: 1 source: " + customJarPath
5858
),
59-
"Class CustomLoadee2 implements the interface CustomInterface2_ia, but no interface has been specified in the input line");
59+
"java.lang.NoClassDefFoundError: CustomInterface2_ia",
60+
"Cannot find CustomLoadee2");
6061

6162
dumpShouldFail(
6263
"TESTCASE E2: missing one interface",
@@ -67,7 +68,7 @@ appJar, classlist(
6768
"CustomInterface2_ib id: 3 super: 1 source: " + customJarPath,
6869
"CustomLoadee2 id: 4 super: 1 interfaces: 2 source: " + customJarPath
6970
),
70-
"The interface CustomInterface2_ib implemented by class CustomLoadee2 does not match any of the specified interface IDs");
71+
"The number of interfaces (1) specified in class list does not match the class file (2)");
7172

7273
dumpShouldFail(
7374
"TESTCASE E3: specifying an interface that's not implemented by the class",
@@ -101,5 +102,15 @@ appJar, classlist(
101102
"CustomLoadee2 id: 5 super: 4 interfaces: 2 3 source: " + customJarPath
102103
),
103104
"The specified super class CustomLoadee (id 4) does not match actual super class java.lang.Object");
105+
106+
dumpShouldPass(
107+
"TESTCASE E6: JAR file doesn't exist",
108+
appJar, classlist(
109+
"Hello",
110+
"java/lang/Object id: 1",
111+
"NoSuchClass id: 2 super: 1 source: no_such_file.jar"
112+
),
113+
"Cannot find NoSuchClass",
114+
"java.io.IOException: No such file: no_such_file.jar");
104115
}
105116
}

0 commit comments

Comments
 (0)
Please sign in to comment.