1 /*
2 * Copyright (c) 2023, 2026, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.internal.value;
27
28 import jdk.internal.access.JavaLangReflectAccess;
29 import jdk.internal.access.SharedSecrets;
30 import jdk.internal.misc.PreviewFeatures;
31 import jdk.internal.misc.Unsafe;
32 import jdk.internal.vm.annotation.ForceInline;
33 import jdk.internal.vm.annotation.IntrinsicCandidate;
34
35 import java.lang.reflect.Field;
36 import java.lang.reflect.Modifier;
37 import java.util.Objects;
38
39 import static java.lang.classfile.ClassFile.ACC_STATIC;
40 import static java.lang.classfile.ClassFile.ACC_STRICT_INIT;
41
42 /**
43 * Utilities to access package private methods of java.lang.Class and related reflection classes.
44 */
45 public final class ValueClass {
46 private static final JavaLangReflectAccess JLRA = SharedSecrets.getJavaLangReflectAccess();
47
48 /// {@return whether this field type may store value objects}
49 /// This excludes primitives and includes Object.
50 public static boolean isValueObjectCompatible(Class<?> fieldType) {
51 return PreviewFeatures.isEnabled() &&
52 (fieldType.isValue() ||
53 fieldType.isInterface() ||
54 fieldType == Object.class);
55 }
56
57 /// {@return whether an object of this exact class is a value object}
58 /// This excludes abstract value classes.
59 public static boolean isConcreteValueClass(Class<?> clazz) {
60 return clazz.isValue() && !Modifier.isAbstract(clazz.getModifiers());
61 }
62
63 /// {@return whether a field of type `c` can be represented with a payload
64 /// without oops} For example, primitive type fields and value classes with
65 /// all primitive fields recursively may be represented by a payload of a
66 /// layout without oops. Returns false if there is no flat layout for a
67 /// field of type `c`.
68 public static boolean hasBinaryPayload(Class<?> c) {
69 // non-concrete value class type field always a reference
70 if (!ValueClass.isConcreteValueClass(c))
71 return c.isPrimitive();
72 // Check the flat layout
73 return Unsafe.getUnsafe().isFlatPayloadBinary(c);
74 }
75
76 /**
77 * {@return {@code true} if the field is NullRestricted}
78 */
79 public static boolean isNullRestrictedField(Field f) {
80 return JLRA.isNullRestrictedField(f);
81 }
82
83 @ForceInline
84 private static void validateArrayArguments(Class<?> componentType,
85 int length) {
86 if (componentType == null) {
87 throw new NullPointerException("Component type is null");
88 }
89 if (!isConcreteValueClass(componentType)) {
90 throw new IllegalArgumentException("Component type is not a concrete value class");
91 }
92 if (length < 0) {
93 throw new NegativeArraySizeException(Integer.toString(length));
94 }
95 }
96
97 @ForceInline
98 private static void validateArrayArguments(Class<?> componentType,
99 int length, Object initVal) {
100 validateArrayArguments(componentType, length);
101 if (initVal == null) {
102 throw new NullPointerException("Initial value is null");
103 }
104 // Special arrays are monomorphic, Java mirror can be compared directly
105 if (initVal.getClass() != componentType) {
106 throw new IllegalArgumentException("Type mismatch between array and initial value");
107 }
108 }
109
110 /**
111 * Allocate an array of a value class type with components that behave in
112 * the same way as a {@link jdk.internal.vm.annotation.NullRestricted}
113 * field.
114 * <p>
115 * Because these behaviors are not specified by Java SE, arrays created with
116 * this method should only be used by internal JDK code for experimental
117 * purposes and should not affect user-observable outcomes.
118 *
119 * @throws IllegalArgumentException if {@code componentType} is not a
120 * value class type.
121 */
122 @ForceInline
123 public static Object[] newNullRestrictedAtomicArray(Class<?> componentType,
124 int length, Object initVal) {
125 validateArrayArguments(componentType, length, initVal);
126 return newNullRestrictedAtomicArray0(componentType, length, initVal);
127 }
128
129 @IntrinsicCandidate
130 private static native Object[] newNullRestrictedAtomicArray0(Class<?> componentType,
131 int length, Object initVal);
132
133 @ForceInline
134 public static Object[] newNullRestrictedNonAtomicArray(Class<?> componentType,
135 int length, Object initVal) {
136 validateArrayArguments(componentType, length, initVal);
137 return newNullRestrictedNonAtomicArray0(componentType, length, initVal);
138 }
139
140 @IntrinsicCandidate
141 private static native Object[] newNullRestrictedNonAtomicArray0(Class<?> componentType,
142 int length, Object initVal);
143
144 @ForceInline
145 public static Object[] newNullableAtomicArray(Class<?> componentType,
146 int length) {
147 validateArrayArguments(componentType, length);
148 return newNullableAtomicArray0(componentType, length);
149 }
150
151 @IntrinsicCandidate
152 private static native Object[] newNullableAtomicArray0(Class<?> componentType,
153 int length);
154
155 public static Object[] newReferenceArray(Class<?> componentType,
156 int length) {
157 validateArrayArguments(componentType, length);
158 return newReferenceArray0(componentType, length);
159 }
160
161 private static native Object[] newReferenceArray0(Class<?> componentType,
162 int length);
163
164 /**
165 * {@return true if the given array is a flat array}
166 */
167 @ForceInline
168 public static boolean isFlatArray(Object[] array) {
169 return isFlatArray0(Objects.requireNonNull(array));
170 }
171
172 @IntrinsicCandidate
173 private static native boolean isFlatArray0(Object[] array);
174
175 @ForceInline
176 private static void validateSpecialArray(Object[] array) {
177 Objects.requireNonNull(array);
178 if (!isConcreteValueClass(array.getClass().getComponentType())) {
179 throw new IllegalArgumentException("Element class is not a concrete value class");
180 }
181 }
182
183 public static Object[] copyOfSpecialArray(Object[] array, int newLength) {
184 validateSpecialArray(array);
185 if (newLength < 0) {
186 throw new NegativeArraySizeException("" + newLength);
187 }
188 return copyOfSpecialArray0(array, 0, newLength);
189 }
190
191 public static Object[] copyOfRangeSpecialArray(Object[] array, int from, int to) {
192 validateSpecialArray(array);
193 int length = array.length;
194 if (from < 0 || from > length) {
195 throw new ArrayIndexOutOfBoundsException("source index " + from + " out of bounds for object array[" + length + "]");
196 }
197 if (from > to) {
198 throw new IllegalArgumentException(from + " > " + to);
199 }
200 return copyOfSpecialArray0(array, from, to);
201 }
202
203 private static native Object[] copyOfSpecialArray0(Object[] array, int from, int to);
204
205 /**
206 * {@return true if the given array is a null-restricted array}
207 */
208 @ForceInline
209 public static boolean isNullRestrictedArray(Object[] array) {
210 return isNullRestrictedArray0(Objects.requireNonNull(array));
211 }
212
213 @IntrinsicCandidate
214 private static native boolean isNullRestrictedArray0(Object[] array);
215
216 /**
217 * {@return true if the given array uses a layout designed for atomic accesses }
218 */
219 @ForceInline
220 public static boolean isAtomicArray(Object[] array) {
221 return isAtomicArray0(Objects.requireNonNull(array));
222 }
223
224 @IntrinsicCandidate
225 private static native boolean isAtomicArray0(Object[] array);
226
227 // This class also serves as a lazy holder of its singleton instance
228 private static final class StrictInstanceFieldClassValue extends ClassValue<Boolean> {
229 private static final StrictInstanceFieldClassValue INSTANCE = new StrictInstanceFieldClassValue();
230
231 private StrictInstanceFieldClassValue() {}
232
233 @Override
234 protected Boolean computeValue(Class<?> type) {
235 if (!isClassOrInterface(type)) {
236 return false;
237 }
238 for (var field : type.getDeclaredFields()) {
239 // Reflection filters fields, hope the filtered classes don't declare strict fields
240 if ((field.getModifiers() & (ACC_STATIC | ACC_STRICT_INIT)) == ACC_STRICT_INIT) {
241 return true;
242 }
243 }
244 return false;
245 }
246 }
247
248 /// Returns whether a class or interface declares strict instance fields.
249 /// This does not include inherited instance fields.
250 public static boolean hasStrictInstanceField(Class<?> cl) {
251 if (!isClassOrInterface(cl)) {
252 // Not class or interface
253 return false;
254 }
255 return StrictInstanceFieldClassValue.INSTANCE.get(cl);
256 }
257
258 // Only primitive and array types have both ABSTRACT and FINAL flags set
259 private static final int NON_CLASS_COMMON_MODIFIERS = Modifier.ABSTRACT | Modifier.FINAL;
260
261 /// Returns if a Class object represents a class or interface instead of a
262 /// primitive type or an array type.
263 private static boolean isClassOrInterface(Class<?> cl) {
264 return (cl.getModifiers() & NON_CLASS_COMMON_MODIFIERS) != NON_CLASS_COMMON_MODIFIERS;
265 }
266 }