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