1 /*
2 * Copyright (c) 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 "classfile/moduleEntry.hpp"
26 #include "classfile/packageEntry.hpp"
27 #include "classfile/symbolTable.hpp"
28 #include "classfile/vmClasses.hpp"
29 #include "classfile/vmSymbols.hpp"
30 #include "gc/shared/collectedHeap.inline.hpp"
31 #include "memory/iterator.inline.hpp"
32 #include "memory/metadataFactory.hpp"
33 #include "memory/metaspaceClosure.hpp"
34 #include "memory/oopFactory.hpp"
35 #include "memory/resourceArea.hpp"
36 #include "memory/universe.hpp"
37 #include "oops/arrayKlass.hpp"
38 #include "oops/instanceKlass.hpp"
39 #include "oops/klass.inline.hpp"
40 #include "oops/markWord.hpp"
41 #include "oops/oop.inline.hpp"
42 #include "oops/refArrayKlass.inline.hpp"
43 #include "oops/refArrayOop.inline.hpp"
44 #include "oops/symbol.hpp"
45 #include "runtime/handles.inline.hpp"
46 #include "runtime/mutexLocker.hpp"
47 #include "utilities/macros.hpp"
48
49 RefArrayKlass *RefArrayKlass::allocate_klass(ClassLoaderData* loader_data, int n,
50 Klass* k, Symbol *name, ArrayKlass::ArrayProperties props,
51 TRAPS) {
52 assert(RefArrayKlass::header_size() <= InstanceKlass::header_size(),
53 "array klasses must be same size as InstanceKlass");
54
55 int size = ArrayKlass::static_size(RefArrayKlass::header_size());
56
57 return new (loader_data, size, THREAD) RefArrayKlass(n, k, name, props);
58 }
59
60 RefArrayKlass* RefArrayKlass::allocate_refArray_klass(ClassLoaderData* loader_data, int n,
61 Klass* element_klass, ArrayKlass::ArrayProperties props,
62 TRAPS) {
63 assert(!ArrayKlass::is_null_restricted(props) || (n == 1 && element_klass->is_inline_klass()),
64 "null-free unsupported");
65
66 // Eagerly allocate the direct array supertype.
67 Klass* super_klass = nullptr;
68 if (!Universe::is_bootstrapping() || vmClasses::Object_klass_is_loaded()) {
69 assert(MultiArray_lock->holds_lock(THREAD),
70 "must hold lock after bootstrapping");
71 Klass* element_super = element_klass->super();
72 super_klass = element_klass->array_klass(CHECK_NULL);
73 }
74
75 // Create type name for klass.
76 Symbol* name = ArrayKlass::create_element_klass_array_name(element_klass, CHECK_NULL);
77
78 // Initialize instance variables
79 RefArrayKlass* oak = RefArrayKlass::allocate_klass(loader_data, n, element_klass,
80 name, props, CHECK_NULL);
81
82 ModuleEntry* module = oak->module();
83 assert(module != nullptr, "No module entry for array");
84
85 // Call complete_create_array_klass after all instance variables has been
86 // initialized.
87 ArrayKlass::complete_create_array_klass(oak, super_klass, module, CHECK_NULL);
88
89 // Add all classes to our internal class loader list here,
90 // including classes in the bootstrap (null) class loader.
91 // Do this step after creating the mirror so that if the
92 // mirror creation fails, loaded_classes_do() doesn't find
93 // an array class without a mirror.
94 loader_data->add_class(oak);
95
96 return oak;
97 }
98
99 RefArrayKlass::RefArrayKlass(int n, Klass* element_klass, Symbol* name,
100 ArrayKlass::ArrayProperties props)
101 : ObjArrayKlass(n, element_klass, name, Kind, props,
102 ArrayKlass::is_null_restricted(props) ? markWord::null_free_array_prototype() : markWord::prototype()) {
103 set_dimension(n);
104 set_element_klass(element_klass);
105
106 Klass* bk;
107 if (element_klass->is_objArray_klass()) {
108 bk = ObjArrayKlass::cast(element_klass)->bottom_klass();
109 } else {
110 bk = element_klass;
111 }
112 assert(bk != nullptr && (bk->is_instance_klass() || bk->is_typeArray_klass()),
113 "invalid bottom klass");
114 set_bottom_klass(bk);
115 set_class_loader_data(bk->class_loader_data());
116
117 if (element_klass->is_array_klass()) {
118 set_lower_dimension(ArrayKlass::cast(element_klass));
119 }
120
121 int lh = array_layout_helper(T_OBJECT);
122 if (ArrayKlass::is_null_restricted(props)) {
123 assert(n == 1, "Bytecode does not support null-free multi-dim");
124 lh = layout_helper_set_null_free(lh);
125 #ifdef _LP64
126 assert(prototype_header().is_null_free_array(), "sanity");
127 #endif
128 }
129 set_layout_helper(lh);
130 assert(is_array_klass(), "sanity");
131 assert(is_refArray_klass(), "sanity");
132 }
133
134 size_t RefArrayKlass::oop_size(oop obj) const {
135 // In this assert, we cannot safely access the Klass* with compact headers,
136 // because size_given_klass() calls oop_size() on objects that might be
137 // concurrently forwarded, which would overwrite the Klass*.
138 assert(UseCompactObjectHeaders || obj->is_refArray(), "must be a reference array");
139 return refArrayOop(obj)->object_size();
140 }
141
142 objArrayOop RefArrayKlass::allocate_instance(int length, ArrayProperties props, TRAPS) {
143 check_array_allocation_length(
144 length, arrayOopDesc::max_array_length(T_OBJECT), CHECK_NULL);
145 size_t size = refArrayOopDesc::object_size(length);
146 objArrayOop array = (objArrayOop)Universe::heap()->array_allocate(
147 this, size, length,
148 /* do_zero */ true, CHECK_NULL);
149 assert(array->is_refArray(), "Must be");
150 objArrayHandle array_h(THREAD, array);
151 return array_h();
152 }
153
154
155 // Either oop or narrowOop depending on UseCompressedOops.
156 void RefArrayKlass::do_copy(arrayOop s, size_t src_offset, arrayOop d,
157 size_t dst_offset, int length, TRAPS) {
158 if (s == d) {
159 // since source and destination are equal we do not need conversion checks.
160 assert(length > 0, "sanity check");
161 ArrayAccess<>::oop_arraycopy(s, src_offset, d, dst_offset, length);
162 } else {
163 // We have to make sure all elements conform to the destination array
164 Klass *bound = RefArrayKlass::cast(d->klass())->element_klass();
165 Klass *stype = RefArrayKlass::cast(s->klass())->element_klass();
166 // Perform null check if dst is null-free but src has no such guarantee
167 bool null_check = ((!s->klass()->is_null_free_array_klass()) &&
168 d->klass()->is_null_free_array_klass());
169 if (stype == bound || stype->is_subtype_of(bound)) {
170 if (null_check) {
171 ArrayAccess<ARRAYCOPY_DISJOINT | ARRAYCOPY_NOTNULL>::oop_arraycopy(
172 s, src_offset, d, dst_offset, length);
173 } else {
174 ArrayAccess<ARRAYCOPY_DISJOINT>::oop_arraycopy(s, src_offset, d,
175 dst_offset, length);
176 }
177 } else {
178 if (null_check) {
179 ArrayAccess<ARRAYCOPY_DISJOINT | ARRAYCOPY_CHECKCAST |
180 ARRAYCOPY_NOTNULL>::oop_arraycopy(s, src_offset, d,
181 dst_offset, length);
182 } else {
183 ArrayAccess<ARRAYCOPY_DISJOINT | ARRAYCOPY_CHECKCAST>::oop_arraycopy(
184 s, src_offset, d, dst_offset, length);
185 }
186 }
187 }
188 }
189
190 void RefArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d, int dst_pos,
191 int length, TRAPS) {
192 assert(s->is_refArray(), "must be a reference array");
193
194 if (UseArrayFlattening) {
195 if (d->is_flatArray()) {
196 FlatArrayKlass::cast(d->klass())->copy_array(s, src_pos, d, dst_pos, length, THREAD);
197 return;
198 }
199 if (s->is_flatArray()) {
200 FlatArrayKlass::cast(s->klass())->copy_array(s, src_pos, d, dst_pos, length, THREAD);
201 return;
202 }
203 }
204
205 if (!d->is_refArray()) {
206 ResourceMark rm(THREAD);
207 stringStream ss;
208 if (d->is_typeArray()) {
209 ss.print(
210 "arraycopy: type mismatch: can not copy object array[] into %s[]",
211 type2name_tab[ArrayKlass::cast(d->klass())->element_type()]);
212 } else {
213 ss.print("arraycopy: destination type %s is not an array",
214 d->klass()->external_name());
215 }
216 THROW_MSG(vmSymbols::java_lang_ArrayStoreException(), ss.as_string());
217 }
218
219 // Check is all offsets and lengths are non negative
220 if (src_pos < 0 || dst_pos < 0 || length < 0) {
221 // Pass specific exception reason.
222 ResourceMark rm(THREAD);
223 stringStream ss;
224 if (src_pos < 0) {
225 ss.print("arraycopy: source index %d out of bounds for object array[%d]",
226 src_pos, s->length());
227 } else if (dst_pos < 0) {
228 ss.print(
229 "arraycopy: destination index %d out of bounds for object array[%d]",
230 dst_pos, d->length());
231 } else {
232 ss.print("arraycopy: length %d is negative", length);
233 }
234 THROW_MSG(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(),
235 ss.as_string());
236 }
237 // Check if the ranges are valid
238 if ((((unsigned int)length + (unsigned int)src_pos) >
239 (unsigned int)s->length()) ||
240 (((unsigned int)length + (unsigned int)dst_pos) >
241 (unsigned int)d->length())) {
242 // Pass specific exception reason.
243 ResourceMark rm(THREAD);
244 stringStream ss;
245 if (((unsigned int)length + (unsigned int)src_pos) >
246 (unsigned int)s->length()) {
247 ss.print(
248 "arraycopy: last source index %u out of bounds for object array[%d]",
249 (unsigned int)length + (unsigned int)src_pos, s->length());
250 } else {
251 ss.print("arraycopy: last destination index %u out of bounds for object "
252 "array[%d]",
253 (unsigned int)length + (unsigned int)dst_pos, d->length());
254 }
255 THROW_MSG(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(),
256 ss.as_string());
257 }
258
259 // Special case. Boundary cases must be checked first
260 // This allows the following call: copy_array(s, s.length(), d.length(), 0).
261 // This is correct, since the position is supposed to be an 'in between
262 // point', i.e., s.length(), points to the right of the last element.
263 if (length == 0) {
264 return;
265 }
266 if (UseCompressedOops) {
267 size_t src_offset =
268 (size_t)refArrayOopDesc::obj_at_offset<narrowOop>(src_pos);
269 size_t dst_offset =
270 (size_t)refArrayOopDesc::obj_at_offset<narrowOop>(dst_pos);
271 assert(arrayOopDesc::obj_offset_to_raw<narrowOop>(s, src_offset, nullptr) ==
272 refArrayOop(s)->obj_at_addr<narrowOop>(src_pos),
273 "sanity");
274 assert(arrayOopDesc::obj_offset_to_raw<narrowOop>(d, dst_offset, nullptr) ==
275 refArrayOop(d)->obj_at_addr<narrowOop>(dst_pos),
276 "sanity");
277 do_copy(s, src_offset, d, dst_offset, length, CHECK);
278 } else {
279 size_t src_offset = (size_t)refArrayOopDesc::obj_at_offset<oop>(src_pos);
280 size_t dst_offset = (size_t)refArrayOopDesc::obj_at_offset<oop>(dst_pos);
281 assert(arrayOopDesc::obj_offset_to_raw<oop>(s, src_offset, nullptr) ==
282 refArrayOop(s)->obj_at_addr<oop>(src_pos),
283 "sanity");
284 assert(arrayOopDesc::obj_offset_to_raw<oop>(d, dst_offset, nullptr) ==
285 refArrayOop(d)->obj_at_addr<oop>(dst_pos),
286 "sanity");
287 do_copy(s, src_offset, d, dst_offset, length, CHECK);
288 }
289 }
290
291 void RefArrayKlass::initialize(TRAPS) {
292 bottom_klass()->initialize(THREAD); // dispatches to either InstanceKlass or TypeArrayKlass
293 }
294
295 void RefArrayKlass::metaspace_pointers_do(MetaspaceClosure *it) {
296 ObjArrayKlass::metaspace_pointers_do(it);
297 }
298
299 // Printing
300
301 void RefArrayKlass::print_on(outputStream* st) const {
302 #ifndef PRODUCT
303 Klass::print_on(st);
304 st->print(" - element klass: ");
305 element_klass()->print_value_on(st);
306 st->cr();
307 #endif // PRODUCT
308 }
309
310 void RefArrayKlass::print_value_on(outputStream* st) const {
311 assert(is_klass(), "must be klass");
312
313 element_klass()->print_value_on(st);
314 st->print("[]");
315 }
316
317 #ifndef PRODUCT
318
319 void RefArrayKlass::oop_print_on(oop obj, outputStream* st) {
320 ArrayKlass::oop_print_on(obj, st);
321 assert(obj->is_refArray(), "must be refArray");
322 refArrayOop oa = refArrayOop(obj);
323 int print_len = MIN2(oa->length(), MaxElementPrintSize);
324 for (int index = 0; index < print_len; index++) {
325 st->print(" - %3d : ", index);
326 if (oa->obj_at(index) != nullptr) {
327 oa->obj_at(index)->print_value_on(st);
328 st->cr();
329 } else {
330 st->print_cr("null");
331 }
332 }
333 int remaining = oa->length() - print_len;
334 if (remaining > 0) {
335 st->print_cr(" - <%d more elements, increase MaxElementPrintSize to print>",
336 remaining);
337 }
338 }
339
340 #endif // PRODUCT
341
342 void RefArrayKlass::oop_print_value_on(oop obj, outputStream* st) {
343 assert(obj->is_refArray(), "must be refArray");
344 st->print("a ");
345 element_klass()->print_value_on(st);
346 int len = refArrayOop(obj)->length();
347 st->print("[%d] ", len);
348 if (obj != nullptr) {
349 obj->print_address_on(st);
350 } else {
351 st->print_cr("null");
352 }
353 }
354
355 // Verification
356
357 void RefArrayKlass::verify_on(outputStream* st) {
358 ArrayKlass::verify_on(st);
359 guarantee(element_klass()->is_klass(), "should be klass");
360 guarantee(bottom_klass()->is_klass(), "should be klass");
361 Klass *bk = bottom_klass();
362 guarantee(bk->is_instance_klass() || bk->is_typeArray_klass() ||
363 bk->is_flatArray_klass(),
364 "invalid bottom klass");
365 }
366
367 void RefArrayKlass::oop_verify_on(oop obj, outputStream* st) {
368 ArrayKlass::oop_verify_on(obj, st);
369 guarantee(obj->is_refArray(), "must be refArray");
370 guarantee(obj->is_null_free_array() || (!is_null_free_array_klass()),
371 "null-free klass but not object");
372 refArrayOop oa = refArrayOop(obj);
373 for (int index = 0; index < oa->length(); index++) {
374 guarantee(oopDesc::is_oop_or_null(oa->obj_at(index)), "should be oop");
375 }
376 }