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 }