1 /*
2 * Copyright (c) 2024, 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.
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 * @test
26 * @enablePreview
27 * @run junit/othervm -XX:-UseArrayFlattening -XX:-UseNullableValueFlattening NullRestrictedArraysTest
28 * @run junit/othervm -XX:+UseArrayFlattening -XX:+UseNullableValueFlattening NullRestrictedArraysTest
29 */
30
31 import java.lang.invoke.MethodHandles;
32 import java.lang.invoke.VarHandle;
33 import java.lang.reflect.Array;
34 import java.lang.reflect.Field;
35 import java.util.Arrays;
36 import java.util.stream.Stream;
37
38 import jdk.internal.value.ValueClass;
39 import jdk.internal.vm.annotation.LooselyConsistentValue;
40 import jdk.internal.vm.annotation.NullRestricted;
41
42 import org.junit.jupiter.api.Test;
43 import org.junit.jupiter.params.ParameterizedTest;
44 import org.junit.jupiter.params.provider.Arguments;
45 import org.junit.jupiter.params.provider.MethodSource;
46
47 import static org.junit.jupiter.api.Assertions.*;
48
49 public class NullRestrictedArraysTest {
50 interface I {
51 int getValue();
52 }
53 @LooselyConsistentValue
54 static value class Value implements I {
55 int v;
56 Value() {
57 this(0);
58 }
59 Value(int v) {
60 this.v = v;
61 }
62 public int getValue() {
63 return v;
64 }
65 }
66
67 static class T {
68 String s;
69 Value obj; // can be null
70 @NullRestricted
71 Value value;
72
73 T() {
74 value = new Value();
75 super();
76 }
77 }
78
79 static Stream<Arguments> checkedField() throws ReflectiveOperationException {
80 Value v = new Value();
81 return Stream.of(
82 Arguments.of(T.class.getDeclaredField("s"), String.class, "", false),
83 Arguments.of(T.class.getDeclaredField("obj"), Value.class, null, false),
84 Arguments.of(T.class.getDeclaredField("value"), Value.class, v, true)
85 );
86 }
87
88 /*
89 * Test creating null-restricted arrays
90 */
91 @ParameterizedTest
92 @MethodSource("checkedField")
93 public void testNullRestrictedArrays(Field field, Class<?> type, Object initValue,
94 boolean nullRestricted) throws ReflectiveOperationException {
95 boolean nr = ValueClass.isNullRestrictedField(field);
96 assertEquals(nr, nullRestricted);
97 assertTrue(field.getType() == type);
98 Object[] array = nullRestricted
99 ? ValueClass.newNullRestrictedAtomicArray(type, 4, initValue)
100 : (Object[]) Array.newInstance(type, 4);
101 assertTrue(ValueClass.isNullRestrictedArray(array) == nullRestricted);
102 for (int i=0; i < array.length; i++) {
103 array[i] = type.newInstance();
104 }
105 if (nullRestricted) {
106 // NPE thrown if elements in a null-restricted array set to null
107 assertThrows(NullPointerException.class, () -> array[0] = null);
108 } else {
109 array[0] = null;
110 }
111 }
112
113 /*
114 * Test Arrays::copyOf and Arrays::copyOfRange to create null-restricted arrays.
115 */
116 @Test
117 public void testArraysCopyOf() {
118 int len = 4;
119 Object[] array = (Object[]) Array.newInstance(Value.class, len);
120 Object[] nullRestrictedArray = ValueClass.newNullRestrictedNonAtomicArray(Value.class, len, new Value());
121 for (int i=0; i < len; i++) {
122 array[i] = new Value(i);
123 nullRestrictedArray[i] = new Value(i);
124 }
125 testCopyOf(array, nullRestrictedArray);
126 // Cannot extend a null-restricted array without providing a value to fill the new elements
127 // testCopyOfRange(array, nullRestrictedArray, 1, len+2);
128 };
129
130 private void testCopyOf(Object[] array, Object[] nullRestrictedArray) {
131 Object[] newArray1 = Arrays.copyOf(array, array.length);
132 Object[] newArray2 = Arrays.copyOf(nullRestrictedArray, nullRestrictedArray.length);
133
134 assertFalse(ValueClass.isNullRestrictedArray(newArray1));
135 assertTrue(ValueClass.isNullRestrictedArray(newArray2));
136
137 // elements in a normal array can be null
138 for (int i=0; i < array.length; i++) {
139 newArray1[i] = null;
140 }
141 // NPE thrown if elements in a null-restricted array set to null
142 assertThrows(NullPointerException.class, () -> newArray2[0] = null);
143 }
144
145 private void testCopyOfRange(Object[] array, Object[] nullRestrictedArray, int from, int to) {
146 Object[] newArray1 = Arrays.copyOfRange(array, from, to);
147
148 // elements in a normal array can be null
149 for (int i=0; i < newArray1.length; i++) {
150 newArray1[i] = null;
151 }
152
153 // check the new array padded with null if normal array and
154 // zero instance if null-restricted array
155 for (int i=0; i < newArray1.length; i++) {
156 if (from+1 >= array.length) {
157 // padded with null
158 assertTrue(newArray1[i] == null);
159 }
160 }
161
162 if (to > array.length) {
163 // NullRestricted arrays do not have a value to fill new array elements
164 assertThrows(IllegalArgumentException.class, () -> Arrays.copyOfRange(nullRestrictedArray, from, to));
165 } else {
166 Object[] newArray2 = Arrays.copyOfRange(nullRestrictedArray, from, to);
167 System.out.println("newArray2 " + newArray2.length + " " + Arrays.toString(newArray2));
168 // NPE thrown if elements in a null-restricted array set to null
169 assertThrows(NullPointerException.class, () -> newArray2[0] = null);
170 }
171 }
172
173 @Test
174 public void testVarHandle() {
175 int len = 4;
176 Object[] array = (Object[]) Array.newInstance(Value.class, len);
177 Object[] nullRestrictedArray = ValueClass.newNullRestrictedNonAtomicArray(Value.class, len, new Value());
178
179 // Test var handles
180 testVarHandleArray(array, Value[].class, false);
181 testVarHandleArray(array, I[].class, false);
182 testVarHandleArray(nullRestrictedArray, Value[].class, true);
183 testVarHandleArray(nullRestrictedArray, I[].class, true);
184 }
185
186 private void testVarHandleArray(Object[] array, Class<?> arrayClass, boolean isNullRestricted) {
187 for (int i=0; i < array.length; i++) {
188 array[i] = new Value(i);
189 }
190
191 VarHandle vh = MethodHandles.arrayElementVarHandle(arrayClass);
192 Value value = new Value(0);
193 Value value1 = new Value(1);
194 Value value2 = new Value(2);
195
196 assertTrue(vh.get(array, 0) == value);
197 assertTrue(vh.getVolatile(array, 0) == value);
198 assertTrue(vh.getOpaque(array, 0) == value);
199 assertTrue(vh.getAcquire(array, 0) == value);
200
201 // test set with null values
202
203 if (!isNullRestricted) {
204 // if not null-restricted, we expect these set operations to succeed
205
206 vh.set(array, 0, null);
207 assertNull(vh.get(array, 0));
208 vh.setVolatile(array, 0, null);
209 assertNull(vh.get(array, 0));
210 vh.setOpaque(array, 0, null);
211 assertNull(vh.get(array, 0));
212 vh.setRelease(array, 0, null);
213 assertNull(vh.get(array, 0));
214
215 assertTrue(vh.compareAndSet(array, 1, value1, null));
216 assertNull(vh.get(array, 0));
217 vh.set(array, 1, value1);
218
219 assertEquals(vh.compareAndExchange(array, 1, value1, null), value1);
220 assertNull(vh.get(array, 0));
221 vh.set(array, 1, value1);
222
223 assertEquals(vh.compareAndExchangeAcquire(array, 1, value1, null), value1);
224 assertNull(vh.get(array, 0));
225 vh.set(array, 1, value1);
226
227 assertEquals(vh.compareAndExchangeRelease(array, 1, value1, null), value1);
228 assertNull(vh.get(array, 0));
229 vh.set(array, 1, value1);
230
231 assertTrue(vh.weakCompareAndSet(array, 1, value1, null));
232 assertNull(vh.get(array, 0));
233 vh.set(array, 1, value1);
234
235 assertTrue(vh.weakCompareAndSetAcquire(array, 1, value1, null));
236 assertNull(vh.get(array, 0));
237 vh.set(array, 1, value1);
238
239 assertTrue(vh.weakCompareAndSetPlain(array, 1, value1, null));
240 assertNull(vh.get(array, 0));
241 vh.set(array, 1, value1);
242
243 assertTrue(vh.weakCompareAndSetRelease(array, 1, value1, null));
244 assertNull(vh.get(array, 0));
245 vh.set(array, 1, value1);
246 } else {
247 // if null-restricted, we expect these set operations to fail
248
249 assertThrows(NullPointerException.class, () -> vh.set(array, 0, null));
250 assertThrows(NullPointerException.class, () -> vh.setVolatile(array, 0, null));
251 assertThrows(NullPointerException.class, () -> vh.setOpaque(array, 0, null));
252 assertThrows(NullPointerException.class, () -> vh.setRelease(array, 0, null));
253
254 assertThrows(NullPointerException.class, () -> vh.compareAndSet(array, 1, value1, null));
255 assertThrows(NullPointerException.class, () -> vh.compareAndExchange(array, 1, value1, null));
256 assertThrows(NullPointerException.class, () -> vh.compareAndExchangeAcquire(array, 1, value1, null));
257 assertThrows(NullPointerException.class, () -> vh.compareAndExchangeRelease(array, 1, value1, null));
258 assertThrows(NullPointerException.class, () -> vh.weakCompareAndSet(array, 1, value1, null));
259 assertThrows(NullPointerException.class, () -> vh.weakCompareAndSetAcquire(array, 1, value1, null));
260 assertThrows(NullPointerException.class, () -> vh.weakCompareAndSetPlain(array, 1, value1, null));
261 assertThrows(NullPointerException.class, () -> vh.weakCompareAndSetRelease(array, 1, value1, null));
262 }
263
264 // test set with non-null values
265
266 vh.set(array, 0, value1);
267 assertEquals(vh.get(array, 0), value1);
268 vh.setVolatile(array, 0, value1);
269 assertEquals(vh.get(array, 0), value1);
270 vh.setOpaque(array, 0, value1);
271 assertEquals(vh.get(array, 0), value1);
272 vh.setRelease(array, 0, value1);
273 assertEquals(vh.get(array, 0), value1);
274
275 assertTrue(vh.compareAndSet(array, 1, value1, value2));
276 assertEquals(vh.get(array, 1), value2);
277 vh.set(array, 1, value1);
278
279 assertEquals(vh.compareAndExchange(array, 1, value1, value2), value1);
280 assertEquals(vh.get(array, 1), value2);
281 vh.set(array, 1, value1);
282
283 assertEquals(vh.compareAndExchangeAcquire(array, 1, value1, value2), value1);
284 assertEquals(vh.get(array, 1), value2);
285 vh.set(array, 1, value1);
286
287 assertEquals(vh.compareAndExchangeRelease(array, 1, value1, value2), value1);
288 assertEquals(vh.get(array, 1), value2);
289 vh.set(array, 1, value1);
290
291 assertTrue(vh.weakCompareAndSet(array, 1, value1, value2));
292 assertEquals(vh.get(array, 1), value2);
293 vh.set(array, 1, value1);
294
295 assertTrue(vh.weakCompareAndSetAcquire(array, 1, value1, value2));
296 assertEquals(vh.get(array, 1), value2);
297 vh.set(array, 1, value1);
298
299 assertTrue(vh.weakCompareAndSetPlain(array, 1, value1, value2));
300 assertEquals(vh.get(array, 1), value2);
301 vh.set(array, 1, value1);
302
303 assertTrue(vh.weakCompareAndSetRelease(array, 1, value1, value2));
304 assertEquals(vh.get(array, 1), value2);
305 vh.set(array, 1, value1);
306
307 // test atomic set with null witness
308
309 assertFalse(vh.compareAndSet(array, 2, null, value1));
310 assertEquals(vh.get(array, 2), value2);
311
312 assertNotNull(vh.compareAndExchange(array, 2, null, value1));
313 assertEquals(vh.get(array, 2), value2);
314
315 assertNotNull(vh.compareAndExchangeAcquire(array, 2, null, value1));
316 assertEquals(vh.get(array, 2), value2);
317
318 assertNotNull(vh.compareAndExchangeRelease(array, 2, null, value1));
319 assertEquals(vh.get(array, 2), value2);
320
321 assertFalse(vh.weakCompareAndSet(array, 2, null, value1));
322 assertEquals(vh.get(array, 2), value2);
323
324 assertFalse(vh.weakCompareAndSetAcquire(array, 2, null, value1));
325 assertEquals(vh.get(array, 2), value2);
326
327 assertFalse(vh.weakCompareAndSetPlain(array, 2, null, value1));
328 assertEquals(vh.get(array, 2), value2);
329
330 assertFalse(vh.weakCompareAndSetRelease(array, 2, null, value1));
331 assertEquals(vh.get(array, 2), value2);
332 }
333 }