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 }