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 }