| /* | |
| * 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 |