1 /* 2 * Copyright (c) 2018, 2024, 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 package runtime.valhalla.inlinetypes; 25 26 27 28 /* 29 * @test UnsafeTest 30 * @requires vm.debug == true 31 * @summary unsafe get/put/with inline type 32 * @modules java.base/jdk.internal.misc 33 * @modules java.base/jdk.internal.value 34 * @library /test/lib 35 * @modules java.base/jdk.internal.vm.annotation 36 * @enablePreview 37 * @compile Point.java UnsafeTest.java 38 * @run main/othervm -Xint -XX:+UseNullableValueFlattening -XX:+UseArrayFlattening -XX:+UseFieldFlattening -XX:+PrintInlineLayout runtime.valhalla.inlinetypes.UnsafeTest 39 */ 40 41 // TODO 8350865 Implement unsafe intrinsics for nullable flat fields/arrays in C2 42 43 import jdk.internal.misc.Unsafe; 44 import jdk.internal.misc.VM; 45 import jdk.internal.value.ValueClass; 46 import jdk.internal.vm.annotation.LooselyConsistentValue; 47 import jdk.internal.vm.annotation.NullRestricted; 48 import jdk.test.lib.Asserts; 49 50 import java.lang.reflect.*; 51 import java.util.List; 52 import static jdk.test.lib.Asserts.*; 53 54 public class UnsafeTest { 55 static final Unsafe U = Unsafe.getUnsafe(); 56 57 @LooselyConsistentValue 58 static value class Value1 { 59 @NullRestricted 60 Point point; 61 Point[] array; 62 Value1(Point p, Point... points) { 63 this.point = p; 64 this.array = points; 65 } 66 } 67 68 @LooselyConsistentValue 69 static value class Value2 { 70 int i; 71 @NullRestricted 72 Value1 v; 73 74 Value2(Value1 v, int i) { 75 this.v = v; 76 this.i = i; 77 } 78 } 79 80 @LooselyConsistentValue 81 static value class Value3 { 82 Object o; 83 @NullRestricted 84 Value2 v; 85 86 Value3(Value2 v, Object ref) { 87 this.v = v; 88 this.o = ref; 89 } 90 91 } 92 93 public static void test0() throws Throwable { 94 printValueClass(Value3.class, 0); 95 96 Value1 v1 = new Value1(new Point(10,10), new Point(20,20), new Point(30,30)); 97 Value2 v2 = new Value2(v1, 20); 98 Value3 v3 = new Value3(v2, List.of("Value3")); 99 long off_o = U.objectFieldOffset(Value3.class, "o"); 100 long off_v = U.objectFieldOffset(Value3.class, "v"); 101 long off_i = U.objectFieldOffset(Value2.class, "i"); 102 long off_v2 = U.objectFieldOffset(Value2.class, "v"); 103 104 long off_point = U.objectFieldOffset(Value1.class, "point"); 105 106 List<String> list = List.of("Value1", "Value2", "Value3"); 107 Value3 v = v3; 108 try { 109 v = U.makePrivateBuffer(v); 110 // patch v3.o 111 U.putReference(v, off_o, list); 112 // patch v3.v.i; 113 U.putInt(v, off_v + off_i - U.valueHeaderSize(Value2.class), 999); 114 // patch v3.v.v.point 115 U.putValue(v, off_v + off_v2 - U.valueHeaderSize(Value2.class) + off_point - U.valueHeaderSize(Value1.class), 116 Point.class, new Point(100, 100)); 117 } finally { 118 v = U.finishPrivateBuffer(v); 119 } 120 121 assertEquals(v.v.v.point, new Point(100, 100)); 122 assertEquals(v.v.i, 999); 123 assertEquals(v.o, list); 124 assertEquals(v.v.v.array, v1.array); 125 126 Value1 nv1 = new Value1(new Point(70,70), new Point(80,80), new Point(90,90)); 127 Value2 nv2 = new Value2(nv1, 100); 128 Value3 nv3 = new Value3(nv2, list); 129 130 try { 131 v = U.makePrivateBuffer(v); 132 // patch v3.v 133 U.putValue(v, off_v2, Value2.class, nv2); 134 } finally { 135 v = U.finishPrivateBuffer(v); 136 } 137 assertEquals(v, nv3); 138 } 139 140 static void printValueClass(Class<?> vc, int level) { 141 String indent = ""; 142 for (int i=0; i < level; i++) { 143 indent += " "; 144 } 145 System.out.format("%s%s header size %d%n", indent, vc, U.valueHeaderSize(vc)); 146 for (Field f : vc.getDeclaredFields()) { 147 System.out.format("%s%s: %s%s offset %d%n", indent, f.getName(), 148 U.isFlatField(f) ? "flattened " : "", f.getType(), 149 U.objectFieldOffset(vc, f.getName())); 150 if (U.isFlatField(f)) { 151 printValueClass(f.getType(), level+1); 152 } 153 } 154 } 155 156 // Requires -XX:+UseNullableValueFlattening 157 static value class MyValue0 { 158 int val; 159 160 public MyValue0(int i) { 161 val = i; 162 } 163 } 164 165 static class Container0 { 166 MyValue0 v; 167 } 168 169 public static void test1() throws Throwable { 170 Container0 c = new Container0(); 171 Class<?> cc = Container0.class; 172 Field[] fields = cc.getDeclaredFields(); 173 Asserts.assertEquals(fields.length, 1); 174 Field f = fields[0]; 175 System.out.println("Field found: " + f); 176 Asserts.assertTrue(U.isFlatField(f)); 177 Asserts.assertTrue(U.hasNullMarker(f)); 178 int nmOffset = U.nullMarkerOffset(f); 179 Asserts.assertNotEquals(nmOffset, -1); 180 byte nm = U.getByte(c, nmOffset); 181 Asserts.assertEquals(nm, (byte)0); 182 c.v = new MyValue0(42); 183 Asserts.assertNotNull(c.v); 184 nm = U.getByte(c, nmOffset); 185 Asserts.assertNotEquals(nm, 0); 186 U.getAndSetByteRelease(c, nmOffset, (byte)0); 187 Asserts.assertNull(c.v); 188 } 189 190 static value class TestValue1 { 191 short s0,s1; 192 193 TestValue1() { 194 s0 = 0; 195 s1 = 0; 196 } 197 198 TestValue1(short v0, short v1) { 199 s0 = v0; 200 s1 = v1; 201 } 202 } 203 204 static class Container1 { 205 TestValue1 value; 206 } 207 208 // Testing of nullable flat field supports in Unsafe.getFlatValue()/Unsafe.putValue() 209 public static void testNullableFlatFields() throws Throwable { 210 Container1 c = new Container1(); 211 Class<?> cc = Container1.class; 212 Field field = cc.getDeclaredField("value"); 213 Class<?> fc = TestValue1.class; 214 long offset = U.objectFieldOffset(field); 215 if (!U.isFlatField(field)) return; // Field not flattened (due to VM flags?), test doesn't apply 216 // Initial value of the field must be null 217 Asserts.assertNull(U.getValue(c, offset, fc)); 218 // Writing all zero value to the field, field must become non-null 219 TestValue1 val0 = new TestValue1((short)0, (short)0); 220 U.putValue(c, offset, fc, val0); 221 TestValue1 rval = U.getValue(c, offset, fc); 222 Asserts.assertNotNull(rval); 223 Asserts.assertEQ((short)0, rval.s0); 224 Asserts.assertEQ((short)0, rval.s1); 225 Asserts.assertEQ((short)0, c.value.s0); 226 Asserts.assertEQ((short)0, c.value.s1); 227 // Writing null to the field, field must become null again 228 U.putValue(c, offset, fc, null); 229 Asserts.assertNull(U.getValue(c, offset, fc)); 230 Asserts.assertNull(c.value); 231 // Writing non zero value to the field 232 TestValue1 val1 = new TestValue1((short)-1, (short)-2); 233 U.putValue(c, offset, fc, val1); 234 rval = U.getValue(c, offset, fc); 235 Asserts.assertNotNull(rval); 236 Asserts.assertNotNull(c.value); 237 Asserts.assertEQ((short)-1, rval.s0); 238 Asserts.assertEQ((short)-2, rval.s1); 239 Asserts.assertEQ((short)-1, c.value.s0); 240 Asserts.assertEQ((short)-2, c.value.s1); 241 // Writing a different non zero value 242 TestValue1 val2 = new TestValue1((short)Short.MAX_VALUE, (short)3); 243 U.putValue(c, offset, fc, val2); 244 rval = U.getValue(c, offset, fc); 245 Asserts.assertNotNull(rval); 246 Asserts.assertNotNull(c.value); 247 Asserts.assertEQ(Short.MAX_VALUE, c.value.s0); 248 Asserts.assertEQ((short)3, rval.s1); 249 Asserts.assertEQ(Short.MAX_VALUE, c.value.s0); 250 Asserts.assertEQ((short)3, rval.s1); 251 } 252 253 public static void testNullableFlatFields2() throws Throwable { 254 Container1 c = new Container1(); 255 Class<?> cc = Container1.class; 256 Field field = cc.getDeclaredField("value"); 257 Class<?> fc = TestValue1.class; 258 long offset = U.objectFieldOffset(field); 259 int layoutKind = U.fieldLayout(field); 260 if (!U.isFlatField(field)) return; // Field not flattened (due to VM flags?), test doesn't apply 261 // Initial value of the field must be null 262 Asserts.assertNull(U.getFlatValue(c, offset, layoutKind, fc)); 263 // Writing all zero value to the field, field must become non-null 264 TestValue1 val0 = new TestValue1((short)0, (short)0); 265 U.putFlatValue(c, offset, layoutKind, fc, val0); 266 TestValue1 rval = U.getFlatValue(c, offset, layoutKind, fc); 267 Asserts.assertNotNull(rval); 268 Asserts.assertEQ((short)0, rval.s0); 269 Asserts.assertEQ((short)0, rval.s1); 270 Asserts.assertEQ((short)0, c.value.s0); 271 Asserts.assertEQ((short)0, c.value.s1); 272 // Writing null to the field, field must become null again 273 U.putFlatValue(c, offset, layoutKind, fc, null); 274 Asserts.assertNull(U.getFlatValue(c, offset, layoutKind, fc)); 275 Asserts.assertNull(c.value); 276 // Writing non zero value to the field 277 TestValue1 val1 = new TestValue1((short)-1, (short)-2); 278 U.putFlatValue(c, offset, layoutKind, fc, val1); 279 rval = U.getFlatValue(c, offset, layoutKind, fc); 280 Asserts.assertNotNull(rval); 281 Asserts.assertNotNull(c.value); 282 Asserts.assertEQ((short)-1, rval.s0); 283 Asserts.assertEQ((short)-2, rval.s1); 284 Asserts.assertEQ((short)-1, c.value.s0); 285 Asserts.assertEQ((short)-2, c.value.s1); 286 // Writing a different non zero value 287 TestValue1 val2 = new TestValue1((short)Short.MAX_VALUE, (short)3); 288 U.putFlatValue(c, offset, layoutKind, fc, val2); 289 rval = U.getFlatValue(c, offset, layoutKind, fc); 290 Asserts.assertNotNull(rval); 291 Asserts.assertNotNull(c.value); 292 Asserts.assertEQ(Short.MAX_VALUE, c.value.s0); 293 Asserts.assertEQ((short)3, rval.s1); 294 Asserts.assertEQ(Short.MAX_VALUE, c.value.s0); 295 Asserts.assertEQ((short)3, rval.s1); 296 } 297 298 // Testing of nullable flat arrays supports in Unsafe.getValue()/Unsafe.putValue() 299 public static void testNullableFlatArrays() throws Throwable { 300 final int ARRAY_LENGTH = 10; 301 TestValue1[] array = (TestValue1[])ValueClass.newNullableAtomicArray(TestValue1.class, ARRAY_LENGTH); 302 long baseOffset = U.arrayBaseOffset(array.getClass()); 303 int scaleIndex = U.arrayIndexScale(array.getClass()); 304 for (int i = 0; i < ARRAY_LENGTH; i++) { 305 Asserts.assertNull(U.getValue(array, baseOffset + i * scaleIndex, TestValue1.class)); 306 } 307 TestValue1 val = new TestValue1((short)0, (short)0); 308 for (int i = 0; i < ARRAY_LENGTH; i++) { 309 if (i % 2 == 0) { 310 U.putValue(array, baseOffset + i * scaleIndex, TestValue1.class, val ); 311 } 312 } 313 for (int i = 0; i < ARRAY_LENGTH; i++) { 314 if (i % 2 == 0) { 315 Asserts.assertNotNull(U.getValue(array, baseOffset + i * scaleIndex, TestValue1.class)); 316 Asserts.assertNotNull(array[i]); 317 } else { 318 Asserts.assertNull(U.getValue(array, baseOffset + i * scaleIndex, TestValue1.class)); 319 Asserts.assertNull(array[i]); 320 } 321 } 322 TestValue1 val2 = new TestValue1((short)Short.MAX_VALUE, (short)Short.MIN_VALUE); 323 for (int i = 0; i < ARRAY_LENGTH; i++) { 324 if (i % 2 != 0) { 325 U.putValue(array, baseOffset + i * scaleIndex, TestValue1.class, val2 ); 326 } else { 327 U.putValue(array, baseOffset + i * scaleIndex, TestValue1.class, null ); 328 } 329 } 330 for (int i = 0; i < ARRAY_LENGTH; i++) { 331 if (i % 2 != 0) { 332 TestValue1 rval = U.getValue(array, baseOffset + i * scaleIndex, TestValue1.class); 333 Asserts.assertNotNull(rval); 334 Asserts.assertEQ(val2.s0, rval.s0); 335 Asserts.assertEQ(val2.s1, rval.s1); 336 Asserts.assertNotNull(array[i]); 337 Asserts.assertEQ(val2.s0, array[i].s0); 338 Asserts.assertEQ(val2.s1, array[i].s1); 339 } else { 340 Asserts.assertNull(U.getValue(array, baseOffset + i * scaleIndex, TestValue1.class)); 341 Asserts.assertNull(array[i]); 342 } 343 } 344 for (int i = 0; i < ARRAY_LENGTH; i++) { 345 U.putValue(array, baseOffset + i * scaleIndex, TestValue1.class, null ); 346 } 347 for (int i = 0; i < ARRAY_LENGTH; i++) { 348 Asserts.assertNull(U.getValue(array, baseOffset + i * scaleIndex, TestValue1.class)); 349 Asserts.assertNull(array[i]); 350 } 351 } 352 353 // Testing of nullable flat arrays supports in Unsafe.getFlatValue()/Unsafe.putFlatValue() 354 public static void testNullableFlatArrays2() throws Throwable { 355 final int ARRAY_LENGTH = 10; 356 TestValue1[] array = (TestValue1[])ValueClass.newNullableAtomicArray(TestValue1.class, ARRAY_LENGTH); 357 long baseOffset = U.arrayBaseOffset(array.getClass()); 358 int scaleIndex = U.arrayIndexScale(array.getClass()); 359 int layoutKind = U.arrayLayout(array.getClass()); 360 for (int i = 0; i < ARRAY_LENGTH; i++) { 361 Asserts.assertNull(U.getFlatValue(array, baseOffset + i * scaleIndex, layoutKind, TestValue1.class)); 362 } 363 TestValue1 val = new TestValue1((short)0, (short)0); 364 for (int i = 0; i < ARRAY_LENGTH; i++) { 365 if (i % 2 == 0) { 366 U.putFlatValue(array, baseOffset + i * scaleIndex, layoutKind, TestValue1.class, val ); 367 } 368 } 369 for (int i = 0; i < ARRAY_LENGTH; i++) { 370 if (i % 2 == 0) { 371 Asserts.assertNotNull(U.getFlatValue(array, baseOffset + i * scaleIndex, layoutKind, TestValue1.class)); 372 Asserts.assertNotNull(array[i]); 373 } else { 374 Asserts.assertNull(U.getFlatValue(array, baseOffset + i * scaleIndex, layoutKind, TestValue1.class)); 375 Asserts.assertNull(array[i]); 376 } 377 } 378 TestValue1 val2 = new TestValue1((short)Short.MAX_VALUE, (short)Short.MIN_VALUE); 379 for (int i = 0; i < ARRAY_LENGTH; i++) { 380 if (i % 2 != 0) { 381 U.putFlatValue(array, baseOffset + i * scaleIndex, layoutKind, TestValue1.class, val2 ); 382 } else { 383 U.putFlatValue(array, baseOffset + i * scaleIndex, layoutKind, TestValue1.class, null ); 384 } 385 } 386 for (int i = 0; i < ARRAY_LENGTH; i++) { 387 if (i % 2 != 0) { 388 TestValue1 rval = U.getFlatValue(array, baseOffset + i * scaleIndex, layoutKind, TestValue1.class); 389 Asserts.assertNotNull(rval); 390 Asserts.assertEQ(val2.s0, rval.s0); 391 Asserts.assertEQ(val2.s1, rval.s1); 392 Asserts.assertNotNull(array[i]); 393 Asserts.assertEQ(val2.s0, array[i].s0); 394 Asserts.assertEQ(val2.s1, array[i].s1); 395 } else { 396 Asserts.assertNull(U.getFlatValue(array, baseOffset + i * scaleIndex, layoutKind, TestValue1.class)); 397 Asserts.assertNull(array[i]); 398 } 399 } 400 for (int i = 0; i < ARRAY_LENGTH; i++) { 401 U.putFlatValue(array, baseOffset + i * scaleIndex, layoutKind, TestValue1.class, null ); 402 } 403 for (int i = 0; i < ARRAY_LENGTH; i++) { 404 Asserts.assertNull(U.getFlatValue(array, baseOffset + i * scaleIndex, layoutKind, TestValue1.class)); 405 Asserts.assertNull(array[i]); 406 } 407 } 408 409 public static void main(String[] args) throws Throwable { 410 test0(); 411 test1(); 412 testNullableFlatFields(); 413 testNullableFlatFields2(); 414 testNullableFlatArrays(); 415 testNullableFlatArrays2(); 416 } 417 418 }