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 }