8317269: Store old classes in linked state in AOT cache

Reviewed-by: coleenp, matsaave
This commit is contained in:
Ioi Lam 2025-09-10 19:21:00 +00:00
parent fdc11a1569
commit 85715e1050
35 changed files with 1485 additions and 90 deletions

View File

@ -652,6 +652,8 @@ char* VM_PopulateDumpSharedSpace::dump_read_only_tables(AOTClassLocationConfig*&
}
void VM_PopulateDumpSharedSpace::doit() {
CDSConfig::set_is_at_aot_safepoint(true);
if (!CDSConfig::is_dumping_final_static_archive()) {
guarantee(!CDSConfig::is_using_archive(), "We should not be using an archive when we dump");
}
@ -717,6 +719,8 @@ void VM_PopulateDumpSharedSpace::doit() {
_map_info->set_serialized_data(serialized_data);
_map_info->set_cloned_vtables(CppVtables::vtables_serialized_base());
_map_info->header()->set_class_location_config(cl_config);
CDSConfig::set_is_at_aot_safepoint(false);
}
class CollectClassesForLinking : public KlassClosure {
@ -773,12 +777,9 @@ bool AOTMetaspace::may_be_eagerly_linked(InstanceKlass* ik) {
return true;
}
void AOTMetaspace::link_shared_classes(TRAPS) {
AOTClassLinker::initialize();
AOTClassInitializer::init_test_class(CHECK);
void AOTMetaspace::link_all_loaded_classes(JavaThread* current) {
while (true) {
ResourceMark rm(THREAD);
ResourceMark rm(current);
CollectClassesForLinking collect_classes;
bool has_linked = false;
const GrowableArray<OopHandle>* mirrors = collect_classes.mirrors();
@ -786,7 +787,7 @@ void AOTMetaspace::link_shared_classes(TRAPS) {
OopHandle mirror = mirrors->at(i);
InstanceKlass* ik = InstanceKlass::cast(java_lang_Class::as_Klass(mirror.resolve()));
if (may_be_eagerly_linked(ik)) {
has_linked |= try_link_class(THREAD, ik);
has_linked |= try_link_class(current, ik);
}
}
@ -796,6 +797,13 @@ void AOTMetaspace::link_shared_classes(TRAPS) {
// Class linking includes verification which may load more classes.
// Keep scanning until we have linked no more classes.
}
}
void AOTMetaspace::link_shared_classes(TRAPS) {
AOTClassLinker::initialize();
AOTClassInitializer::init_test_class(CHECK);
link_all_loaded_classes(THREAD);
// Eargerly resolve all string constants in constant pools
{

View File

@ -135,6 +135,7 @@ public:
}
static bool try_link_class(JavaThread* current, InstanceKlass* ik);
static void link_all_loaded_classes(JavaThread* current);
static void link_shared_classes(TRAPS) NOT_CDS_RETURN;
static bool may_be_eagerly_linked(InstanceKlass* ik) NOT_CDS_RETURN_(false);

View File

@ -937,7 +937,7 @@ void ArchiveBuilder::make_klasses_shareable() {
ADD_COUNT(num_enum_klasses);
}
if (!ik->can_be_verified_at_dumptime()) {
if (CDSConfig::is_old_class_for_verifier(ik)) {
ADD_COUNT(num_old_klasses);
old = " old";
}

View File

@ -56,6 +56,7 @@ bool CDSConfig::_has_temp_aot_config_file = false;
bool CDSConfig::_old_cds_flags_used = false;
bool CDSConfig::_new_aot_flags_used = false;
bool CDSConfig::_disable_heap_dumping = false;
bool CDSConfig::_is_at_aot_safepoint = false;
const char* CDSConfig::_default_archive_path = nullptr;
const char* CDSConfig::_input_static_archive_path = nullptr;
@ -922,6 +923,35 @@ bool CDSConfig::is_dumping_lambdas_in_legacy_mode() {
return !is_dumping_method_handles();
}
bool CDSConfig::is_preserving_verification_constraints() {
// Verification dependencies are classes used in assignability checks by the
// bytecode verifier. In the following example, the verification dependencies
// for X are A and B.
//
// class X {
// A getA() { return new B(); }
// }
//
// With the AOT cache, we can ensure that all the verification dependencies
// (A and B in the above example) are unconditionally loaded during the bootstrap
// of the production run. This means that if a class was successfully verified
// in the assembly phase, all of the verifier's assignability checks will remain
// valid in the production run, so we don't need to verify aot-linked classes again.
if (is_dumping_preimage_static_archive()) { // writing AOT config
return AOTClassLinking;
} else if (is_dumping_final_static_archive()) { // writing AOT cache
return is_dumping_aot_linked_classes();
} else {
// For simplicity, we don't support this optimization with the old CDS workflow.
return false;
}
}
bool CDSConfig::is_old_class_for_verifier(const InstanceKlass* ik) {
return ik->major_version() < 50 /*JAVA_6_VERSION*/;
}
#if INCLUDE_CDS_JAVA_HEAP
bool CDSConfig::are_vm_options_incompatible_with_dumping_heap() {
return check_options_incompatible_with_dumping_heap() != nullptr;

View File

@ -30,6 +30,7 @@
#include "utilities/macros.hpp"
class JavaThread;
class InstanceKlass;
class CDSConfig : public AllStatic {
#if INCLUDE_CDS
@ -43,6 +44,7 @@ class CDSConfig : public AllStatic {
static bool _has_aot_linked_classes;
static bool _is_single_command_training;
static bool _has_temp_aot_config_file;
static bool _is_at_aot_safepoint;
const static char* _default_archive_path;
const static char* _input_static_archive_path;
@ -99,6 +101,9 @@ public:
static const char* type_of_archive_being_written();
static void prepare_for_dumping();
static bool is_at_aot_safepoint() { return CDS_ONLY(_is_at_aot_safepoint) NOT_CDS(false); }
static void set_is_at_aot_safepoint(bool value) { CDS_ONLY(_is_at_aot_safepoint = value); }
// --- Basic CDS features
// archive(s) in general
@ -161,6 +166,10 @@ public:
static bool is_using_aot_linked_classes() NOT_CDS_JAVA_HEAP_RETURN_(false);
static void set_has_aot_linked_classes(bool has_aot_linked_classes) NOT_CDS_JAVA_HEAP_RETURN;
// Bytecode verification
static bool is_preserving_verification_constraints();
static bool is_old_class_for_verifier(const InstanceKlass* ik);
// archive_path
// Points to the classes.jsa in $JAVA_HOME (could be input or output)

View File

@ -47,7 +47,7 @@ size_t DumpTimeClassInfo::runtime_info_bytesize() const {
num_enum_klass_static_fields());
}
void DumpTimeClassInfo::add_verification_constraint(InstanceKlass* k, Symbol* name,
void DumpTimeClassInfo::add_verification_constraint(Symbol* name,
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) {
if (_verifier_constraints == nullptr) {
_verifier_constraints = new (mtClass) GrowableArray<DTVerifierConstraint>(4, mtClass);
@ -73,9 +73,14 @@ void DumpTimeClassInfo::add_verification_constraint(InstanceKlass* k, Symbol* na
if (log_is_enabled(Trace, aot, verification)) {
ResourceMark rm;
log_trace(aot, verification)("add_verification_constraint: %s: %s must be subclass of %s [0x%x] array len %d flags len %d",
k->external_name(), from_name->as_klass_external_name(),
name->as_klass_external_name(), c, vc_array->length(), vcflags_array->length());
if (from_name != nullptr) {
log_trace(aot, verification)("add verification constraint: %s: %s must be subclass of %s [0x%x]",
_klass->external_name(), from_name->as_klass_external_name(),
name->as_klass_external_name(), c);
} else {
log_trace(aot, verification)("added old verification constraint: %s: %s", _klass->external_name(),
name->as_klass_external_name());
}
}
}

View File

@ -88,7 +88,7 @@ class DumpTimeClassInfo: public CHeapObj<mtClass> {
Symbol* _from_name;
public:
DTVerifierConstraint() : _name(nullptr), _from_name(nullptr) {}
DTVerifierConstraint(Symbol* n, Symbol* fn) : _name(n), _from_name(fn) {
DTVerifierConstraint(Symbol* n, Symbol* fn = nullptr) : _name(n), _from_name(fn) {
Symbol::maybe_increment_refcount(_name);
Symbol::maybe_increment_refcount(_from_name);
}
@ -152,8 +152,9 @@ public:
DumpTimeClassInfo& operator=(const DumpTimeClassInfo&) = delete;
~DumpTimeClassInfo();
void add_verification_constraint(InstanceKlass* k, Symbol* name,
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object);
// For old verifier: only name is saved; all other fields are null/false.
void add_verification_constraint(Symbol* name,
Symbol* from_name = nullptr, bool from_field_is_protected = false, bool from_is_array = false, bool from_is_object = false);
void record_linking_constraint(Symbol* name, Handle loader1, Handle loader2);
void add_enum_klass_static_field(int archived_heap_root_index);
int enum_klass_static_field(int which_field);
@ -175,6 +176,14 @@ public:
return array_length_or_zero(_verifier_constraint_flags);
}
Symbol* verifier_constraint_name_at(int i) const {
return _verifier_constraints->at(i).name();
}
Symbol* verifier_constraint_from_name_at(int i) const {
return _verifier_constraints->at(i).from_name();
}
int num_loader_constraints() const {
return array_length_or_zero(_loader_constraints);
}

View File

@ -110,6 +110,12 @@ public:
}
void doit() {
CDSConfig::set_is_at_aot_safepoint(true);
doit_inner();
CDSConfig::set_is_at_aot_safepoint(false);
}
void doit_inner() {
verify_universe("Before CDS dynamic dump");
DEBUG_ONLY(SystemDictionaryShared::NoClassLoadingMark nclm);

View File

@ -471,12 +471,12 @@ class LambdaProxyClassDictionary::CleanupDumpTimeLambdaProxyClassTable: StackObj
// If the caller class and/or nest_host are excluded, the associated lambda proxy
// must also be excluded.
bool always_exclude = SystemDictionaryShared::check_for_exclusion(caller_ik, nullptr) ||
SystemDictionaryShared::check_for_exclusion(nest_host, nullptr);
bool always_exclude = SystemDictionaryShared::should_be_excluded(caller_ik) ||
SystemDictionaryShared::should_be_excluded(nest_host);
for (int i = info._proxy_klasses->length() - 1; i >= 0; i--) {
InstanceKlass* ik = info._proxy_klasses->at(i);
if (always_exclude || SystemDictionaryShared::check_for_exclusion(ik, nullptr)) {
if (always_exclude || SystemDictionaryShared::should_be_excluded(ik)) {
LambdaProxyClassDictionary::reset_registered_lambda_proxy_class(ik);
info._proxy_klasses->remove_at(i);
}

View File

@ -40,12 +40,18 @@ void RunTimeClassInfo::init(DumpTimeClassInfo& info) {
_num_verifier_constraints = info.num_verifier_constraints();
_num_loader_constraints = info.num_loader_constraints();
int i;
if (CDSConfig::is_preserving_verification_constraints() && CDSConfig::is_dumping_final_static_archive()) {
// The production run doesn't need the verifier constraints, as we can guarantee that all classes checked by
// the verifier during AOT training/assembly phases cannot be replaced in the production run.
_num_verifier_constraints = 0;
}
if (_num_verifier_constraints > 0) {
RTVerifierConstraint* vf_constraints = verifier_constraints();
char* flags = verifier_constraint_flags();
for (i = 0; i < _num_verifier_constraints; i++) {
vf_constraints[i]._name = builder->any_to_offset_u4(info._verifier_constraints->at(i).name());
vf_constraints[i]._from_name = builder->any_to_offset_u4(info._verifier_constraints->at(i).from_name());
vf_constraints[i]._name = builder->any_to_offset_u4(info._verifier_constraints->at(i).name());
vf_constraints[i]._from_name = builder->any_or_null_to_offset_u4(info._verifier_constraints->at(i).from_name());
}
for (i = 0; i < _num_verifier_constraints; i++) {
flags[i] = info._verifier_constraint_flags->at(i);

View File

@ -59,7 +59,9 @@ class RunTimeClassInfo {
u4 _name;
u4 _from_name;
Symbol* name() { return ArchiveUtils::offset_to_archived_address<Symbol*>(_name); }
Symbol* from_name() { return ArchiveUtils::offset_to_archived_address<Symbol*>(_from_name); }
Symbol* from_name() {
return (_from_name == 0) ? nullptr : ArchiveUtils::offset_to_archived_address<Symbol*>(_from_name);
}
};
struct RTLoaderConstraint {

View File

@ -204,28 +204,156 @@ DumpTimeClassInfo* SystemDictionaryShared::get_info_locked(InstanceKlass* k) {
return info;
}
bool SystemDictionaryShared::check_for_exclusion(InstanceKlass* k, DumpTimeClassInfo* info) {
if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache(k)) {
// We have reached a super type that's already in the base archive. Treat it
// as "not excluded".
return false;
}
if (info == nullptr) {
info = _dumptime_table->get(k);
assert(info != nullptr, "supertypes of any classes in _dumptime_table must either be shared, or must also be in _dumptime_table");
}
bool SystemDictionaryShared::should_be_excluded_impl(InstanceKlass* k, DumpTimeClassInfo* info) {
assert_lock_strong(DumpTimeTable_lock);
if (!info->has_checked_exclusion()) {
if (check_for_exclusion_impl(k)) {
info->set_excluded();
}
info->set_has_checked_exclusion();
check_exclusion_for_self_and_dependencies(k);
assert(info->has_checked_exclusion(), "must be");
}
return info->is_excluded();
}
// <func> returns bool and takes a single parameter of Symbol*
// The return value indicates whether we want to keep on iterating or not.
template<typename Function>
void SystemDictionaryShared::iterate_verification_constraint_names(InstanceKlass* k, DumpTimeClassInfo* info, Function func) {
int n = info->num_verifier_constraints();
bool cont; // continue iterating?
for (int i = 0; i < n; i++) {
cont = func(info->verifier_constraint_name_at(i));
if (!cont) {
return; // early termination
}
Symbol* from_name = info->verifier_constraint_from_name_at(i);
if (from_name != nullptr) {
cont = func(from_name);
if (!cont) {
return; // early termination
}
}
}
}
// This is a table of classes that need to be checked for exclusion.
class SystemDictionaryShared::ExclusionCheckCandidates
: public HashTable<InstanceKlass*, DumpTimeClassInfo*, 15889> {
void add_candidate(InstanceKlass* k) {
if (contains(k)) {
return;
}
if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache(k)) {
return;
}
DumpTimeClassInfo* info = SystemDictionaryShared::get_info_locked(k);
if (info->has_checked_exclusion()) {
// We have check exclusion of k and all of its dependencies, so there's no need to check again.
return;
}
put(k, info);
if (!k->is_loaded()) {
// super types are not yet initialized for k.
return;
}
InstanceKlass* super = k->java_super();
if (super != nullptr) {
add_candidate(super);
}
Array<InstanceKlass*>* interfaces = k->local_interfaces();
int len = interfaces->length();
for (int i = 0; i < len; i++) {
add_candidate(interfaces->at(i));
}
InstanceKlass* nest_host = k->nest_host_or_null();
if (nest_host != nullptr && nest_host != k) {
add_candidate(nest_host);
}
if (CDSConfig::is_preserving_verification_constraints()) {
SystemDictionaryShared::iterate_verification_constraint_names(k, info, [&] (Symbol* constraint_class_name) {
Klass* constraint_bottom_class = find_verification_constraint_bottom_class(k, constraint_class_name);
if (constraint_bottom_class != nullptr && constraint_bottom_class->is_instance_klass()) {
add_candidate(InstanceKlass::cast(constraint_bottom_class));
}
return true; // Keep iterating.
});
}
}
public:
ExclusionCheckCandidates(InstanceKlass* k) {
add_candidate(k);
}
};
// A class X is excluded if check_self_exclusion() returns true for X or any of
// X's "exclusion dependency" classes, which include:
// - ik's super types
// - ik's nest host (if any)
//
// plus, if CDSConfig::is_preserving_verification_constraints()==true:
// - ik's verification constraints. These are the classes used in assignability checks
// when verifying ik's bytecodes.
//
// This method ensure that exclusion check is performed on X and all of its exclusion dependencies.
void SystemDictionaryShared::check_exclusion_for_self_and_dependencies(InstanceKlass* ik) {
assert_lock_strong(DumpTimeTable_lock);
ResourceMark rm;
// This will recursively find ik and all of its exclusion dependencies that have not yet been checked.
ExclusionCheckCandidates candidates(ik);
// (1) Check each class to see if it should be excluded due to its own problems
candidates.iterate_all([&] (InstanceKlass* k, DumpTimeClassInfo* info) {
if (check_self_exclusion(k)) {
info->set_excluded();
}
});
// (2) Check each class to see if it should be excluded because of problems in a depeendency class
while (true) {
bool found_new_exclusion = false;
candidates.iterate_all([&] (InstanceKlass* k, DumpTimeClassInfo* info) {
if (!info->is_excluded() && check_dependencies_exclusion(k, info)) {
info->set_excluded();
found_new_exclusion = true;
}
});
// Algorithm notes:
//
// The dependencies form a directed graph, possibly cyclic. Class X is excluded
// if it has at least one directed path that reaches class Y, where
// check_self_exclusion(Y) returns true.
//
// Because of the possibility of cycles in the graph, we cannot use simple
// recursion. Otherwise we will either never terminate, or will miss some paths.
//
// Hence, we keep doing a linear scan of the candidates until we stop finding
// new exclusions.
//
// In the worst case, we find one exclusion per iteration of the while loop,
// so the while loop gets executed O(N^2) times. However, in reality we have
// very few exclusions, so in most cases the while loop executes only once, and we
// walk each edge in the dependencies graph exactly once.
if (!found_new_exclusion) {
break;
}
}
candidates.iterate_all([&] (InstanceKlass* k, DumpTimeClassInfo* info) {
// All candidates have been fully checked, so we don't need to check them again.
info->set_has_checked_exclusion();
});
}
// Returns true so the caller can do: return warn_excluded(".....");
bool SystemDictionaryShared::warn_excluded(InstanceKlass* k, const char* reason) {
ResourceMark rm;
@ -248,7 +376,8 @@ bool SystemDictionaryShared::is_early_klass(InstanceKlass* ik) {
return (info != nullptr) ? info->is_early_klass() : false;
}
bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
bool SystemDictionaryShared::check_self_exclusion(InstanceKlass* k) {
assert_lock_strong(DumpTimeTable_lock);
if (CDSConfig::is_dumping_final_static_archive() && k->defined_by_other_loaders()
&& k->in_aot_cache()) {
return false; // Do not exclude: unregistered classes are passed from preimage to final image.
@ -301,9 +430,8 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
return warn_excluded(k, "Failed verification");
} else if (CDSConfig::is_dumping_aot_linked_classes()) {
// Most loaded classes should have been speculatively linked by AOTMetaspace::link_class_for_cds().
// However, we do not speculatively link old classes, as they are not recorded by
// SystemDictionaryShared::record_linking_constraint(). As a result, such an unlinked
// class may fail to verify in AOTLinkedClassBulkLoader::init_required_classes_for_loader(),
// Old classes may not be linked if CDSConfig::is_preserving_verification_constraints()==false.
// An unlinked class may fail to verify in AOTLinkedClassBulkLoader::init_required_classes_for_loader(),
// causing the JVM to fail at bootstrap.
return warn_excluded(k, "Unlinked class not supported by AOTClassLinking");
} else if (CDSConfig::is_dumping_preimage_static_archive()) {
@ -329,10 +457,13 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
return true;
}
InstanceKlass* super = k->super();
if (super != nullptr && check_for_exclusion(super, nullptr)) {
ResourceMark rm;
aot_log_warning(aot)("Skipping %s: super class %s is excluded", k->name()->as_C_string(), super->name()->as_C_string());
return false;
}
// Returns true if DumpTimeClassInfo::is_excluded() is true for at least one of k's exclusion dependencies.
bool SystemDictionaryShared::check_dependencies_exclusion(InstanceKlass* k, DumpTimeClassInfo* info) {
InstanceKlass* super = k->java_super();
if (super != nullptr && is_dependency_excluded(k, super, "super")) {
return true;
}
@ -340,21 +471,87 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
int len = interfaces->length();
for (int i = 0; i < len; i++) {
InstanceKlass* intf = interfaces->at(i);
if (check_for_exclusion(intf, nullptr)) {
ResourceMark rm;
aot_log_warning(aot)("Skipping %s: interface %s is excluded", k->name()->as_C_string(), intf->name()->as_C_string());
if (is_dependency_excluded(k, intf, "interface")) {
return true;
}
}
InstanceKlass* nest_host = k->nest_host_or_null();
if (nest_host != nullptr && nest_host != k && check_for_exclusion(nest_host, nullptr)) {
ResourceMark rm;
aot_log_warning(aot)("Skipping %s: nest_host class %s is excluded", k->name()->as_C_string(), nest_host->name()->as_C_string());
if (nest_host != nullptr && nest_host != k && is_dependency_excluded(k, nest_host, "nest host class")) {
return true;
}
return false; // false == k should NOT be excluded
if (CDSConfig::is_preserving_verification_constraints()) {
bool excluded = false;
iterate_verification_constraint_names(k, info, [&] (Symbol* constraint_class_name) {
if (check_verification_constraint_exclusion(k, constraint_class_name)) {
// If one of the verification constraint class has been excluded, the assignability checks
// by the verifier may no longer be valid in the production run. For safety, exclude this class.
excluded = true;
return false; // terminate iteration; k will be excluded
} else {
return true; // keep iterating
}
});
if (excluded) {
// At least one verification constraint class has been excluded
return true;
}
}
return false;
}
bool SystemDictionaryShared::is_dependency_excluded(InstanceKlass* k, InstanceKlass* dependency, const char* type) {
if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache(dependency)) {
return false;
}
DumpTimeClassInfo* dependency_info = get_info_locked(dependency);
if (dependency_info->is_excluded()) {
ResourceMark rm;
aot_log_warning(aot)("Skipping %s: %s %s is excluded", k->name()->as_C_string(), type, dependency->name()->as_C_string());
return true;
}
return false;
}
bool SystemDictionaryShared::check_verification_constraint_exclusion(InstanceKlass* k, Symbol* constraint_class_name) {
Klass* constraint_bottom_class = find_verification_constraint_bottom_class(k, constraint_class_name);
if (constraint_bottom_class == nullptr) {
// We don't have a bottom class (constraint_class_name is a type array), or constraint_class_name
// has not been loaded. The latter case happens when the new verifier was checking
// if constraint_class_name is assignable to an interface, and found the answer without resolving
// constraint_class_name.
//
// Since this class is not even loaded, it surely cannot be excluded.
return false;
} else if (constraint_bottom_class->is_instance_klass()) {
if (is_dependency_excluded(k, InstanceKlass::cast(constraint_bottom_class), "verification constraint")) {
return true;
}
} else {
assert(constraint_bottom_class->is_typeArray_klass(), "must be");
}
return false;
}
Klass* SystemDictionaryShared::find_verification_constraint_bottom_class(InstanceKlass* k, Symbol* constraint_class_name) {
Thread* current = Thread::current();
Handle loader(current, k->class_loader());
Klass* constraint_class = SystemDictionary::find_instance_or_array_klass(current, constraint_class_name, loader);
if (constraint_class == nullptr) {
return nullptr;
}
if (constraint_class->is_objArray_klass()) {
constraint_class = ObjArrayKlass::cast(constraint_class)->bottom_klass();
}
precond(constraint_class->is_typeArray_klass() || constraint_class->is_instance_klass());
return constraint_class;
}
bool SystemDictionaryShared::is_builtin_loader(ClassLoaderData* loader_data) {
@ -556,7 +753,7 @@ void SystemDictionaryShared::handle_class_unloading(InstanceKlass* klass) {
void SystemDictionaryShared::init_dumptime_info_from_preimage(InstanceKlass* k) {
init_dumptime_info(k);
copy_verification_constraints_from_preimage(k);
copy_verification_info_from_preimage(k);
copy_linking_constraints_from_preimage(k);
if (SystemDictionary::is_platform_class_loader(k->class_loader())) {
@ -651,16 +848,21 @@ public:
// Returns true if the class should be excluded. This can be called by
// AOTConstantPoolResolver before or after we enter the CDS safepoint.
// When called before the safepoint, we need to link the class so that
// it can be checked by check_for_exclusion().
// it can be checked by should_be_excluded_impl().
bool SystemDictionaryShared::should_be_excluded(Klass* k) {
assert(CDSConfig::is_dumping_archive(), "sanity");
assert(CDSConfig::current_thread_is_vm_or_dumper(), "sanity");
if (k->is_objArray_klass()) {
return should_be_excluded(ObjArrayKlass::cast(k)->bottom_klass());
if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache(k)) {
// We have reached a super type that's already in the base archive. Treat it
// as "not excluded".
return false;
}
if (!k->is_instance_klass()) {
if (k->is_objArray_klass()) {
return should_be_excluded(ObjArrayKlass::cast(k)->bottom_klass());
} else if (!k->is_instance_klass()) {
assert(k->is_typeArray_klass(), "must be");
return false;
} else {
InstanceKlass* ik = InstanceKlass::cast(k);
@ -672,7 +874,7 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) {
if (!SafepointSynchronize::is_at_safepoint()) {
if (!ik->is_linked()) {
// check_for_exclusion() below doesn't link unlinked classes. We come
// should_be_excluded_impl() below doesn't link unlinked classes. We come
// here only when we are trying to aot-link constant pool entries, so
// we'd better link the class.
JavaThread* THREAD = JavaThread::current();
@ -681,6 +883,10 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) {
CLEAR_PENDING_EXCEPTION;
return true; // linking failed -- let's exclude it
}
// Also link any classes that were loaded for the verification of ik or its supertypes.
// Otherwise we might miss the verification constraints of those classes.
AOTMetaspace::link_all_loaded_classes(THREAD);
}
MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
@ -688,8 +894,17 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) {
if (p->is_excluded()) {
return true;
}
return check_for_exclusion(ik, p);
return should_be_excluded_impl(ik, p);
} else {
// When called within the CDS safepoint, the correctness of this function
// relies on the call to AOTMetaspace::link_all_loaded_classes()
// that happened right before we enter the CDS safepoint.
//
// Do not call this function in other types of safepoints. For example, if this
// is called in a GC safepoint, a klass may be improperly excluded because some
// of its verification constraints have not yet been linked.
assert(CDSConfig::is_at_aot_safepoint(), "Do not call this function in any other safepoint");
// No need to check for is_linked() as all eligible classes should have
// already been linked in AOTMetaspace::link_class_for_cds().
// Can't take the lock as we are in safepoint.
@ -697,12 +912,13 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) {
if (p->is_excluded()) {
return true;
}
return check_for_exclusion(ik, p);
return should_be_excluded_impl(ik, p);
}
}
}
void SystemDictionaryShared::finish_exclusion_checks() {
assert_at_safepoint();
if (CDSConfig::is_dumping_dynamic_archive() || CDSConfig::is_dumping_preimage_static_archive()) {
// Do this first -- if a base class is excluded due to duplication,
// all of its subclasses will also be excluded.
@ -713,7 +929,7 @@ void SystemDictionaryShared::finish_exclusion_checks() {
}
_dumptime_table->iterate_all_live_classes([&] (InstanceKlass* k, DumpTimeClassInfo& info) {
SystemDictionaryShared::check_for_exclusion(k, &info);
SystemDictionaryShared::should_be_excluded_impl(k, &info);
});
_dumptime_table->update_counts();
@ -793,7 +1009,7 @@ void SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbo
bool* skip_assignability_check) {
assert(CDSConfig::is_dumping_archive(), "sanity");
DumpTimeClassInfo* info = get_info(k);
info->add_verification_constraint(k, name, from_name, from_field_is_protected,
info->add_verification_constraint(name, from_name, from_field_is_protected,
from_is_array, from_is_object);
if (CDSConfig::is_dumping_classic_static_archive() && !is_builtin(k)) {
@ -818,6 +1034,15 @@ void SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbo
}
}
// When the old verifier is verifying the class <ik> at dump time, it tries to resolve a
// class with the given <name>. For the verification result to be valid at run time, we must
// ensure that <name> resolves to the exact same Klass as in dump time.
void SystemDictionaryShared::add_old_verification_constraint(Thread* current, InstanceKlass* ik, Symbol* name) {
precond(CDSConfig::is_preserving_verification_constraints());
DumpTimeClassInfo* info = get_info(ik);
info->add_verification_constraint(name);
}
void SystemDictionaryShared::add_enum_klass_static_field(InstanceKlass* ik, int root_index) {
assert(CDSConfig::is_dumping_heap(), "sanity");
DumpTimeClassInfo* info = get_info_locked(ik);
@ -836,6 +1061,13 @@ void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass
Symbol* name = vc->name();
Symbol* from_name = vc->from_name();
if (from_name == nullptr) {
// This is for old verifier. No need to check, as we can guarantee that all classes checked by
// the old verifier during AOT training phase cannot be replaced in the asembly phase.
precond(CDSConfig::is_dumping_final_static_archive());
continue;
}
if (log_is_enabled(Trace, aot, verification)) {
ResourceMark rm(THREAD);
log_trace(aot, verification)("check_verification_constraint: %s: %s must be subclass of %s [0x%x]",
@ -860,7 +1092,7 @@ void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass
}
}
void SystemDictionaryShared::copy_verification_constraints_from_preimage(InstanceKlass* klass) {
void SystemDictionaryShared::copy_verification_info_from_preimage(InstanceKlass* klass) {
assert(CDSConfig::is_using_archive(), "called at run time with CDS enabled only");
DumpTimeClassInfo* dt_info = get_info(klass);
RunTimeClassInfo* rt_info = RunTimeClassInfo::get_for(klass); // from preimage
@ -872,7 +1104,7 @@ void SystemDictionaryShared::copy_verification_constraints_from_preimage(Instanc
Symbol* name = vc->name();
Symbol* from_name = vc->from_name();
dt_info->add_verification_constraint(klass, name, from_name,
dt_info->add_verification_constraint(name, from_name,
rt_info->from_field_is_protected(i), rt_info->from_is_array(i), rt_info->from_is_object(i));
}
}

View File

@ -146,7 +146,7 @@ class SystemDictionaryShared: public SystemDictionary {
};
private:
class ExclusionCheckCandidates;
static DumpTimeSharedClassTable* _dumptime_table;
static ArchiveInfo _static_archive;
@ -175,14 +175,27 @@ private:
static void write_dictionary(RunTimeSharedDictionary* dictionary,
bool is_builtin);
static bool is_jfr_event_class(InstanceKlass *k);
static bool check_for_exclusion_impl(InstanceKlass* k);
static bool should_be_excluded_impl(InstanceKlass* k, DumpTimeClassInfo* info);
// exclusion checks
static void check_exclusion_for_self_and_dependencies(InstanceKlass *k);
static bool check_self_exclusion(InstanceKlass* k);
static bool check_dependencies_exclusion(InstanceKlass* k, DumpTimeClassInfo* info);
static bool check_verification_constraint_exclusion(InstanceKlass* k, Symbol* constraint_class_name);
static bool is_dependency_excluded(InstanceKlass* k, InstanceKlass* dependency, const char* type);
static bool is_excluded_verification_constraint(InstanceKlass* k, Symbol* constraint_class_name);
static Klass* find_verification_constraint_bottom_class(InstanceKlass* k, Symbol* constraint_class_name);
static void remove_dumptime_info(InstanceKlass* k) NOT_CDS_RETURN;
static bool has_been_redefined(InstanceKlass* k);
DEBUG_ONLY(static bool _class_loading_may_happen;)
static void copy_verification_constraints_from_preimage(InstanceKlass* klass);
static void copy_verification_info_from_preimage(InstanceKlass* klass);
static void copy_linking_constraints_from_preimage(InstanceKlass* klass);
template<typename Function>
static void iterate_verification_constraint_names(InstanceKlass* k, DumpTimeClassInfo* info, Function func);
public:
static bool is_early_klass(InstanceKlass* k); // Was k loaded while JvmtiExport::is_early_phase()==true
static bool has_archived_enum_objs(InstanceKlass* ik);
@ -239,6 +252,7 @@ public:
Symbol* from_name, bool from_field_is_protected,
bool from_is_array, bool from_is_object,
bool* skip_assignability_check);
static void add_old_verification_constraint(Thread* current, InstanceKlass* k, Symbol* name);
static void check_verification_constraints(InstanceKlass* klass,
TRAPS) NOT_CDS_RETURN;
static void add_enum_klass_static_field(InstanceKlass* ik, int root_index);
@ -258,7 +272,6 @@ public:
static DumpTimeSharedClassTable* dumptime_table() { return _dumptime_table; }
static bool should_be_excluded(Klass* k);
static bool check_for_exclusion(InstanceKlass* k, DumpTimeClassInfo* info);
static void validate_before_archiving(InstanceKlass* k);
static bool is_excluded_class(InstanceKlass* k);
static void set_excluded(InstanceKlass* k);

View File

@ -2839,18 +2839,20 @@ void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handl
DEBUG_ONLY(FieldInfoStream::validate_search_table(_constants, _fieldinfo_stream, _fieldinfo_search_table));
}
// Check if a class or any of its supertypes has a version older than 50.
// CDS will not perform verification of old classes during dump time because
// without changing the old verifier, the verification constraint cannot be
// retrieved during dump time.
// Verification of archived old classes will be performed during run time.
bool InstanceKlass::can_be_verified_at_dumptime() const {
if (AOTMetaspace::in_aot_cache(this)) {
// This is a class that was dumped into the base archive, so we know
// it was verified at dump time.
return true;
}
if (major_version() < 50 /*JAVA_6_VERSION*/) {
if (CDSConfig::is_preserving_verification_constraints()) {
return true;
}
if (CDSConfig::is_old_class_for_verifier(this)) {
// The old verifier does not save verification constraints, so at run time
// SystemDictionaryShared::check_verification_constraints() will not work for this class.
return false;
}
if (super() != nullptr && !super()->can_be_verified_at_dumptime()) {

View File

@ -323,9 +323,8 @@ void VirtualCallTypeData::post_initialize(BytecodeStream* stream, MethodData* md
static bool is_excluded(Klass* k) {
#if INCLUDE_CDS
if (SafepointSynchronize::is_at_safepoint() &&
CDSConfig::is_dumping_archive() &&
CDSConfig::current_thread_is_vm_or_dumper()) {
if (CDSConfig::is_at_aot_safepoint()) {
// Check for CDS exclusion only at CDS safe point.
if (k->is_instance_klass() && !InstanceKlass::cast(k)->is_loaded()) {
log_debug(aot, training)("Purged %s from MDO: unloaded class", k->name()->as_C_string());
return true;

View File

@ -554,7 +554,11 @@ void KlassTrainingData::cleanup(Visitor& visitor) {
}
visitor.visit(this);
if (has_holder()) {
bool is_excluded = !holder()->is_loaded() || SystemDictionaryShared::check_for_exclusion(holder(), nullptr);
bool is_excluded = !holder()->is_loaded();
if (CDSConfig::is_at_aot_safepoint()) {
// Check for AOT exclusion only at AOT safe point.
is_excluded |= SystemDictionaryShared::should_be_excluded(holder());
}
if (is_excluded) {
ResourceMark rm;
log_debug(aot, training)("Cleanup KTD %s", name()->as_klass_external_name());
@ -573,7 +577,8 @@ void MethodTrainingData::cleanup(Visitor& visitor) {
}
visitor.visit(this);
if (has_holder()) {
if (SystemDictionaryShared::check_for_exclusion(holder()->method_holder(), nullptr)) {
if (CDSConfig::is_at_aot_safepoint() && SystemDictionaryShared::should_be_excluded(holder()->method_holder())) {
// Check for AOT exclusion only at AOT safe point.
log_debug(aot, training)("Cleanup MTD %s::%s", name()->as_klass_external_name(), signature()->as_utf8());
if (_final_profile != nullptr && _final_profile->method() != _holder) {
log_warning(aot, training)("Stale MDO for %s::%s", name()->as_klass_external_name(), signature()->as_utf8());

View File

@ -850,6 +850,13 @@ JVM_ENTRY(jclass, JVM_FindClassFromClass(JNIEnv *env, const char *name,
log_debug(class, resolve)("%s %s (verification)", from_name, to);
}
#if INCLUDE_CDS
if (CDSConfig::is_preserving_verification_constraints() && from_class->is_instance_klass()) {
InstanceKlass* ik = InstanceKlass::cast(from_class);
SystemDictionaryShared::add_old_verification_constraint(THREAD, ik, h_name);
}
#endif
return result;
JVM_END

View File

@ -303,11 +303,11 @@ void mutex_init() {
#endif
MUTEX_DEFN(DumpTimeTable_lock , PaddedMutex , nosafepoint);
MUTEX_DEFN(CDSLambda_lock , PaddedMutex , nosafepoint);
MUTEX_DEFN(DumpRegion_lock , PaddedMutex , nosafepoint);
MUTEX_DEFL(DumpRegion_lock , PaddedMutex , DumpTimeTable_lock);
MUTEX_DEFN(ClassListFile_lock , PaddedMutex , nosafepoint);
MUTEX_DEFN(UnregisteredClassesTable_lock , PaddedMutex , nosafepoint-1);
MUTEX_DEFN(LambdaFormInvokers_lock , PaddedMutex , safepoint);
MUTEX_DEFN(ScratchObjects_lock , PaddedMutex , nosafepoint-1); // Holds DumpTimeTable_lock
MUTEX_DEFL(ScratchObjects_lock , PaddedMutex , DumpTimeTable_lock);
MUTEX_DEFN(FinalImageRecipes_lock , PaddedMutex , nosafepoint);
#endif // INCLUDE_CDS
MUTEX_DEFN(Bootclasspath_lock , PaddedMutex , nosafepoint);

View File

@ -526,6 +526,7 @@ hotspot_aot_classlinking = \
-runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java \
-runtime/cds/appcds/cacheObject/ArchivedModuleCompareTest.java \
-runtime/cds/appcds/CDSandJFR.java \
-runtime/cds/appcds/LambdaContainsOldInf.java \
-runtime/cds/appcds/customLoader/CustomClassListDump.java \
-runtime/cds/appcds/customLoader/HelloCustom_JFR.java \
-runtime/cds/appcds/customLoader/OldClassAndInf.java \
@ -533,14 +534,17 @@ hotspot_aot_classlinking = \
-runtime/cds/appcds/customLoader/ParallelTestSingleFP.java \
-runtime/cds/appcds/customLoader/SameNameInTwoLoadersTest.java \
-runtime/cds/appcds/DumpClassListWithLF.java \
-runtime/cds/appcds/dynamicArchive/ModulePath.java \
-runtime/cds/appcds/dynamicArchive/LambdaContainsOldInf.java \
-runtime/cds/appcds/dynamicArchive/LambdaCustomLoader.java \
-runtime/cds/appcds/dynamicArchive/LambdaForOldInfInBaseArchive.java \
-runtime/cds/appcds/dynamicArchive/LambdaInBaseArchive.java \
-runtime/cds/appcds/dynamicArchive/LambdasInTwoArchives.java \
-runtime/cds/appcds/dynamicArchive/ModulePath.java \
-runtime/cds/appcds/dynamicArchive/NestHostOldInf.java \
-runtime/cds/appcds/dynamicArchive/OldClassAndInf.java \
-runtime/cds/appcds/dynamicArchive/OldClassInBaseArchive.java \
-runtime/cds/appcds/dynamicArchive/OldClassVerifierTrouble.java \
-runtime/cds/appcds/dynamicArchive/RedefineCallerClassTest.java \
-runtime/cds/appcds/HelloExtTest.java \
-runtime/cds/appcds/javaldr/ExceptionDuringDumpAtObjectsInitPhase.java \
-runtime/cds/appcds/javaldr/GCDuringDump.java \

View File

@ -99,7 +99,6 @@ public class ExcludedClasses {
if (runMode == RunMode.ASSEMBLY) {
out.shouldNotMatch("aot,resolve.*archived field.*TestApp.Foo => TestApp.Foo.ShouldBeExcluded.f:I");
} else if (runMode == RunMode.PRODUCTION) {
out.shouldContain("check_verification_constraint: TestApp$Foo$Taz: TestApp$Foo$ShouldBeExcludedChild must be subclass of TestApp$Foo$ShouldBeExcluded");
out.shouldContain("jdk.jfr.Event source: jrt:/jdk.jfr");
out.shouldMatch("TestApp[$]Foo[$]ShouldBeExcluded source: .*/app.jar");
out.shouldMatch("TestApp[$]Foo[$]ShouldBeExcludedChild source: .*/app.jar");
@ -259,14 +258,9 @@ class TestApp {
static class Taz {
static ShouldBeExcluded m() {
// When verifying this method, we need to check the constraint that
// ShouldBeExcluded must be a supertype of ShouldBeExcludedChild. This information
// is checked by SystemDictionaryShared::check_verification_constraints() when the Taz
// class is linked during the production run.
//
// Because ShouldBeExcluded is excluded from the AOT archive, it must be loaded
// dynamically from app.jar inside SystemDictionaryShared::check_verification_constraints().
// This must happen after the app class loader has been fully restored from the AOT cache.
// Taz should be excluded from the AOT cache because it has a verification constraint that
// "ShouldBeExcludedChild must be a subtype of ShouldBeExcluded", but ShouldBeExcluded is
// excluded from the AOT cache.
return new ShouldBeExcludedChild();
}
static void hotSpot4() {

View File

@ -0,0 +1,38 @@
/*
* 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.
*
*/
super public class OldA
version 49:0
{
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
}

View File

@ -0,0 +1,162 @@
/*
* 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 Store old classes linked state in AOT cache as long as their verification constraints are not excluded.
* @bug 8317269
* @requires vm.cds.supports.aot.class.linking
* @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes
* @build OldClass OldA OldClassWithVerifierConstraints OldClassWithExcludedVerifierConstraints
* @build OldClassSupport
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
* AppUsesOldClass MyIntf OldClass OldA NewB MyEvent MyEvent2
* OldClassWithVerifierConstraints
* OldClassWithExcludedVerifierConstraints
* NewClassWithExcludedVerifierConstraints
* @run driver OldClassSupport
*/
import jdk.jfr.Event;
import jdk.test.lib.cds.CDSAppTester;
import jdk.test.lib.helpers.ClassFileInstaller;
import jdk.test.lib.process.OutputAnalyzer;
public class OldClassSupport {
static final String appJar = ClassFileInstaller.getJarPath("app.jar");
static final String mainClass = "AppUsesOldClass";
public static void main(String[] args) throws Exception {
Tester tester = new Tester();
tester.run(new String[] {"AOT", "--two-step-training"} );
}
static class Tester extends CDSAppTester {
public Tester() {
super(mainClass);
}
@Override
public String classpath(RunMode runMode) {
return appJar;
}
@Override
public String[] vmArgs(RunMode runMode) {
return new String[] {
"-Xlog:aot+class=debug",
"-Xlog:aot+resolve=trace",
};
}
@Override
public String[] appCommandLine(RunMode runMode) {
return new String[] {"-Xlog:cds+class=debug", mainClass};
}
@Override
public void checkExecution(OutputAnalyzer out, RunMode runMode) {
Class[] included = {
OldClass.class,
OldA.class,
NewB.class,
OldClassWithVerifierConstraints.class,
};
Class[] excluded = {
OldClassWithExcludedVerifierConstraints.class,
NewClassWithExcludedVerifierConstraints.class,
};
if (runMode == RunMode.TRAINING) {
shouldInclude(out, false, included);
shouldNotInclude(out, excluded);
shouldSkip(out, excluded);
} else if (runMode == RunMode.ASSEMBLY) {
shouldInclude(out, true, included);
shouldNotInclude(out, excluded);
}
}
}
static void shouldInclude(OutputAnalyzer out, boolean linked, Class[] classes) {
for (Class c : classes) {
out.shouldMatch("aot,class.* = 0x.* app *" + c.getName() + (linked ? " .*aot-linked" : ""));
}
}
static void shouldNotInclude(OutputAnalyzer out, Class[] classes) {
for (Class c : classes) {
out.shouldNotMatch("aot,class.* = 0x.* app *" + c.getName());
}
}
static void shouldSkip(OutputAnalyzer out, Class[] classes) {
for (Class c : classes) {
out.shouldMatch("Skipping " + c.getName() + ": verification constraint .* is excluded");
}
}
}
class AppUsesOldClass {
public static void main(String args[]) {
System.out.println("Old Class Instance: " + new OldClass());
System.out.println(get_OldA_from_NewB());
System.out.println(OldClassWithVerifierConstraints.get_OldA_from_NewB());
System.out.println(OldClassWithExcludedVerifierConstraints.get_Event_from_MyEvent());
System.out.println(NewClassWithExcludedVerifierConstraints.get_MyEvent_from_MyEvent2());
System.out.println(new MyEvent());
// OldClassWithExcludedVerifierConstraints should still be excluded even it has been used
// in a lambda expression during the training run.
run((OldClassWithExcludedVerifierConstraints x) -> {
System.out.println(x);
});
}
static OldA get_OldA_from_NewB() {
return new NewB();
}
static void run(MyIntf intf) {
intf.function(new OldClassWithExcludedVerifierConstraints());
}
}
interface MyIntf {
public void function(OldClassWithExcludedVerifierConstraints x);
}
class NewB extends OldA {}
class MyEvent extends Event {}
class MyEvent2 extends MyEvent {}
class NewClassWithExcludedVerifierConstraints {
static MyEvent get_MyEvent_from_MyEvent2() {
return new MyEvent2();
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.
*
*/
// This old class has a verification constraint that "MyEvent must be a subtype of Event". However,
// Event and all of its subtypes are excluded from the AOT cache, so this class must also be excluded.
super public class OldClassWithExcludedVerifierConstraints
version 49:0
{
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
static Method get_Event_from_MyEvent:"()Ljdk/jfr/Event;"
stack 2 locals 0
{
new class MyEvent;
dup;
invokespecial Method MyEvent."<init>":"()V";
areturn;
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.
*
*/
// This old class as a verification constraint that "NewB must be a subtype of OldA". Since both
// OldA and NewB are not excluded, then this class should be cached in aot-linked state.
super public class OldClassWithVerifierConstraints
version 49:0
{
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
static Method get_OldA_from_NewB:"()LOldA;"
stack 2 locals 0
{
new class NewB;
dup;
invokespecial Method NewB."<init>":"()V";
areturn;
}
}

View File

@ -0,0 +1,294 @@
/*
* Copyright (c) 2023, 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
* @bug 8317269
* @requires vm.cds
* @requires vm.cds.supports.aot.class.linking
* @summary Test for verification of classes that are aot-linked
* @library /test/jdk/lib/testlibrary
* /test/lib
* /test/hotspot/jtreg/runtime/cds/appcds
* /test/hotspot/jtreg/runtime/cds/appcds/test-classes
* @build GoodOldClass
* BadOldClass BadOldClass2 BadOldClass3 BadOldClass4
* BadNewClass BadNewClass2 BadNewClass3 BadNewClass4
* @build AOTClassLinkingVerification
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app1.jar
* AOTClassLinkingVerificationApp
* Unlinked UnlinkedSuper
* BadOldClass
* BadOldClass2
* BadOldClass3
* BadOldClass4
* BadNewClass
* BadNewClass2
* BadNewClass3
* BadNewClass4
* GoodOldClass Vehicle Car
* Util
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app2.jar
* Foo NotFoo
* UnlinkedSub
* @run driver AOTClassLinkingVerification
*/
import java.io.File;
import java.lang.invoke.MethodHandles;
import jdk.test.lib.cds.CDSAppTester;
import jdk.test.lib.helpers.ClassFileInstaller;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.whitebox.WhiteBox;
public class AOTClassLinkingVerification {
static final String app1Jar = ClassFileInstaller.getJarPath("app1.jar");
static final String app2Jar = ClassFileInstaller.getJarPath("app2.jar");
static final String wbJar = TestCommon.getTestJar("WhiteBox.jar");
static final String bootAppendWhiteBox = "-Xbootclasspath/a:" + wbJar;
static final String mainClass = AOTClassLinkingVerificationApp.class.getName();
static class Tester extends CDSAppTester {
public Tester(String testName) {
super(testName);
}
@Override
public String[] vmArgs(RunMode runMode) {
if (runMode == RunMode.TRAINING ||
runMode == RunMode.ASSEMBLY) {
return new String[] {
"-XX:+AOTClassLinking", "-Xlog:cds+class=debug", bootAppendWhiteBox,
};
} else {
return new String[] {
"-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", bootAppendWhiteBox,
};
}
}
@Override
public String classpath(RunMode runMode) {
if (runMode == RunMode.TRAINING ||
runMode == RunMode.ASSEMBLY) {
return app1Jar;
} else {
return app1Jar + File.pathSeparator + app2Jar;
}
}
@Override
public String[] appCommandLine(RunMode runMode) {
if (runMode == RunMode.TRAINING ||
runMode == RunMode.ASSEMBLY) {
return new String[] {
"AOTClassLinkingVerificationApp", app1Jar, "ASSEMBLY"
};
} else {
return new String[] {
"AOTClassLinkingVerificationApp", app1Jar, "PRODUCTION"
};
}
}
@Override
public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
if (runMode == RunMode.TRAINING) {
out.shouldContain("Preload Warning: Verification failed for BadNewClass");
out.shouldContain("Preload Warning: Verification failed for BadNewClass2");
out.shouldContain("Preload Warning: Verification failed for BadNewClass3");
out.shouldContain("Preload Warning: Verification failed for BadNewClass4");
out.shouldContain("Preload Warning: Verification failed for BadOldClass");
out.shouldContain("Preload Warning: Verification failed for BadOldClass2");
out.shouldContain("Preload Warning: Verification failed for BadOldClass3");
out.shouldContain("Preload Warning: Verification failed for BadOldClass4");
out.shouldContain("Preload Warning: Verification failed for Unlinked");
}
}
}
public static void main(String[] args) throws Exception {
// Dump without app2.jar so:
// - Unlinked can be resolved, but UnlinkedSuper UnlinkedSub cannot be resolved,
// so Unlinked cannot be verified at dump time.
// - BadOldClass2 can be resolved, but Foo and NotFoo cannot be resolved,
// so BadOldClass2 cannot be verified at dump time.
// - BadNewClass2 can be resolved, but Foo and NotFoo cannot be resolved,
// so BadNewClass2 cannot be verified at dump time.
Tester t1 = new Tester("verification-aot-linked-classes");
t1.run("AOT");
}
}
class AOTClassLinkingVerificationApp {
static WhiteBox wb = WhiteBox.getWhiteBox();
static ClassLoader classLoader = AOTClassLinkingVerificationApp.class.getClassLoader();
static File app1Jar;
static boolean isProduction;
public static void main(String[] args) throws Exception {
app1Jar = new File(args[0]);
isProduction = args[1].equals("PRODUCTION");
if (isProduction) {
assertNotShared(UnlinkedSub.class);
assertShared(UnlinkedSuper.class);
assertNotShared(Unlinked.class); // failed verification during dump time
assertNotShared(Foo.class);
assertNotShared(NotFoo.class);
}
String s = null;
try {
s = Unlinked.doit();
} catch (NoClassDefFoundError ncdfe) {
// UnlinkedSub is in app2Jar but only app1Jar is used during training
// and assembly phases. So NoClassDefFoundError is expected during
// during training and assembly phases.
if (isProduction) {
throw ncdfe;
}
}
if (isProduction && !s.equals("heyhey")) {
throw new RuntimeException("Unlinked.doit() returns wrong result: " + s);
}
// ===============================================================================
checkSimpleBadClass("BadOldClass");
Class cls_BadOldClass2 = Class.forName("BadOldClass2", false, classLoader);
if (isProduction) {
assertNotShared(cls_BadOldClass2); // failed verification during dump time
}
try {
cls_BadOldClass2.newInstance();
throw new RuntimeException("BadOldClass2 cannot be verified");
} catch (NoClassDefFoundError ncdfe) {
// BadOldClass2 loads Foo and NotFoo which is in app2Jar which is used
// only in production run.
if (isProduction) {
throw ncdfe;
}
} catch (VerifyError expected) {}
checkSimpleBadClass("BadOldClass3");
checkSimpleBadClass("BadOldClass4");
// ===============================================================================
checkSimpleBadClass("BadNewClass");
Class cls_BadNewClass2 = Class.forName("BadNewClass2", false, classLoader);
if (isProduction) {
assertNotShared(cls_BadNewClass2); // failed verification during dump time
}
try {
cls_BadNewClass2.newInstance();
throw new RuntimeException("BadNewClass2 cannot be verified");
} catch (NoClassDefFoundError ncdfe) {
// BadNewClass2 loads Foo and NotFoo which is in app2Jar which is used
// only in production run.
if (isProduction) {
throw ncdfe;
}
} catch (VerifyError expected) {}
checkSimpleBadClass("BadNewClass3");
checkSimpleBadClass("BadNewClass4");
// ===============================================================================
if (isProduction) {
assertAlreadyLoaded("Vehicle");
assertAlreadyLoaded("Car");
assertAlreadyLoaded("GoodOldClass");
assertShared(GoodOldClass.class);
assertShared(Vehicle.class);
assertShared(Car.class);
}
GoodOldClass.doit(); // Should not fail
}
static void checkSimpleBadClass(String className) throws Exception {
Class cls = Class.forName(className, false, classLoader);
if (isProduction) {
assertNotShared(cls); // failed verification during dump time
}
try {
cls.newInstance();
throw new RuntimeException(className + " should not pass verification");
} catch (VerifyError expected) {}
}
static void assertShared(Class c) {
if (!wb.isSharedClass(c)) {
throw new RuntimeException("wb.isSharedClass(" + c.getName() + ") should be true");
}
}
static void assertNotShared(Class c) {
if (wb.isSharedClass(c)) {
throw new RuntimeException("wb.isSharedClass(" + c.getName() + ") should be false");
}
}
static void assertAlreadyLoaded(String className) throws Exception {
byte[] data = Util.getClassFileFromJar(app1Jar, className);
try {
MethodHandles.lookup().defineClass(data);
} catch (LinkageError e) {
if (e.getMessage().contains("duplicate class definition for " + className)) {
return;
} else {
throw e;
}
}
throw new RuntimeException(className + " must have already been loaded");
}
}
class Unlinked {
static String doit() {
UnlinkedSuper sup = new UnlinkedSub();
return sup.doit();
}
}
abstract class UnlinkedSuper {
abstract String doit();
}
class UnlinkedSub extends UnlinkedSuper {
String doit() {
return "heyhey";
}
}
class Foo {}
class NotFoo {}
class Vehicle {}
class Car extends Vehicle {}

View File

@ -0,0 +1,52 @@
/*
* 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.
*
*/
super public class BadNewClass
version 52:0
{
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
/*
* The following method tries to return an Object as a String.
* Verifier should fail.
*/
public Method doit:"()Ljava/lang/String;"
stack 2 locals 1
{
new class java/lang/Object;
dup;
invokespecial Method java/lang/Object."<init>":"()V";
astore_0;
aload_0;
areturn; // tries to return an Object as a String
}
}

View File

@ -0,0 +1,52 @@
/*
* 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.
*
*/
super public class BadNewClass2
version 52:0
{
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
/*
* The following method tries to return a NotFoo as a Foo.
* Verifier should fail.
*/
public Method doit:"()LFoo;"
stack 2 locals 1
{
new class NotFoo;
dup;
invokespecial Method NotFoo."<init>":"()V";
astore_0;
aload_0;
areturn; // tries to return a NotFoo as a Foo
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.
*
*/
super public class BadNewClass3
version 52:0
{
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
/*
* The following method tries to return a String[][] as an Integer[].
* Verifier should fail.
*
* Note: the arrays must have different number of dimensions, or else
* the new verifier will just check the "bottom" classes. I.e., String and Integer
*/
public Method doit:"()[Ljava/lang/Integer;"
stack 2 locals 1
{
iconst_1;
iconst_1;
multianewarray class "[[Ljava/lang/String;", 2;
areturn;
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.
*
*/
super public class BadNewClass4
version 52:0
{
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
/*
* The following method tries to return a String[][] as an Integer[][].
* Verifier should fail.
*
* Note: the new verifier looks up the Integer and String types,
* not the array types.
*/
public Method doit:"()[[Ljava/lang/Integer;"
stack 2 locals 1
{
iconst_1;
iconst_1;
multianewarray class "[[Ljava/lang/String;", 2;
areturn;
}
}

View File

@ -0,0 +1,52 @@
/*
* 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.
*
*/
super public class BadOldClass
version 49:0
{
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
/*
* The following method tries to return an Object as a String.
* Verifier should fail.
*/
public Method doit:"()Ljava/lang/String;"
stack 2 locals 1
{
new class java/lang/Object;
dup;
invokespecial Method java/lang/Object."<init>":"()V";
astore_0;
aload_0;
areturn; // tries to return an Object as a String
}
}

View File

@ -0,0 +1,52 @@
/*
* 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.
*
*/
super public class BadOldClass2
version 49:0
{
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
/*
* The following method tries to return a NotFoo as a Foo.
* Verifier should fail.
*/
public Method doit:"()LFoo;"
stack 2 locals 1
{
new class NotFoo;
dup;
invokespecial Method NotFoo."<init>":"()V";
astore_0;
aload_0;
areturn; // tries to return a NotFoo as a Foo
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.
*
*/
super public class BadOldClass3
version 49:0
{
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
/*
* The following method tries to return a String[][] as an Integer[].
* Verifier should fail.
*
* Note: the arrays have different number of dimensions. The old verifier
* rejects this immediately without looking up the String/Integer types.
*/
public Method doit:"()[Ljava/lang/Integer;"
stack 2 locals 1
{
iconst_1;
iconst_1;
multianewarray class "[[Ljava/lang/String;", 2;
areturn;
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.
*
*/
super public class BadOldClass4
version 49:0
{
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
/*
* The following method tries to return a String[][] as an Integer[][].
* Verifier should fail.
*
* Note: the old verifier looks up the Integer and String types,
* not the array types.
*/
public Method doit:"()[[Ljava/lang/Integer;"
stack 2 locals 1
{
iconst_1;
iconst_1;
multianewarray class "[[Ljava/lang/String;", 2;
areturn;
}
}

View File

@ -147,7 +147,7 @@ public class BulkLoaderTest {
@Override
public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
if (isAOTWorkflow() && runMode == RunMode.TRAINING) {
out.shouldContain("Skipping BadOldClassA: Unlinked class not supported by AOTConfiguration");
out.shouldContain("Skipping BadOldClassA: Failed verification");
out.shouldContain("Skipping SimpleCusty: Duplicated unregistered class");
}

View File

@ -0,0 +1,49 @@
/*
* 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.
*
*/
super public class GoodOldClass
version 49:0
{
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
public static Method doit:"()LVehicle;"
stack 2 locals 1
{
new class Car;
dup;
invokespecial Method Car."<init>":"()V";
astore_0;
aload_0;
areturn; // tries to return a Car as a Vehicle
}
}