mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 14:24:46 +02:00
8348240: Remove SystemDictionaryShared::lookup_super_for_unregistered_class()
Reviewed-by: ccheung, coleenp
This commit is contained in:
parent
48ece07214
commit
7f16a0875c
10 changed files with 231 additions and 153 deletions
|
@ -42,6 +42,7 @@
|
|||
#include "jvm.h"
|
||||
#include "logging/log.hpp"
|
||||
#include "logging/logTag.hpp"
|
||||
#include "memory/oopFactory.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/constantPool.inline.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
|
@ -111,6 +112,12 @@ ClassListParser::~ClassListParser() {
|
|||
_instance = nullptr;
|
||||
}
|
||||
|
||||
void ClassListParser::parse_classlist(const char* classlist_path, ParseMode parse_mode, TRAPS) {
|
||||
UnregisteredClasses::initialize(CHECK);
|
||||
ClassListParser parser(classlist_path, parse_mode);
|
||||
parser.parse(THREAD);
|
||||
}
|
||||
|
||||
void ClassListParser::parse(TRAPS) {
|
||||
for (; !_input_stream.done(); _input_stream.next()) {
|
||||
_line = _input_stream.current_line();
|
||||
|
@ -387,6 +394,19 @@ bool ClassListParser::parse_uint_option(const char* option_name, int* value) {
|
|||
return false;
|
||||
}
|
||||
|
||||
objArrayOop ClassListParser::get_specified_interfaces(TRAPS) {
|
||||
const int n = _interfaces->length();
|
||||
if (n == 0) {
|
||||
return nullptr;
|
||||
} else {
|
||||
objArrayOop array = oopFactory::new_objArray(vmClasses::Class_klass(), n, CHECK_NULL);
|
||||
for (int i = 0; i < n; i++) {
|
||||
array->obj_at_put(i, lookup_class_by_id(_interfaces->at(i))->java_mirror());
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
void ClassListParser::print_specified_interfaces() {
|
||||
const int n = _interfaces->length();
|
||||
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
|
|||
|
||||
ResourceMark rm;
|
||||
char * source_path = os::strdup_check_oom(ClassLoader::uri_to_path(_source));
|
||||
InstanceKlass* k = UnregisteredClasses::load_class(class_name, source_path, CHECK_NULL);
|
||||
InstanceKlass* specified_super = lookup_class_by_id(_super);
|
||||
Handle super_class(THREAD, specified_super->java_mirror());
|
||||
objArrayOop r = get_specified_interfaces(CHECK_NULL);
|
||||
objArrayHandle interfaces(THREAD, r);
|
||||
InstanceKlass* k = UnregisteredClasses::load_class(class_name, source_path,
|
||||
super_class, interfaces, CHECK_NULL);
|
||||
if (k->java_super() != specified_super) {
|
||||
error("The specified super class %s (id %d) does not match actual super class %s",
|
||||
specified_super->external_name(), _super,
|
||||
k->java_super()->external_name());
|
||||
}
|
||||
if (k->local_interfaces()->length() != _interfaces->length()) {
|
||||
print_specified_interfaces();
|
||||
print_actual_interfaces(k);
|
||||
|
@ -734,49 +764,6 @@ InstanceKlass* ClassListParser::lookup_class_by_id(int id) {
|
|||
return *klass_ptr;
|
||||
}
|
||||
|
||||
|
||||
InstanceKlass* ClassListParser::lookup_super_for_current_class(Symbol* super_name) {
|
||||
if (!is_loading_from_source()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
InstanceKlass* k = lookup_class_by_id(super());
|
||||
if (super_name != k->name()) {
|
||||
error("The specified super class %s (id %d) does not match actual super class %s",
|
||||
k->name()->as_klass_external_name(), super(),
|
||||
super_name->as_klass_external_name());
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
InstanceKlass* ClassListParser::lookup_interface_for_current_class(Symbol* interface_name) {
|
||||
if (!is_loading_from_source()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int n = _interfaces->length();
|
||||
if (n == 0) {
|
||||
error("Class %s implements the interface %s, but no interface has been specified in the input line",
|
||||
_class_name, interface_name->as_klass_external_name());
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i=0; i<n; i++) {
|
||||
InstanceKlass* k = lookup_class_by_id(_interfaces->at(i));
|
||||
if (interface_name == k->name()) {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
||||
// interface_name is not specified by the "interfaces:" keyword.
|
||||
print_specified_interfaces();
|
||||
error("The interface %s implemented by class %s does not match any of the specified interface IDs",
|
||||
interface_name->as_klass_external_name(), _class_name);
|
||||
ShouldNotReachHere();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
InstanceKlass* ClassListParser::find_builtin_class_helper(JavaThread* current, Symbol* class_name_symbol, oop class_loader_oop) {
|
||||
Handle class_loader(current, class_loader_oop);
|
||||
return SystemDictionary::find_instance_klass(current, class_name_symbol, class_loader);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 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
|
||||
|
@ -137,12 +137,10 @@ private:
|
|||
void print_diagnostic_info(outputStream* st, const char* msg, ...) ATTRIBUTE_PRINTF(3, 0);
|
||||
void constant_pool_resolution_warning(const char* msg, ...) ATTRIBUTE_PRINTF(2, 0);
|
||||
void error(const char* msg, ...) ATTRIBUTE_PRINTF(2, 0);
|
||||
objArrayOop get_specified_interfaces(TRAPS);
|
||||
|
||||
public:
|
||||
static void parse_classlist(const char* classlist_path, ParseMode parse_mode, TRAPS) {
|
||||
ClassListParser parser(classlist_path, parse_mode);
|
||||
parser.parse(THREAD);
|
||||
}
|
||||
static void parse_classlist(const char* classlist_path, ParseMode parse_mode, TRAPS);
|
||||
|
||||
static bool is_parsing_thread();
|
||||
static ClassListParser* instance() {
|
||||
|
@ -201,12 +199,6 @@ public:
|
|||
}
|
||||
|
||||
bool is_loading_from_source();
|
||||
|
||||
// Look up the super or interface of the current class being loaded
|
||||
// (in this->load_current_class()).
|
||||
InstanceKlass* lookup_super_for_current_class(Symbol* super_name);
|
||||
InstanceKlass* lookup_interface_for_current_class(Symbol* interface_name);
|
||||
|
||||
static void populate_cds_indy_info(const constantPoolHandle &pool, int cp_index, CDSIndyInfo* cii, TRAPS);
|
||||
};
|
||||
#endif // SHARE_CDS_CLASSLISTPARSER_HPP
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include "classfile/classLoaderExt.hpp"
|
||||
#include "classfile/javaClasses.inline.hpp"
|
||||
#include "classfile/symbolTable.hpp"
|
||||
#include "classfile/systemDictionaryShared.hpp"
|
||||
#include "classfile/systemDictionary.hpp"
|
||||
#include "classfile/vmSymbols.hpp"
|
||||
#include "memory/oopFactory.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
|
@ -38,10 +38,22 @@
|
|||
#include "runtime/javaCalls.hpp"
|
||||
#include "services/threadService.hpp"
|
||||
|
||||
InstanceKlass* UnregisteredClasses::_UnregisteredClassLoader_klass = nullptr;
|
||||
|
||||
void UnregisteredClasses::initialize(TRAPS) {
|
||||
if (_UnregisteredClassLoader_klass == nullptr) {
|
||||
// no need for synchronization as this function is called single-threaded.
|
||||
Symbol* klass_name = SymbolTable::new_symbol("jdk/internal/misc/CDS$UnregisteredClassLoader");
|
||||
Klass* k = SystemDictionary::resolve_or_fail(klass_name, true, CHECK);
|
||||
_UnregisteredClassLoader_klass = InstanceKlass::cast(k);
|
||||
}
|
||||
}
|
||||
|
||||
// Load the class of the given name from the location given by path. The path is specified by
|
||||
// the "source:" in the class list file (see classListParser.cpp), and can be a directory or
|
||||
// a JAR file.
|
||||
InstanceKlass* UnregisteredClasses::load_class(Symbol* name, const char* path, TRAPS) {
|
||||
InstanceKlass* UnregisteredClasses::load_class(Symbol* name, const char* path,
|
||||
Handle super_class, objArrayHandle interfaces, TRAPS) {
|
||||
assert(name != nullptr, "invariant");
|
||||
assert(CDSConfig::is_dumping_static_archive(), "this function is only used with -Xshare:dump");
|
||||
|
||||
|
@ -49,19 +61,23 @@ InstanceKlass* UnregisteredClasses::load_class(Symbol* name, const char* path, T
|
|||
THREAD->get_thread_stat()->perf_timers_addr(),
|
||||
PerfClassTraceTime::CLASS_LOAD);
|
||||
|
||||
// Call CDS$UnregisteredClassLoader::load(String name, Class<?> superClass, Class<?>[] interfaces)
|
||||
Symbol* methodName = SymbolTable::new_symbol("load");
|
||||
Symbol* methodSignature = SymbolTable::new_symbol("(Ljava/lang/String;Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/Class;");
|
||||
Symbol* path_symbol = SymbolTable::new_symbol(path);
|
||||
Symbol* findClass = SymbolTable::new_symbol("findClass");
|
||||
Handle url_classloader = get_url_classloader(path_symbol, CHECK_NULL);
|
||||
Handle classloader = get_classloader(path_symbol, CHECK_NULL);
|
||||
Handle ext_class_name = java_lang_String::externalize_classname(name, CHECK_NULL);
|
||||
|
||||
JavaValue result(T_OBJECT);
|
||||
JavaCallArguments args(2);
|
||||
args.set_receiver(url_classloader);
|
||||
JavaCallArguments args(3);
|
||||
args.set_receiver(classloader);
|
||||
args.push_oop(ext_class_name);
|
||||
args.push_oop(super_class);
|
||||
args.push_oop(interfaces);
|
||||
JavaCalls::call_virtual(&result,
|
||||
vmClasses::URLClassLoader_klass(),
|
||||
findClass,
|
||||
vmSymbols::string_class_signature(),
|
||||
UnregisteredClassLoader_klass(),
|
||||
methodName,
|
||||
methodSignature,
|
||||
&args,
|
||||
CHECK_NULL);
|
||||
assert(result.get_type() == T_OBJECT, "just checking");
|
||||
|
@ -69,45 +85,35 @@ InstanceKlass* UnregisteredClasses::load_class(Symbol* name, const char* path, T
|
|||
return InstanceKlass::cast(java_lang_Class::as_Klass(obj));
|
||||
}
|
||||
|
||||
class URLClassLoaderTable : public ResourceHashtable<
|
||||
class UnregisteredClasses::ClassLoaderTable : public ResourceHashtable<
|
||||
Symbol*, OopHandle,
|
||||
137, // prime number
|
||||
AnyObj::C_HEAP> {};
|
||||
|
||||
static URLClassLoaderTable* _url_classloader_table = nullptr;
|
||||
static UnregisteredClasses::ClassLoaderTable* _classloader_table = nullptr;
|
||||
|
||||
Handle UnregisteredClasses::create_url_classloader(Symbol* path, TRAPS) {
|
||||
Handle UnregisteredClasses::create_classloader(Symbol* path, TRAPS) {
|
||||
ResourceMark rm(THREAD);
|
||||
JavaValue result(T_OBJECT);
|
||||
Handle path_string = java_lang_String::create_from_str(path->as_C_string(), CHECK_NH);
|
||||
JavaCalls::call_static(&result,
|
||||
vmClasses::jdk_internal_loader_ClassLoaders_klass(),
|
||||
vmSymbols::toFileURL_name(),
|
||||
vmSymbols::toFileURL_signature(),
|
||||
Handle classloader = JavaCalls::construct_new_instance(
|
||||
UnregisteredClassLoader_klass(),
|
||||
vmSymbols::string_void_signature(),
|
||||
path_string, CHECK_NH);
|
||||
assert(result.get_type() == T_OBJECT, "just checking");
|
||||
oop url_h = result.get_oop();
|
||||
objArrayHandle urls = oopFactory::new_objArray_handle(vmClasses::URL_klass(), 1, CHECK_NH);
|
||||
urls->obj_at_put(0, url_h);
|
||||
|
||||
Handle url_classloader = JavaCalls::construct_new_instance(
|
||||
vmClasses::URLClassLoader_klass(),
|
||||
vmSymbols::url_array_classloader_void_signature(),
|
||||
urls, Handle(), CHECK_NH);
|
||||
return url_classloader;
|
||||
return classloader;
|
||||
}
|
||||
|
||||
Handle UnregisteredClasses::get_url_classloader(Symbol* path, TRAPS) {
|
||||
if (_url_classloader_table == nullptr) {
|
||||
_url_classloader_table = new (mtClass)URLClassLoaderTable();
|
||||
Handle UnregisteredClasses::get_classloader(Symbol* path, TRAPS) {
|
||||
if (_classloader_table == nullptr) {
|
||||
_classloader_table = new (mtClass)ClassLoaderTable();
|
||||
}
|
||||
OopHandle* url_classloader_ptr = _url_classloader_table->get(path);
|
||||
if (url_classloader_ptr != nullptr) {
|
||||
return Handle(THREAD, (*url_classloader_ptr).resolve());
|
||||
OopHandle* classloader_ptr = _classloader_table->get(path);
|
||||
if (classloader_ptr != nullptr) {
|
||||
return Handle(THREAD, (*classloader_ptr).resolve());
|
||||
} else {
|
||||
Handle url_classloader = create_url_classloader(path, CHECK_NH);
|
||||
_url_classloader_table->put(path, OopHandle(Universe::vm_global(), url_classloader()));
|
||||
Handle classloader = create_classloader(path, CHECK_NH);
|
||||
_classloader_table->put(path, OopHandle(Universe::vm_global(), classloader()));
|
||||
path->increment_refcount();
|
||||
return url_classloader;
|
||||
return classloader;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 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
|
||||
|
@ -25,15 +25,30 @@
|
|||
#ifndef SHARE_CDS_UNREGISTEREDCLASSES_HPP
|
||||
#define SHARE_CDS_UNREGISTEREDCLASSES_HPP
|
||||
|
||||
#include "memory/allStatic.hpp"
|
||||
#include "runtime/handles.hpp"
|
||||
|
||||
class InstanceKlass;
|
||||
class Symbol;
|
||||
|
||||
class UnregisteredClasses: AllStatic {
|
||||
public:
|
||||
static InstanceKlass* load_class(Symbol* h_name, const char* path, TRAPS);
|
||||
static InstanceKlass* load_class(Symbol* h_name, const char* path,
|
||||
Handle super_class, objArrayHandle interfaces,
|
||||
TRAPS);
|
||||
static void initialize(TRAPS);
|
||||
static InstanceKlass* UnregisteredClassLoader_klass() {
|
||||
return _UnregisteredClassLoader_klass;
|
||||
}
|
||||
|
||||
class ClassLoaderTable;
|
||||
|
||||
private:
|
||||
static Handle create_url_classloader(Symbol* path, TRAPS);
|
||||
static Handle get_url_classloader(Symbol* path, TRAPS);
|
||||
// Don't put this in vmClasses as it's used only with CDS dumping.
|
||||
static InstanceKlass* _UnregisteredClassLoader_klass;
|
||||
|
||||
static Handle create_classloader(Symbol* path, TRAPS);
|
||||
static Handle get_classloader(Symbol* path, TRAPS);
|
||||
};
|
||||
|
||||
#endif // SHARE_CDS_UNREGISTEREDCLASSES_HPP
|
||||
|
|
|
@ -423,16 +423,6 @@ InstanceKlass* SystemDictionary::resolve_with_circularity_detection(Symbol* clas
|
|||
|
||||
assert(next_name != nullptr, "null superclass for resolving");
|
||||
assert(!Signature::is_array(next_name), "invalid superclass name");
|
||||
#if INCLUDE_CDS
|
||||
if (CDSConfig::is_dumping_static_archive()) {
|
||||
// Special processing for handling UNREGISTERED shared classes.
|
||||
InstanceKlass* k = SystemDictionaryShared::lookup_super_for_unregistered_class(class_name,
|
||||
next_name, is_superclass);
|
||||
if (k) {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
#endif // INCLUDE_CDS
|
||||
|
||||
// If class_name is already loaded, just return the superclass or superinterface.
|
||||
// Make sure there's a placeholder for the class_name before resolving.
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "cds/heapShared.hpp"
|
||||
#include "cds/metaspaceShared.hpp"
|
||||
#include "cds/runTimeClassInfo.hpp"
|
||||
#include "cds/unregisteredClasses.hpp"
|
||||
#include "classfile/classFileStream.hpp"
|
||||
#include "classfile/classLoader.hpp"
|
||||
#include "classfile/classLoaderData.inline.hpp"
|
||||
|
@ -352,6 +353,12 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
|
|||
}
|
||||
}
|
||||
|
||||
if (k == UnregisteredClasses::UnregisteredClassLoader_klass()) {
|
||||
ResourceMark rm;
|
||||
log_info(cds)("Skipping %s: used only when dumping CDS archive", k->name()->as_C_string());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // false == k should NOT be excluded
|
||||
}
|
||||
|
||||
|
@ -470,45 +477,6 @@ bool SystemDictionaryShared::add_unregistered_class(Thread* current, InstanceKla
|
|||
return (klass == *v);
|
||||
}
|
||||
|
||||
// This function is called to lookup the super/interfaces of shared classes for
|
||||
// unregistered loaders. E.g., SharedClass in the below example
|
||||
// where "super:" (and optionally "interface:") have been specified.
|
||||
//
|
||||
// java/lang/Object id: 0
|
||||
// Interface id: 2 super: 0 source: cust.jar
|
||||
// SharedClass id: 4 super: 0 interfaces: 2 source: cust.jar
|
||||
InstanceKlass* SystemDictionaryShared::lookup_super_for_unregistered_class(
|
||||
Symbol* class_name, Symbol* super_name, bool is_superclass) {
|
||||
|
||||
assert(CDSConfig::is_dumping_static_archive(), "only when static dumping");
|
||||
|
||||
if (!ClassListParser::is_parsing_thread()) {
|
||||
// Unregistered classes can be created only by ClassListParser::_parsing_thread.
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ClassListParser* parser = ClassListParser::instance();
|
||||
if (parser == nullptr) {
|
||||
// We're still loading the well-known classes, before the ClassListParser is created.
|
||||
return nullptr;
|
||||
}
|
||||
if (class_name->equals(parser->current_class_name())) {
|
||||
// When this function is called, all the numbered super and interface types
|
||||
// must have already been loaded. Hence this function is never recursively called.
|
||||
if (is_superclass) {
|
||||
return parser->lookup_super_for_current_class(super_name);
|
||||
} else {
|
||||
return parser->lookup_interface_for_current_class(super_name);
|
||||
}
|
||||
} else {
|
||||
// The VM is not trying to resolve a super type of parser->current_class_name().
|
||||
// Instead, it's resolving an error class (because parser->current_class_name() has
|
||||
// failed parsing or verification). Don't do anything here.
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SystemDictionaryShared::set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs) {
|
||||
assert(CDSConfig::is_dumping_archive(), "sanity");
|
||||
assert(!is_builtin(k), "must be unregistered class");
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 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
|
||||
|
@ -138,7 +138,6 @@
|
|||
/* support for CDS */ \
|
||||
do_klass(ByteArrayInputStream_klass, java_io_ByteArrayInputStream ) \
|
||||
do_klass(URL_klass, java_net_URL ) \
|
||||
do_klass(URLClassLoader_klass, java_net_URLClassLoader ) \
|
||||
do_klass(Enum_klass, java_lang_Enum ) \
|
||||
do_klass(Jar_Manifest_klass, java_util_jar_Manifest ) \
|
||||
do_klass(jdk_internal_loader_BuiltinClassLoader_klass,jdk_internal_loader_BuiltinClassLoader ) \
|
||||
|
|
|
@ -124,7 +124,6 @@ class SerializeClosure;
|
|||
template(java_security_ProtectionDomain, "java/security/ProtectionDomain") \
|
||||
template(java_security_SecureClassLoader, "java/security/SecureClassLoader") \
|
||||
template(java_net_URL, "java/net/URL") \
|
||||
template(java_net_URLClassLoader, "java/net/URLClassLoader") \
|
||||
template(java_util_jar_Manifest, "java/util/jar/Manifest") \
|
||||
template(java_io_ByteArrayInputStream, "java/io/ByteArrayInputStream") \
|
||||
template(java_io_Serializable, "java/io/Serializable") \
|
||||
|
@ -739,7 +738,6 @@ class SerializeClosure;
|
|||
template(runtimeSetup, "runtimeSetup") \
|
||||
template(toFileURL_name, "toFileURL") \
|
||||
template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \
|
||||
template(url_array_classloader_void_signature, "([Ljava/net/URL;Ljava/lang/ClassLoader;)V") \
|
||||
\
|
||||
/* jcmd Thread.dump_to_file */ \
|
||||
template(jdk_internal_vm_ThreadDumper, "jdk/internal/vm/ThreadDumper") \
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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
|
||||
|
@ -31,6 +31,10 @@ import java.io.InputStreamReader;
|
|||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -337,4 +341,112 @@ public class CDS {
|
|||
System.out.println("The process was attached by jcmd and dumped a " + (isStatic ? "static" : "dynamic") + " archive " + archiveFilePath);
|
||||
return archiveFilePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is used only by native JVM code at CDS dump time for loading
|
||||
* "unregistered classes", which are archived classes that are intended to
|
||||
* be loaded by custom class loaders during runtime.
|
||||
* See src/hotspot/share/cds/unregisteredClasses.cpp.
|
||||
*/
|
||||
private static class UnregisteredClassLoader extends URLClassLoader {
|
||||
private String currentClassName;
|
||||
private Class<?> currentSuperClass;
|
||||
private Class<?>[] currentInterfaces;
|
||||
|
||||
/**
|
||||
* Used only by native code. Construct an UnregisteredClassLoader for loading
|
||||
* unregistered classes from the specified file. If the file doesn't exist,
|
||||
* the exception will be caughted by native code which will print a warning message and continue.
|
||||
*
|
||||
* @param fileName path of the the JAR file to load unregistered classes from.
|
||||
*/
|
||||
private UnregisteredClassLoader(String fileName) throws InvalidPathException, IOException {
|
||||
super(toURLArray(fileName), /*parent*/null);
|
||||
currentClassName = null;
|
||||
currentSuperClass = null;
|
||||
currentInterfaces = null;
|
||||
}
|
||||
|
||||
private static URL[] toURLArray(String fileName) throws InvalidPathException, IOException {
|
||||
if (!((new File(fileName)).exists())) {
|
||||
throw new IOException("No such file: " + fileName);
|
||||
}
|
||||
return new URL[] {
|
||||
// Use an intermediate File object to construct a URI/URL without
|
||||
// authority component as URLClassPath can't handle URLs with a UNC
|
||||
// server name in the authority component.
|
||||
Path.of(fileName).toRealPath().toFile().toURI().toURL()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the class of the given <code>/name<code> from the JAR file that was given to
|
||||
* the constructor of the current UnregisteredClassLoader instance. This class must be
|
||||
* a direct subclass of <code>superClass</code>. This class must be declared to implement
|
||||
* the specified <code>interfaces</code>.
|
||||
* <p>
|
||||
* This method must be called in a single threaded context. It will never be recursed (thus
|
||||
* the asserts)
|
||||
*
|
||||
* @param name the name of the class to be loaded.
|
||||
* @param superClass must not be null. The named class must have a super class.
|
||||
* @param interfaces could be null if the named class does not implement any interfaces.
|
||||
*/
|
||||
private Class<?> load(String name, Class<?> superClass, Class<?>[] interfaces)
|
||||
throws ClassNotFoundException
|
||||
{
|
||||
assert currentClassName == null;
|
||||
assert currentSuperClass == null;
|
||||
assert currentInterfaces == null;
|
||||
|
||||
try {
|
||||
currentClassName = name;
|
||||
currentSuperClass = superClass;
|
||||
currentInterfaces = interfaces;
|
||||
|
||||
return findClass(name);
|
||||
} finally {
|
||||
currentClassName = null;
|
||||
currentSuperClass = null;
|
||||
currentInterfaces = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method must be called from inside the <code>load()</code> method. The <code>/name<code>
|
||||
* can be only:
|
||||
* <ul>
|
||||
* <li> the <code>name</code> parameter for <code>load()</code>
|
||||
* <li> the name of the <code>superClass</code> parameter for <code>load()</code>
|
||||
* <li> the name of one of the interfaces in <code>interfaces</code> parameter for <code>load()</code>
|
||||
* <ul>
|
||||
*
|
||||
* For all other cases, a <code>ClassNotFoundException</code> will be thrown.
|
||||
*/
|
||||
protected Class<?> findClass(final String name)
|
||||
throws ClassNotFoundException
|
||||
{
|
||||
Objects.requireNonNull(currentClassName);
|
||||
Objects.requireNonNull(currentSuperClass);
|
||||
|
||||
if (name.equals(currentClassName)) {
|
||||
// Note: the following call will call back to <code>this.findClass(name)</code> to
|
||||
// resolve the super types of the named class.
|
||||
return super.findClass(name);
|
||||
}
|
||||
if (name.equals(currentSuperClass.getName())) {
|
||||
return currentSuperClass;
|
||||
}
|
||||
if (currentInterfaces != null) {
|
||||
for (Class<?> c : currentInterfaces) {
|
||||
if (name.equals(c.getName())) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 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
|
||||
|
@ -49,14 +49,15 @@ public class ClassListFormatE extends ClassListFormatBase {
|
|||
//----------------------------------------------------------------------
|
||||
// TESTGROUP E: super class and interfaces
|
||||
//----------------------------------------------------------------------
|
||||
dumpShouldFail(
|
||||
dumpShouldPass(
|
||||
"TESTCASE E1: missing interfaces: keyword",
|
||||
appJar, classlist(
|
||||
"Hello",
|
||||
"java/lang/Object id: 1",
|
||||
"CustomLoadee2 id: 1 super: 1 source: " + customJarPath
|
||||
),
|
||||
"Class CustomLoadee2 implements the interface CustomInterface2_ia, but no interface has been specified in the input line");
|
||||
"java.lang.NoClassDefFoundError: CustomInterface2_ia",
|
||||
"Cannot find CustomLoadee2");
|
||||
|
||||
dumpShouldFail(
|
||||
"TESTCASE E2: missing one interface",
|
||||
|
@ -67,7 +68,7 @@ public class ClassListFormatE extends ClassListFormatBase {
|
|||
"CustomInterface2_ib id: 3 super: 1 source: " + customJarPath,
|
||||
"CustomLoadee2 id: 4 super: 1 interfaces: 2 source: " + customJarPath
|
||||
),
|
||||
"The interface CustomInterface2_ib implemented by class CustomLoadee2 does not match any of the specified interface IDs");
|
||||
"The number of interfaces (1) specified in class list does not match the class file (2)");
|
||||
|
||||
dumpShouldFail(
|
||||
"TESTCASE E3: specifying an interface that's not implemented by the class",
|
||||
|
@ -101,5 +102,15 @@ public class ClassListFormatE extends ClassListFormatBase {
|
|||
"CustomLoadee2 id: 5 super: 4 interfaces: 2 3 source: " + customJarPath
|
||||
),
|
||||
"The specified super class CustomLoadee (id 4) does not match actual super class java.lang.Object");
|
||||
|
||||
dumpShouldPass(
|
||||
"TESTCASE E6: JAR file doesn't exist",
|
||||
appJar, classlist(
|
||||
"Hello",
|
||||
"java/lang/Object id: 1",
|
||||
"NoSuchClass id: 2 super: 1 source: no_such_file.jar"
|
||||
),
|
||||
"Cannot find NoSuchClass",
|
||||
"java.io.IOException: No such file: no_such_file.jar");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue