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  * @summary unsafe get/put/with inline type
 31  * @modules java.base/jdk.internal.misc
 32  * @library /test/lib
 33  * @modules java.base/jdk.internal.vm.annotation
 34  * @enablePreview
 35  * @compile Point.java UnsafeTest.java
 36  * @run main/othervm -XX:FlatArrayElementMaxSize=-1 -XX:InlineFieldMaxFlatSize=-1 runtime.valhalla.inlinetypes.UnsafeTest
 37  */
 38 
 39 import jdk.internal.misc.Unsafe;
 40 import jdk.internal.misc.VM;
 41 import jdk.internal.vm.annotation.ImplicitlyConstructible;
 42 import jdk.internal.vm.annotation.LooselyConsistentValue;
 43 import jdk.internal.vm.annotation.NullRestricted;
 44 
 45 import java.lang.reflect.*;
 46 import java.util.List;
 47 import static jdk.test.lib.Asserts.*;
 48 
 49 public class UnsafeTest {
 50     static final Unsafe U = Unsafe.getUnsafe();
 51 
 52     @ImplicitlyConstructible
 53     @LooselyConsistentValue
 54     static value class Value1 {
 55         @NullRestricted
 56         Point point;
 57         Point[] array;
 58         Value1(Point p, Point... points) {
 59             this.point = p;
 60             this.array = points;
 61         }
 62     }
 63 
 64     @ImplicitlyConstructible
 65     @LooselyConsistentValue
 66     static value class Value2 {
 67         int i;
 68         @NullRestricted
 69         Value1 v;
 70 
 71         Value2(Value1 v, int i) {
 72             this.v = v;
 73             this.i = i;
 74         }
 75     }
 76 
 77     @ImplicitlyConstructible
 78     @LooselyConsistentValue
 79     static value class Value3 {
 80         Object o;
 81         @NullRestricted
 82         Value2 v;
 83 
 84         Value3(Value2 v, Object ref) {
 85             this.v = v;
 86             this.o = ref;
 87         }
 88 
 89     }
 90 
 91 
 92     public static void main(String[] args) throws Throwable {
 93         printValueClass(Value3.class, 0);
 94 
 95         Value1 v1 = new Value1(new Point(10,10), new Point(20,20), new Point(30,30));
 96         Value2 v2 = new Value2(v1, 20);
 97         Value3 v3 = new Value3(v2, List.of("Value3"));
 98         long off_o = U.objectFieldOffset(Value3.class, "o");
 99         long off_v = U.objectFieldOffset(Value3.class, "v");
100         long off_i = U.objectFieldOffset(Value2.class, "i");
101         long off_v2 = U.objectFieldOffset(Value2.class, "v");
102 
103         long off_point = U.objectFieldOffset(Value1.class, "point");
104 
105         /*
106          * Layout of Value3
107          *
108          * | valueheader | o | i | x | y | array |
109          *                       ^-------^
110          *                        Point
111          *                       ^---------------^
112          *                        Value1
113          *
114          *                   ^-------------------^
115          *                    Value2
116          */
117         List<String> list = List.of("Value1", "Value2", "Value3");
118         Value3 v = v3;
119         try {
120             v = U.makePrivateBuffer(v);
121             // patch v3.o
122             U.putObject(v, off_o, list);
123             // patch v3.v.i;
124             U.putInt(v, off_v + off_i - U.valueHeaderSize(Value2.class), 999);
125             // patch v3.v.v.point
126             U.putValue(v, off_v + off_v2 - U.valueHeaderSize(Value2.class) + off_point - U.valueHeaderSize(Value1.class),
127                        Point.class, new Point(100, 100));
128         } finally {
129             v = U.finishPrivateBuffer(v);
130         }
131 
132         assertEquals(v.v.v.point, new Point(100, 100));
133         assertEquals(v.v.i, 999);
134         assertEquals(v.o, list);
135         assertEquals(v.v.v.array, v1.array);
136 
137         Value1 nv1 = new Value1(new Point(70,70), new Point(80,80), new Point(90,90));
138         Value2 nv2 = new Value2(nv1, 100);
139         Value3 nv3 = new Value3(nv2, list);
140 
141         try {
142             v = U.makePrivateBuffer(v);
143             // patch v3.v
144             U.putValue(v, off_v2, Value2.class, nv2);
145         } finally {
146             v = U.finishPrivateBuffer(v);
147         }
148         assertEquals(v, nv3);
149     }
150 
151     static void printValueClass(Class<?> vc, int level) {
152         String indent = "";
153         for (int i=0; i < level; i++) {
154             indent += "  ";
155         }
156         System.out.format("%s%s header size %d%n", indent, vc, U.valueHeaderSize(vc));
157         for (Field f : vc.getDeclaredFields()) {
158             System.out.format("%s%s: %s%s offset %d%n", indent, f.getName(),
159                               U.isFlatField(f) ? "flattened " : "", f.getType(),
160                               U.objectFieldOffset(vc, f.getName()));
161             if (U.isFlatField(f)) {
162                 printValueClass(f.getType(), level+1);
163             }
164         }
165     }
166 }