diff --git a/src/hotspot/share/cds/aotArtifactFinder.cpp b/src/hotspot/share/cds/aotArtifactFinder.cpp index d8999774a53..adcc4bfb50a 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.cpp +++ b/src/hotspot/share/cds/aotArtifactFinder.cpp @@ -30,6 +30,7 @@ #include "cds/dumpTimeClassInfo.inline.hpp" #include "cds/heapShared.hpp" #include "cds/lambdaProxyClassDictionary.hpp" +#include "cds/regeneratedClasses.hpp" #include "classfile/systemDictionaryShared.hpp" #include "logging/log.hpp" #include "memory/metaspaceClosure.hpp" @@ -188,7 +189,11 @@ void AOTArtifactFinder::end_scanning_for_oops() { void AOTArtifactFinder::add_aot_inited_class(InstanceKlass* ik) { if (CDSConfig::is_initing_classes_at_dump_time()) { - assert(ik->is_initialized(), "must be"); + if (RegeneratedClasses::is_regenerated_object(ik)) { + precond(RegeneratedClasses::get_original_object(ik)->is_initialized()); + } else { + precond(ik->is_initialized()); + } add_cached_instance_class(ik); bool created; @@ -243,6 +248,11 @@ void AOTArtifactFinder::add_cached_instance_class(InstanceKlass* ik) { add_cached_instance_class(intf); } + InstanceKlass* nest_host = ik->nest_host_or_null(); + if (nest_host != nullptr) { + add_cached_instance_class(nest_host); + } + if (CDSConfig::is_dumping_final_static_archive() && ik->defined_by_other_loaders()) { // The following are not appliable to unregistered classes return; diff --git a/src/hotspot/share/cds/aotClassInitializer.cpp b/src/hotspot/share/cds/aotClassInitializer.cpp index 46a118c42e9..db18a8ee1b5 100644 --- a/src/hotspot/share/cds/aotClassInitializer.cpp +++ b/src/hotspot/share/cds/aotClassInitializer.cpp @@ -26,6 +26,7 @@ #include "cds/archiveBuilder.hpp" #include "cds/cdsConfig.hpp" #include "cds/heapShared.hpp" +#include "cds/regeneratedClasses.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionaryShared.hpp" #include "classfile/vmSymbols.hpp" @@ -103,6 +104,10 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) { return false; } + if (RegeneratedClasses::is_regenerated_object(ik)) { + ik = RegeneratedClasses::get_original_object(ik); + } + if (!ik->is_initialized() && !ik->is_being_initialized()) { return false; } diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index 5f197f70ecc..fca9d970cd4 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -575,6 +575,9 @@ ArchiveBuilder::FollowMode ArchiveBuilder::get_follow_mode(MetaspaceClosure::Ref if (ref->msotype() == MetaspaceObj::ClassType) { Klass* klass = (Klass*)ref->obj(); assert(klass->is_klass(), "must be"); + if (RegeneratedClasses::has_been_regenerated(klass)) { + klass = RegeneratedClasses::get_regenerated_object(klass); + } if (is_excluded(klass)) { ResourceMark rm; log_debug(cds, dynamic)("Skipping class (excluded): %s", klass->external_name()); diff --git a/src/hotspot/share/cds/archiveHeapWriter.cpp b/src/hotspot/share/cds/archiveHeapWriter.cpp index ee1e334e84b..db93027e348 100644 --- a/src/hotspot/share/cds/archiveHeapWriter.cpp +++ b/src/hotspot/share/cds/archiveHeapWriter.cpp @@ -27,6 +27,7 @@ #include "cds/cdsConfig.hpp" #include "cds/filemap.hpp" #include "cds/heapShared.hpp" +#include "cds/regeneratedClasses.hpp" #include "classfile/javaClasses.hpp" #include "classfile/modules.hpp" #include "classfile/systemDictionary.hpp" @@ -543,6 +544,10 @@ template void ArchiveHeapWriter::relocate_field_in_buffer(T* field_ oop source_referent = load_source_oop_from_buffer(field_addr_in_buffer); if (source_referent != nullptr) { if (java_lang_Class::is_instance(source_referent)) { + Klass* k = java_lang_Class::as_Klass(source_referent); + if (RegeneratedClasses::has_been_regenerated(k)) { + source_referent = RegeneratedClasses::get_regenerated_object(k)->java_mirror(); + } // When the source object points to a "real" mirror, the buffered object should point // to the "scratch" mirror, which has all unarchivable fields scrubbed (to be reinstated // at run time). @@ -754,6 +759,11 @@ void ArchiveHeapWriter::compute_ptrmap(ArchiveHeapInfo* heap_info) { Metadata** buffered_field_addr = requested_addr_to_buffered_addr(requested_field_addr); Metadata* native_ptr = *buffered_field_addr; guarantee(native_ptr != nullptr, "sanity"); + + if (RegeneratedClasses::has_been_regenerated(native_ptr)) { + native_ptr = RegeneratedClasses::get_regenerated_object(native_ptr); + } + guarantee(ArchiveBuilder::current()->has_been_buffered((address)native_ptr), "Metadata %p should have been archived", native_ptr); diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index c19c0776465..ad0374c04eb 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -817,9 +817,6 @@ bool CDSConfig::is_dumping_regenerated_lambdaform_invokers() { // that point to the lambda form invokers in the base archive. Such pointers will // be invalid if lambda form invokers are regenerated in the dynamic archive. return false; - } else if (CDSConfig::is_dumping_method_handles()) { - // Work around JDK-8310831, as some methods in lambda form holder classes may not get generated. - return false; } else { return is_dumping_archive(); } diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 06cbaf1dbe7..df045b40583 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -36,6 +36,7 @@ #include "cds/cdsHeapVerifier.hpp" #include "cds/heapShared.hpp" #include "cds/metaspaceShared.hpp" +#include "cds/regeneratedClasses.hpp" #include "classfile/classLoaderData.hpp" #include "classfile/classLoaderExt.hpp" #include "classfile/javaClasses.inline.hpp" @@ -337,6 +338,9 @@ bool HeapShared::archive_object(oop obj, oop referrer, KlassSubGraphInfo* subgra } else if (java_lang_invoke_ResolvedMethodName::is_instance(obj)) { Method* m = java_lang_invoke_ResolvedMethodName::vmtarget(obj); if (m != nullptr) { + if (RegeneratedClasses::has_been_regenerated(m)) { + m = RegeneratedClasses::get_regenerated_object(m); + } InstanceKlass* method_holder = m->method_holder(); AOTArtifactFinder::add_cached_class(method_holder); } @@ -506,10 +510,17 @@ void HeapShared::copy_and_rescan_aot_inited_mirror(InstanceKlass* ik) { ik->set_is_runtime_setup_required(); } - oop orig_mirror = ik->java_mirror(); - oop m = scratch_java_mirror(ik); - assert(ik->is_initialized(), "must be"); + oop orig_mirror; + if (RegeneratedClasses::is_regenerated_object(ik)) { + InstanceKlass* orig_ik = RegeneratedClasses::get_original_object(ik); + precond(orig_ik->is_initialized()); + orig_mirror = orig_ik->java_mirror(); + } else { + precond(ik->is_initialized()); + orig_mirror = ik->java_mirror(); + } + oop m = scratch_java_mirror(ik); int nfields = 0; for (JavaFieldStream fs(ik); !fs.done(); fs.next()) { if (fs.access_flags().is_static()) { @@ -1520,6 +1531,13 @@ bool HeapShared::walk_one_object(PendingOopStack* stack, int level, KlassSubGrap p2i(scratch_java_mirror(orig_obj))); } + if (java_lang_Class::is_instance(orig_obj)) { + Klass* k = java_lang_Class::as_Klass(orig_obj); + if (RegeneratedClasses::has_been_regenerated(k)) { + orig_obj = RegeneratedClasses::get_regenerated_object(k)->java_mirror(); + } + } + if (CDSConfig::is_initing_classes_at_dump_time()) { if (java_lang_Class::is_instance(orig_obj)) { orig_obj = scratch_java_mirror(orig_obj); diff --git a/src/hotspot/share/cds/lambdaFormInvokers.cpp b/src/hotspot/share/cds/lambdaFormInvokers.cpp index d6a51c87513..ab91e76e923 100644 --- a/src/hotspot/share/cds/lambdaFormInvokers.cpp +++ b/src/hotspot/share/cds/lambdaFormInvokers.cpp @@ -224,6 +224,7 @@ void LambdaFormInvokers::regenerate_class(char* class_name, ClassFileStream& st, result->set_is_generated_shared_class(); if (!klass->is_shared()) { + log_info(aot, lambda)("regenerate_class excluding klass %s %s", class_name, klass->name()->as_C_string()); SystemDictionaryShared::set_excluded(InstanceKlass::cast(klass)); // exclude the existing class from dump } log_info(aot, lambda)("Regenerated class %s, old: " INTPTR_FORMAT " new: " INTPTR_FORMAT, diff --git a/src/hotspot/share/cds/regeneratedClasses.cpp b/src/hotspot/share/cds/regeneratedClasses.cpp index 38bf1a11952..f956e9b5255 100644 --- a/src/hotspot/share/cds/regeneratedClasses.cpp +++ b/src/hotspot/share/cds/regeneratedClasses.cpp @@ -34,7 +34,8 @@ #include "utilities/resourceHash.hpp" using RegeneratedObjTable = ResourceHashtable; -static RegeneratedObjTable* _renegerated_objs = nullptr; // InstanceKlass* and Method* +static RegeneratedObjTable* _regenerated_objs = nullptr; // InstanceKlass* and Method* orig_obj -> regen_obj +static RegeneratedObjTable* _original_objs = nullptr; // InstanceKlass* and Method* regen_obj -> orig_obj static GrowableArrayCHeap* _regenerated_mirrors = nullptr; // The regenerated Klass is not added to any class loader, so we need @@ -46,11 +47,15 @@ void RegeneratedClasses::add_class(InstanceKlass* orig_klass, InstanceKlass* reg } _regenerated_mirrors->append(OopHandle(Universe::vm_global(), regen_klass->java_mirror())); - if (_renegerated_objs == nullptr) { - _renegerated_objs = new (mtClass)RegeneratedObjTable(); + if (_regenerated_objs == nullptr) { + _regenerated_objs = new (mtClass)RegeneratedObjTable(); + } + if (_original_objs == nullptr) { + _original_objs = new (mtClass)RegeneratedObjTable(); } - _renegerated_objs->put((address)orig_klass, (address)regen_klass); + _regenerated_objs->put((address)orig_klass, (address)regen_klass); + _original_objs->put((address)regen_klass, (address)orig_klass); Array* methods = orig_klass->methods(); for (int i = 0; i < methods->length(); i++) { Method* orig_m = methods->at(i); @@ -60,26 +65,49 @@ void RegeneratedClasses::add_class(InstanceKlass* orig_klass, InstanceKlass* reg log_warning(aot)("Method in original class is missing from regenerated class: " INTPTR_FORMAT " %s", p2i(orig_m), orig_m->external_name()); } else { - _renegerated_objs->put((address)orig_m, (address)regen_m); + _regenerated_objs->put((address)orig_m, (address)regen_m); + _original_objs->put((address)regen_m, (address)orig_m); } } } bool RegeneratedClasses::has_been_regenerated(address orig_obj) { - if (_renegerated_objs == nullptr) { + if (_regenerated_objs == nullptr) { return false; } else { - return _renegerated_objs->get(orig_obj) != nullptr; + return _regenerated_objs->get(orig_obj) != nullptr; } } +address RegeneratedClasses::get_regenerated_object(address orig_obj) { + assert(_regenerated_objs != nullptr, "must be"); + address* p =_regenerated_objs->get(orig_obj); + assert(p != nullptr, "must be"); + return *p; +} + +bool RegeneratedClasses::is_regenerated_object(address regen_obj) { + if (_original_objs == nullptr) { + return false; + } else { + return _original_objs->get(regen_obj) != nullptr; + } +} + +address RegeneratedClasses::get_original_object(address regen_obj) { + assert(_original_objs != nullptr, "must be"); + address* p =_original_objs->get(regen_obj); + assert(p != nullptr, "must be"); + return *p; +} + void RegeneratedClasses::record_regenerated_objects() { assert_locked_or_safepoint(DumpTimeTable_lock); - if (_renegerated_objs != nullptr) { + if (_regenerated_objs != nullptr) { auto doit = [&] (address orig_obj, address regen_obj) { ArchiveBuilder::current()->record_regenerated_object(orig_obj, regen_obj); }; - _renegerated_objs->iterate_all(doit); + _regenerated_objs->iterate_all(doit); } } @@ -92,7 +120,7 @@ void RegeneratedClasses::cleanup() { delete _regenerated_mirrors; _regenerated_mirrors = nullptr; } - if (_renegerated_objs != nullptr) { - delete _renegerated_objs; + if (_regenerated_objs != nullptr) { + delete _regenerated_objs; } } diff --git a/src/hotspot/share/cds/regeneratedClasses.hpp b/src/hotspot/share/cds/regeneratedClasses.hpp index a1edaffe529..080d84a2154 100644 --- a/src/hotspot/share/cds/regeneratedClasses.hpp +++ b/src/hotspot/share/cds/regeneratedClasses.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -43,7 +43,26 @@ class RegeneratedClasses : public AllStatic { static void add_class(InstanceKlass* orig_klass, InstanceKlass* regen_klass); static void cleanup(); static bool has_been_regenerated(address orig_obj); + static address get_regenerated_object(address orig_obj); // orig_obj -> regen_obj static void record_regenerated_objects(); + + // Handy functions to avoid type casts + template static bool has_been_regenerated(T orig_obj) { + return has_been_regenerated((address)orig_obj); + } + template static T get_regenerated_object(T orig_obj) { + return (T)get_regenerated_object((address)orig_obj); + } + + static bool is_regenerated_object(address regen_obj); + static address get_original_object(address regen_obj); // regen_obj -> orig_obj + + template static bool is_regenerated_object(T regen_obj) { + return is_regenerated_object((address)regen_obj); + } + template static T get_original_object(T regen_obj) { + return (T)get_original_object((address)regen_obj); + } }; #endif // SHARE_CDS_REGENERATEDCLASSES_HPP diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index 8bd09a0d947..31ff7777bf9 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -348,6 +348,13 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) { } } + 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()); + return true; + } + return false; // false == k should NOT be excluded } diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index 2512d8d869d..131e8f145f1 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -444,6 +444,9 @@ public: assert(_nest_host != nullptr, "must be"); return _nest_host; } + InstanceKlass* nest_host_or_null() { + return _nest_host; + } // Used to construct informative IllegalAccessError messages at a higher level, // if there was an issue resolving or validating the nest host. // Returns null if there was no error. diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/NestHostOldInf.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/NestHostOldInf.java index 428e9c83df1..99ab68bda49 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/NestHostOldInf.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/NestHostOldInf.java @@ -82,7 +82,7 @@ public class NestHostOldInf extends DynamicArchiveTestBase { output.shouldHaveExitValue(0) .shouldMatch(".class.load. OldInf source:.*oldclassapp.jar") .shouldMatch(".class.load. ChildOldInf source:.*oldclassapp.jar") - .shouldContain("ChildOldInf$InnerChild source: shared objects file (top)") + .shouldMatch(".class.load. ChildOldInf[$]InnerChild source:.*oldclassapp.jar") .shouldMatch(".class.load. ChildOldInf[$]InnerChild[$][$]Lambda.*/0x.*source:.ChildOldInf"); }); }