1 /* 2 * Copyright (c) 2023, 2024, 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 "precompiled.hpp" 26 #include "cds/archiveHeapLoader.hpp" 27 #include "cds/cdsEnumKlass.hpp" 28 #include "cds/heapShared.hpp" 29 #include "classfile/vmClasses.hpp" 30 #include "classfile/systemDictionaryShared.hpp" 31 #include "memory/resourceArea.hpp" 32 #include "oops/fieldStreams.inline.hpp" 33 #include "oops/oop.inline.hpp" 34 #include "runtime/fieldDescriptor.inline.hpp" 35 36 #if INCLUDE_CDS_JAVA_HEAP 37 38 bool CDSEnumKlass::is_enum_obj(oop orig_obj) { 39 Klass* k = orig_obj->klass(); 40 Klass* buffered_k = ArchiveBuilder::get_buffered_klass(k); 41 return k->is_instance_klass() && 42 InstanceKlass::cast(k)->java_super() == vmClasses::Enum_klass(); 43 } 44 45 // -- Archiving of Enum objects 46 // 47 // Parts of the <clinit> method of a Java enum is synthetically generated by javac. For example, 48 // 49 // enum MyEnum { 50 // FOO, BAR; 51 // static final int BAZ = baz(); 52 // } 53 // 54 // Its <clinit> looks like this: 55 // MyEnum::<clinint> { 56 // static final MyEnum FOO = new MyEnum("FOO"); // generated by javac 57 // static final MyEnum BAR = new MyEnum("BAR"); // generated by javac 58 // static final MyEnum[] $VALUES = $values(); // generated by javac 59 // static final int BAZ = baz(); // from source code. 60 // } 61 // 62 // If MyEnum::FOO object is referenced by any of the archived subgraphs, we must 63 // ensure the archived value equals (in object address) to the runtime value of 64 // MyEnum::FOO. 65 // 66 // However, since MyEnum::<clinint> is synthetically generated by javac, there's 67 // no way to programmatically archive MuEnum::FOO inside the Java code (as you would handle 68 // ModuleLayer::EMPTY_LAYER, for example). 69 // 70 // Instead, we archive all static field of such Enum classes. At runtime, 71 // HeapShared::initialize_enum_klass() skips the <clinit> method and instead pulls 72 // the static fields out of the archived heap. 73 void CDSEnumKlass::archive_enum_obj(int level, 74 KlassSubGraphInfo* subgraph_info, 75 oop orig_obj) { 76 assert(level > 1, "must never be called at the first (outermost) level"); 77 assert(is_enum_obj(orig_obj), "must be"); 78 79 InstanceKlass* ik = InstanceKlass::cast(orig_obj->klass()); 80 if (ik->has_archived_enum_objs()) { 81 return; 82 } 83 84 ik->set_has_archived_enum_objs(); 85 ArchiveBuilder::get_buffered_klass(ik)->set_has_archived_enum_objs(); 86 87 oop mirror = ik->java_mirror(); 88 for (JavaFieldStream fs(ik); !fs.done(); fs.next()) { 89 if (fs.access_flags().is_static()) { 90 fieldDescriptor& fd = fs.field_descriptor(); 91 if (fd.field_type() == T_OBJECT || fd.field_type() == T_ARRAY) { 92 archive_static_oop_field(level, subgraph_info, ik, mirror, fd); 93 } else { 94 archive_static_primitive_field(level, subgraph_info, ik, mirror, fd); 95 } 96 } 97 } 98 } 99 100 bool CDSEnumKlass::can_archive_static_oop_field(InstanceKlass* enum_klass, oop obj) { 101 Klass* klass = obj->klass(); 102 if ( klass == enum_klass || klass == enum_klass->array_klass_or_null()) { 103 // These two types of fields are generated by javac for enum classes 104 return true; 105 } 106 107 // Special handling for the sun.invoke.util.Wrapper enum. We need this in order 108 // to archive instances of java.lang.invoke.MethodTypes. 109 if (klass == vmClasses::Boolean_klass() || 110 klass == vmClasses::Byte_klass() || 111 klass == vmClasses::Character_klass() || 112 klass == vmClasses::Short_klass() || 113 klass == vmClasses::Integer_klass() || 114 klass == vmClasses::Long_klass() || 115 klass == vmClasses::Float_klass() || 116 klass == vmClasses::Double_klass() || 117 klass == vmClasses::Void_klass()) { 118 return true; 119 } 120 121 return false; 122 } 123 124 void CDSEnumKlass::archive_static_oop_field(int level, KlassSubGraphInfo* subgraph_info, 125 InstanceKlass* ik, oop mirror, fieldDescriptor& fd) { 126 ResourceMark rm; 127 oop oop_field = mirror->obj_field(fd.offset()); 128 if (oop_field == nullptr) { 129 return; 130 } 131 132 if (!can_archive_static_oop_field(ik, oop_field)) { 133 guarantee(false, "static field %s::%s is of the wrong type", 134 ik->external_name(), fd.name()->as_C_string()); 135 } 136 bool success = HeapShared::archive_reachable_objects_from(level, subgraph_info, oop_field); 137 assert(success, "VM should have exited with unarchivable objects for _level > 1"); 138 int root_index = HeapShared::append_root(oop_field); 139 log_info(cds, heap)("Archived enum obj @%d %s::%s (" INTPTR_FORMAT ")", 140 root_index, ik->external_name(), fd.name()->as_C_string(), 141 p2i((oopDesc*)oop_field)); 142 SystemDictionaryShared::add_enum_klass_static_field(ik, root_index); 143 } 144 145 146 void CDSEnumKlass::archive_static_primitive_field(int level, KlassSubGraphInfo* subgraph_info, 147 InstanceKlass* ik, oop mirror, fieldDescriptor& fd) { 148 int v; 149 switch (fd.field_type()) { 150 case T_BOOLEAN: 151 v = mirror->bool_field(fd.offset()); 152 SystemDictionaryShared::add_enum_klass_static_field(ik, v); 153 break; 154 case T_BYTE: 155 v = mirror->byte_field(fd.offset()); 156 SystemDictionaryShared::add_enum_klass_static_field(ik, v); 157 break; 158 case T_SHORT: 159 v = mirror->short_field(fd.offset()); 160 SystemDictionaryShared::add_enum_klass_static_field(ik, v); 161 break; 162 case T_CHAR: 163 v = mirror->char_field(fd.offset()); 164 SystemDictionaryShared::add_enum_klass_static_field(ik, v); 165 break; 166 case T_INT: 167 v = mirror->int_field(fd.offset()); 168 SystemDictionaryShared::add_enum_klass_static_field(ik, v); 169 break; 170 case T_LONG: 171 { 172 jlong value = mirror->long_field(fd.offset()); 173 v = value & 0xffffffff; 174 SystemDictionaryShared::add_enum_klass_static_field(ik, v); 175 v = value > 32; 176 SystemDictionaryShared::add_enum_klass_static_field(ik, v); 177 } 178 break; 179 case T_FLOAT: 180 { 181 jfloat value = mirror->float_field(fd.offset()); 182 int* p = (int*)(&value); 183 SystemDictionaryShared::add_enum_klass_static_field(ik, *p); 184 } 185 break; 186 case T_DOUBLE: 187 { 188 jdouble value = mirror->double_field(fd.offset()); 189 int* p = (int*)(&value); 190 SystemDictionaryShared::add_enum_klass_static_field(ik, p[0]); 191 SystemDictionaryShared::add_enum_klass_static_field(ik, p[1]); 192 } 193 break; 194 default: 195 ShouldNotReachHere(); 196 } 197 } 198 199 200 bool CDSEnumKlass::initialize_enum_klass(InstanceKlass* k, TRAPS) { 201 if (!ArchiveHeapLoader::is_in_use()) { 202 return false; 203 } 204 205 RunTimeClassInfo* info = RunTimeClassInfo::get_for(k); 206 assert(info != nullptr, "sanity"); 207 208 if (log_is_enabled(Info, cds, heap)) { 209 ResourceMark rm; 210 log_info(cds, heap)("Initializing Enum class: %s", k->external_name()); 211 } 212 213 oop mirror = k->java_mirror(); 214 int i = 0; 215 for (JavaFieldStream fs(k); !fs.done(); fs.next()) { 216 if (fs.access_flags().is_static()) { 217 int v = info->enum_klass_static_field_root_index_at(i++); 218 fieldDescriptor& fd = fs.field_descriptor(); 219 switch (fd.field_type()) { 220 case T_OBJECT: 221 case T_ARRAY: 222 mirror->obj_field_put(fd.offset(), HeapShared::get_root(v, /*clear=*/true)); 223 break; 224 case T_BOOLEAN: 225 mirror->bool_field_put(fd.offset(), v ? true : false); 226 break; 227 case T_BYTE: 228 mirror->byte_field_put(fd.offset(), (jbyte)v); 229 break; 230 case T_SHORT: 231 mirror->short_field_put(fd.offset(), (jshort)v); 232 break; 233 case T_CHAR: 234 mirror->char_field_put(fd.offset(), (jchar)v); 235 break; 236 case T_INT: 237 mirror->int_field_put(fd.offset(), v); 238 break; 239 case T_LONG: 240 { 241 int v2 = info->enum_klass_static_field_root_index_at(i++); 242 jlong lv = v2; 243 lv <<= 32; 244 lv += v; 245 mirror->long_field_put(fd.offset(), lv); 246 } 247 break; 248 case T_FLOAT: 249 { 250 jfloat* p = (float*)&v; 251 mirror->float_field_put(fd.offset(), *p); 252 } 253 break; 254 case T_DOUBLE: 255 { 256 int v2 = info->enum_klass_static_field_root_index_at(i++); 257 jdouble d = 0; 258 int* p = (int*)(&d); 259 p[0] = v; 260 p[1] = v2; 261 mirror->double_field_put(fd.offset(), d); 262 } 263 break; 264 default: 265 ShouldNotReachHere(); 266 } 267 } 268 } 269 return true; 270 } 271 #endif // INCLUDE_CDS_JAVA_HEAP