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 }