1 /* 2 * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #include "cds/aotConstantPoolResolver.hpp" 26 #include "cds/archiveBuilder.hpp" 27 #include "cds/archiveUtils.inline.hpp" 28 #include "cds/cdsConfig.hpp" 29 #include "cds/finalImageRecipes.hpp" 30 #include "classfile/classLoader.hpp" 31 #include "classfile/javaClasses.hpp" 32 #include "classfile/systemDictionary.hpp" 33 #include "classfile/systemDictionaryShared.hpp" 34 #include "classfile/vmClasses.hpp" 35 #include "memory/oopFactory.hpp" 36 #include "memory/resourceArea.hpp" 37 #include "oops/constantPool.inline.hpp" 38 #include "runtime/handles.inline.hpp" 39 40 GrowableArray<InstanceKlass*>* FinalImageRecipes::_tmp_reflect_klasses = nullptr; 41 GrowableArray<int>* FinalImageRecipes::_tmp_reflect_flags = nullptr; 42 GrowableArray<FinalImageRecipes::TmpDynamicProxyClassInfo>* FinalImageRecipes::_tmp_dynamic_proxy_classes = nullptr; 43 static FinalImageRecipes* _final_image_recipes = nullptr; 44 45 void* FinalImageRecipes::operator new(size_t size) throw() { 46 return ArchiveBuilder::current()->ro_region_alloc(size); 47 } 48 49 void FinalImageRecipes::record_recipes_impl() { 50 assert(CDSConfig::is_dumping_preimage_static_archive(), "must be"); 51 ResourceMark rm; 52 GrowableArray<Klass*>* klasses = ArchiveBuilder::current()->klasses(); 53 54 // The recipes are recorded regardless of CDSConfig::is_dumping_{invokedynamic,dynamic_proxies,reflection_data}(). 55 // If some of these options are not enabled, the corresponding recipes will be 56 // ignored during the final image assembly. 57 58 // InvokeDynamic 59 // Record the indys that have been resolved in the training run. These indys will be 60 // resolved during the final image assembly. 61 62 GrowableArray<InstanceKlass*> tmp_indy_klasses; 63 GrowableArray<Array<int>*> tmp_indy_cp_indices; 64 int total_indys_to_resolve = 0; 65 for (int i = 0; i < klasses->length(); i++) { 66 Klass* k = klasses->at(i); 67 if (k->is_instance_klass()) { 68 InstanceKlass* ik = InstanceKlass::cast(k); 69 GrowableArray<int> indices; 70 71 if (ik->constants()->cache() != nullptr) { 72 Array<ResolvedIndyEntry>* tmp_indy_entries = ik->constants()->cache()->resolved_indy_entries(); 73 if (tmp_indy_entries != nullptr) { 74 for (int i = 0; i < tmp_indy_entries->length(); i++) { 75 ResolvedIndyEntry* rie = tmp_indy_entries->adr_at(i); 76 int cp_index = rie->constant_pool_index(); 77 if (rie->is_resolved()) { 78 indices.append(cp_index); 79 } 80 } 81 } 82 } 83 84 if (indices.length() > 0) { 85 tmp_indy_klasses.append(ArchiveBuilder::current()->get_buffered_addr(ik)); 86 tmp_indy_cp_indices.append(ArchiveUtils::archive_array(&indices)); 87 total_indys_to_resolve += indices.length(); 88 } 89 } 90 } 91 92 _all_klasses = ArchiveUtils::archive_array(klasses); 93 ArchivePtrMarker::mark_pointer(&_all_klasses); 94 95 assert(tmp_indy_klasses.length() == tmp_indy_cp_indices.length(), "must be"); 96 if (tmp_indy_klasses.length() > 0) { 97 _indy_klasses = ArchiveUtils::archive_array(&tmp_indy_klasses); 98 _indy_cp_indices = ArchiveUtils::archive_array(&tmp_indy_cp_indices); 99 100 ArchivePtrMarker::mark_pointer(&_indy_klasses); 101 ArchivePtrMarker::mark_pointer(&_indy_cp_indices); 102 } 103 log_info(cds)("%d indies in %d classes will be resolved in final CDS image", total_indys_to_resolve, tmp_indy_klasses.length()); 104 105 // Reflection Data 106 int reflect_count = 0; 107 if (_tmp_reflect_klasses != nullptr) { 108 for (int i = _tmp_reflect_klasses->length() - 1; i >= 0; i--) { 109 InstanceKlass* ik = _tmp_reflect_klasses->at(i); 110 if (SystemDictionaryShared::is_excluded_class(ik)) { 111 _tmp_reflect_klasses->remove_at(i); 112 _tmp_reflect_flags->remove_at(i); 113 } else { 114 _tmp_reflect_klasses->at_put(i, ArchiveBuilder::current()->get_buffered_addr(ik)); 115 } 116 } 117 if (_tmp_reflect_klasses->length() > 0) { 118 _reflect_klasses = ArchiveUtils::archive_array(_tmp_reflect_klasses); 119 _reflect_flags = ArchiveUtils::archive_array(_tmp_reflect_flags); 120 121 ArchivePtrMarker::mark_pointer(&_reflect_klasses); 122 ArchivePtrMarker::mark_pointer(&_reflect_flags); 123 reflect_count = _tmp_reflect_klasses->length(); 124 } 125 } 126 log_info(cds)("ReflectionData of %d classes will be archived in final CDS image", reflect_count); 127 128 // Dynamic Proxies 129 if (_tmp_dynamic_proxy_classes != nullptr) { 130 // Remove proxies for excluded classes 131 for (int i = _tmp_dynamic_proxy_classes->length() - 1; i >= 0; i--) { 132 TmpDynamicProxyClassInfo* tmp_info = _tmp_dynamic_proxy_classes->adr_at(i); 133 bool exclude = false; 134 for (int j = 0; j < tmp_info->_interfaces->length(); j++) { 135 if (SystemDictionaryShared::is_excluded_class(InstanceKlass::cast(tmp_info->_interfaces->at(j)))) { 136 exclude = true; 137 break; 138 } 139 } 140 if (exclude) { 141 _tmp_dynamic_proxy_classes->remove_at(i); 142 } 143 } 144 int len = _tmp_dynamic_proxy_classes->length(); 145 _dynamic_proxy_classes = ArchiveBuilder::new_ro_array<DynamicProxyClassInfo>(len); 146 ArchivePtrMarker::mark_pointer(&_dynamic_proxy_classes); 147 for (int i = 0; i < len; i++) { 148 TmpDynamicProxyClassInfo* tmp_info = _tmp_dynamic_proxy_classes->adr_at(i); 149 DynamicProxyClassInfo* info = _dynamic_proxy_classes->adr_at(i); 150 info->_loader_type = tmp_info->_loader_type; 151 info->_access_flags = tmp_info->_access_flags; 152 info->_proxy_name = ArchiveBuilder::current()->ro_strdup(tmp_info->_proxy_name); 153 154 ResourceMark rm; 155 GrowableArray<Klass*> buffered_interfaces; 156 for (int j = 0; j < tmp_info->_interfaces->length(); j++) { 157 InstanceKlass* intf = InstanceKlass::cast(tmp_info->_interfaces->at(j)); 158 assert(!SystemDictionaryShared::is_excluded_class(intf), "sanity"); 159 buffered_interfaces.append(ArchiveBuilder::current()->get_buffered_addr(intf)); 160 } 161 info->_interfaces = ArchiveUtils::archive_array(&buffered_interfaces); 162 163 ArchivePtrMarker::mark_pointer(&info->_proxy_name); 164 ArchivePtrMarker::mark_pointer(&info->_interfaces); 165 ArchiveBuilder::alloc_stats()->record_dynamic_proxy_class(); 166 } 167 } 168 } 169 170 void FinalImageRecipes::load_all_classes(TRAPS) { 171 assert(CDSConfig::is_dumping_final_static_archive(), "sanity"); 172 Handle class_loader(THREAD, SystemDictionary::java_system_loader()); 173 for (int i = 0; i < _all_klasses->length(); i++) { 174 Klass* k = _all_klasses->at(i); 175 if (k->is_instance_klass()) { 176 InstanceKlass* ik = InstanceKlass::cast(k); 177 if (ik->is_shared_unregistered_class()) { 178 SystemDictionaryShared::init_dumptime_info(ik); 179 SystemDictionaryShared::add_unregistered_class(THREAD, ik); 180 } else if (!ik->is_hidden()) { 181 Klass* actual = SystemDictionary::resolve_or_fail(ik->name(), class_loader, true, CHECK); 182 if (actual != ik) { 183 ResourceMark rm(THREAD); 184 log_error(cds)("Unable to resolve class from CDS archive: %s", ik->external_name()); 185 log_error(cds)("Expected: " INTPTR_FORMAT ", actual: " INTPTR_FORMAT, p2i(ik), p2i(actual)); 186 log_error(cds)("Please check if your VM command-line is the same as in the training run"); 187 MetaspaceShared::unrecoverable_writing_error(); 188 } 189 assert(ik->is_loaded(), "must be"); 190 ik->link_class(CHECK); 191 } 192 } 193 } 194 } 195 196 void FinalImageRecipes::apply_recipes_for_invokedynamic(TRAPS) { 197 assert(CDSConfig::is_dumping_final_static_archive(), "must be"); 198 199 if (CDSConfig::is_dumping_invokedynamic() && _indy_klasses != nullptr) { 200 assert(_indy_cp_indices != nullptr, "must be"); 201 for (int i = 0; i < _indy_klasses->length(); i++) { 202 InstanceKlass* ik = _indy_klasses->at(i); 203 ConstantPool* cp = ik->constants(); 204 Array<int>* cp_indices = _indy_cp_indices->at(i); 205 GrowableArray<bool> preresolve_list(cp->length(), cp->length(), false); 206 for (int j = 0; j < cp_indices->length(); j++) { 207 preresolve_list.at_put(cp_indices->at(j), true); 208 } 209 AOTConstantPoolResolver::preresolve_indy_cp_entries(THREAD, ik, &preresolve_list); 210 } 211 } 212 } 213 214 void FinalImageRecipes::apply_recipes_for_reflection_data(JavaThread* current) { 215 assert(CDSConfig::is_dumping_final_static_archive(), "must be"); 216 217 if (CDSConfig::is_dumping_reflection_data() && _reflect_klasses != nullptr) { 218 assert(_reflect_flags != nullptr, "must be"); 219 for (int i = 0; i < _reflect_klasses->length(); i++) { 220 InstanceKlass* ik = _reflect_klasses->at(i); 221 int rd_flags = _reflect_flags->at(i); 222 AOTConstantPoolResolver::generate_reflection_data(current, ik, rd_flags); 223 } 224 } 225 } 226 227 void FinalImageRecipes::add_reflection_data_flags(InstanceKlass* ik, TRAPS) { 228 assert(CDSConfig::is_dumping_preimage_static_archive(), "must be"); 229 if (SystemDictionaryShared::is_builtin_loader(ik->class_loader_data()) && !ik->is_hidden() && 230 java_lang_Class::has_reflection_data(ik->java_mirror())) { 231 int rd_flags = AOTConstantPoolResolver::class_reflection_data_flags(ik, CHECK); 232 if (_tmp_reflect_klasses == nullptr) { 233 _tmp_reflect_klasses = new (mtClassShared) GrowableArray<InstanceKlass*>(100, mtClassShared); 234 _tmp_reflect_flags = new (mtClassShared) GrowableArray<int>(100, mtClassShared); 235 } 236 _tmp_reflect_klasses->append(ik); 237 _tmp_reflect_flags->append(rd_flags); 238 } 239 } 240 241 void FinalImageRecipes::add_dynamic_proxy_class(oop loader, const char* proxy_name, objArrayOop interfaces, int access_flags) { 242 int loader_type; 243 if (loader == nullptr) { 244 loader_type = ClassLoader::BOOT_LOADER; 245 } else if (loader == SystemDictionary::java_platform_loader()) { 246 loader_type = ClassLoader::PLATFORM_LOADER; 247 } else if (loader == SystemDictionary::java_system_loader()) { 248 loader_type = ClassLoader::APP_LOADER; 249 } else { 250 return; 251 } 252 253 if (_tmp_dynamic_proxy_classes == nullptr) { 254 _tmp_dynamic_proxy_classes = new (mtClassShared) GrowableArray<TmpDynamicProxyClassInfo>(32, mtClassShared); 255 } 256 257 log_info(cds, dynamic, proxy)("Adding proxy name %s", proxy_name); 258 259 TmpDynamicProxyClassInfo info; 260 info._loader_type = loader_type; 261 info._access_flags = access_flags; 262 info._proxy_name = os::strdup(proxy_name); 263 info._interfaces = new (mtClassShared) GrowableArray<Klass*>(interfaces->length(), mtClassShared); 264 for (int i = 0; i < interfaces->length(); i++) { 265 Klass* intf = java_lang_Class::as_Klass(interfaces->obj_at(i)); 266 info._interfaces->append(InstanceKlass::cast(intf)); 267 268 if (log_is_enabled(Info, cds, dynamic, proxy)) { 269 ResourceMark rm; 270 log_info(cds, dynamic, proxy)("interface[%d] = %s", i, intf->external_name()); 271 } 272 } 273 _tmp_dynamic_proxy_classes->append(info); 274 } 275 276 void FinalImageRecipes::apply_recipes_for_dynamic_proxies(TRAPS) { 277 if (CDSConfig::is_dumping_dynamic_proxies() && _dynamic_proxy_classes != nullptr) { 278 for (int proxy_index = 0; proxy_index < _dynamic_proxy_classes->length(); proxy_index++) { 279 DynamicProxyClassInfo* info = _dynamic_proxy_classes->adr_at(proxy_index); 280 281 Handle loader(THREAD, ArchiveUtils::builtin_loader_from_type(info->_loader_type)); 282 283 oop proxy_name_oop = java_lang_String::create_oop_from_str(info->_proxy_name, CHECK); 284 Handle proxy_name(THREAD, proxy_name_oop); 285 286 int num_intfs = info->_interfaces->length(); 287 objArrayOop interfaces_oop = oopFactory::new_objArray(vmClasses::Class_klass(), num_intfs, CHECK); 288 objArrayHandle interfaces(THREAD, interfaces_oop); 289 for (int intf_index = 0; intf_index < num_intfs; intf_index++) { 290 Klass* k = info->_interfaces->at(intf_index); 291 assert(k->java_mirror() != nullptr, "must be loaded"); 292 interfaces()->obj_at_put(intf_index, k->java_mirror()); 293 } 294 295 AOTConstantPoolResolver::define_dynamic_proxy_class(loader, proxy_name, interfaces, info->_access_flags, CHECK); 296 } 297 } 298 } 299 300 void FinalImageRecipes::record_recipes() { 301 _final_image_recipes = new FinalImageRecipes(); 302 _final_image_recipes->record_recipes_impl(); 303 } 304 305 void FinalImageRecipes::apply_recipes(TRAPS) { 306 assert(CDSConfig::is_dumping_final_static_archive(), "must be"); 307 if (_final_image_recipes != nullptr) { 308 _final_image_recipes->apply_recipes_impl(THREAD); 309 if (HAS_PENDING_EXCEPTION) { 310 log_error(cds)("%s: %s", PENDING_EXCEPTION->klass()->external_name(), 311 java_lang_String::as_utf8_string(java_lang_Throwable::message(PENDING_EXCEPTION))); 312 log_error(cds)("Please check if your VM command-line is the same as in the training run"); 313 MetaspaceShared::unrecoverable_writing_error("Unexpected exception, use -Xlog:cds,exceptions=trace for detail"); 314 } 315 } 316 317 // Set it to null as we don't need to write this table into the final image. 318 _final_image_recipes = nullptr; 319 } 320 321 void FinalImageRecipes::apply_recipes_impl(TRAPS) { 322 load_all_classes(CHECK); 323 apply_recipes_for_invokedynamic(CHECK); 324 apply_recipes_for_reflection_data(CHECK); 325 apply_recipes_for_dynamic_proxies(CHECK); 326 } 327 328 void FinalImageRecipes::serialize(SerializeClosure* soc) { 329 soc->do_ptr((void**)&_final_image_recipes); 330 }