| /* |
| * 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. |
| * |
| */ |
| |
| #include "cds/archiveHeapLoader.hpp" |
| #include "cds/cdsEnumKlass.hpp" |
| #include "cds/heapShared.hpp" |
| #include "classfile/vmClasses.hpp" |
| #include "classfile/systemDictionaryShared.hpp" |
| #include "memory/resourceArea.hpp" |
| #include "oops/fieldStreams.inline.hpp" |
| #include "oops/oop.inline.hpp" |
| #include "runtime/fieldDescriptor.inline.hpp" |
| |
| #if INCLUDE_CDS_JAVA_HEAP |
| |
| bool CDSEnumKlass::is_enum_obj(oop orig_obj) { |
| Klass* k = orig_obj->klass(); |
| return k->is_instance_klass() && |
| InstanceKlass::cast(k)->is_enum_subclass(); |
| } |
| |
| // -- Handling of Enum objects |
| // Java Enum classes have synthetic <clinit> methods that look like this |
| // enum MyEnum {FOO, BAR} |
| // MyEnum::<clinint> { |
| // /*static final MyEnum*/ MyEnum::FOO = new MyEnum("FOO"); |
| // /*static final MyEnum*/ MyEnum::BAR = new MyEnum("BAR"); |
| // } |
| // |
| // If MyEnum::FOO object is referenced by any of the archived subgraphs, we must |
| // ensure the archived value equals (in object address) to the runtime value of |
| // MyEnum::FOO. |
| // |
| // However, since MyEnum::<clinint> is synthetically generated by javac, there's |
| // no way of programmatically handling this inside the Java code (as you would handle |
| // ModuleLayer::EMPTY_LAYER, for example). |
| // |
| // Instead, we archive all static field of such Enum classes. At runtime, |
| // HeapShared::initialize_enum_klass() skips the <clinit> method and instead pulls |
| // the static fields out of the archived heap. |
| void CDSEnumKlass::handle_enum_obj(int level, |
| KlassSubGraphInfo* subgraph_info, |
| oop orig_obj) { |
| assert(level > 1, "must never be called at the first (outermost) level"); |
| assert(is_enum_obj(orig_obj), "must be"); |
| |
| InstanceKlass* ik = InstanceKlass::cast(orig_obj->klass()); |
| if (ik->has_archived_enum_objs()) { |
| return; |
| } |
| |
| ik->set_has_archived_enum_objs(); |
| |
| oop mirror = ik->java_mirror(); |
| for (JavaFieldStream fs(ik); !fs.done(); fs.next()) { |
| if (fs.access_flags().is_static()) { |
| archive_static_field(level, subgraph_info, ik, mirror, fs); |
| } |
| } |
| } |
| |
| void CDSEnumKlass::archive_static_field(int level, KlassSubGraphInfo* subgraph_info, |
| InstanceKlass* ik, oop mirror, JavaFieldStream& fs) { |
| ResourceMark rm; |
| fieldDescriptor& fd = fs.field_descriptor(); |
| if (fd.field_type() != T_OBJECT && fd.field_type() != T_ARRAY) { |
| guarantee(false, "static field %s::%s must be T_OBJECT or T_ARRAY", |
| ik->external_name(), fd.name()->as_C_string()); |
| } |
| oop oop_field = mirror->obj_field(fd.offset()); |
| if (oop_field == nullptr) { |
| guarantee(false, "static field %s::%s must not be null", |
| ik->external_name(), fd.name()->as_C_string()); |
| } else if (oop_field->klass() != ik && oop_field->klass() != ik->array_klass_or_null()) { |
| guarantee(false, "static field %s::%s is of the wrong type", |
| ik->external_name(), fd.name()->as_C_string()); |
| } |
| bool success = HeapShared::archive_reachable_objects_from(level, subgraph_info, oop_field); |
| assert(success, "VM should have exited with unarchivable objects for _level > 1"); |
| int root_index = HeapShared::append_root(oop_field); |
| log_info(aot, heap)("Archived enum obj @%d %s::%s (" INTPTR_FORMAT ")", |
| root_index, ik->external_name(), fd.name()->as_C_string(), |
| p2i((oopDesc*)oop_field)); |
| SystemDictionaryShared::add_enum_klass_static_field(ik, root_index); |
| } |
| |
| bool CDSEnumKlass::initialize_enum_klass(InstanceKlass* k, TRAPS) { |
| if (!ArchiveHeapLoader::is_in_use()) { |
| return false; |
| } |
| |
| RunTimeClassInfo* info = RunTimeClassInfo::get_for(k); |
| assert(info != nullptr, "sanity"); |
| |
| if (log_is_enabled(Info, aot, heap)) { |
| ResourceMark rm; |
| log_info(aot, heap)("Initializing Enum class: %s", k->external_name()); |
| } |
| |
| oop mirror = k->java_mirror(); |
| int i = 0; |
| for (JavaFieldStream fs(k); !fs.done(); fs.next()) { |
| if (fs.access_flags().is_static()) { |
| int root_index = info->enum_klass_static_field_root_index_at(i++); |
| fieldDescriptor& fd = fs.field_descriptor(); |
| assert(fd.field_type() == T_OBJECT || fd.field_type() == T_ARRAY, "must be"); |
| mirror->obj_field_put(fd.offset(), HeapShared::get_root(root_index, /*clear=*/true)); |
| } |
| } |
| return true; |
| } |
| #endif // INCLUDE_CDS_JAVA_HEAP |