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 // TODO: 8353180: Remove requires != Xcomp
 25 
 26 /*
 27  * @test
 28  * @enablePreview
 29  * @requires vm.compMode != "Xcomp"
 30  * @run junit/othervm -XX:-UseArrayFlattening -XX:-UseNullableValueFlattening NullRestrictedArraysTest
 31  * @run junit/othervm -XX:+UseArrayFlattening -XX:+UseNullableValueFlattening NullRestrictedArraysTest
 32  */
 33 
 34 import java.lang.invoke.MethodHandles;
 35 import java.lang.invoke.VarHandle;
 36 import java.lang.reflect.Array;
 37 import java.lang.reflect.Field;
 38 import java.util.Arrays;
 39 import java.util.stream.Stream;
 40 
 41 import jdk.internal.value.ValueClass;
 42 import jdk.internal.vm.annotation.LooselyConsistentValue;
 43 import jdk.internal.vm.annotation.NullRestricted;
 44 import jdk.internal.vm.annotation.Strict;
 45 
 46 import org.junit.jupiter.api.Test;
 47 import org.junit.jupiter.params.ParameterizedTest;
 48 import org.junit.jupiter.params.provider.Arguments;
 49 import org.junit.jupiter.params.provider.MethodSource;
 50 
 51 import static org.junit.jupiter.api.Assertions.*;
 52 
 53 public class NullRestrictedArraysTest {
 54     interface I {
 55         int getValue();
 56     }
 57     @LooselyConsistentValue
 58     static value class Value implements I {
 59         int v;
 60         Value() {
 61             this(0);
 62         }
 63         Value(int v) {
 64             this.v = v;
 65         }
 66         public int getValue() {
 67             return v;
 68         }
 69     }
 70 
 71     static class T {
 72         String s;
 73         Value obj;  // can be null
 74         @Strict
 75         @NullRestricted
 76         Value value = new Value();
 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 }