--- /dev/null 2018-12-07 12:58:07.000000000 -0800 +++ new/test/hotspot/jtreg/runtime/valhalla/valuetypes/UnsafeTest.java 2018-12-07 12:58:06.000000000 -0800 @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package runtime.valhalla.valuetypes; + +/* + * @test UnsafeTest + * @summary unsafe get/put/with value + * @modules java.base/jdk.internal.misc + * @compile -XDallowWithFieldOperator Point.java UnsafeTest.java + * @run main/othervm -Xint -XX:+EnableValhalla runtime.valhalla.valuetypes.UnsafeTest + * @run main/othervm -Xcomp -XX:+EnableValhalla runtime.valhalla.valuetypes.UnsafeTest + */ + +import jdk.internal.misc.Unsafe; + +import java.lang.reflect.*; +import java.util.List; +import java.util.Set; + +public class UnsafeTest { + static final Unsafe U = Unsafe.getUnsafe(); + + static value class Value1 { + Point point; + Point[] array; + Value1() { + this.point = Point.createPoint(1, 1); + this.array = new Point[0]; + } + + static Value1 create(Point p, Point... points) { + Value1 o = Value1.default; + o = __WithField(o.point, p); + o = __WithField(o.array, points); + return o; + } + } + + static value class Value2 { + int i; + Value1 v; + + Value2() { + this.i = 0; + this.v = Value1.create(Point.createPoint(0,0), new Point[0]); + } + + static Value2 create(Value1 v, int i) { + Value2 o = Value2.default; + o = __WithField(o.v, v); + o = __WithField(o.i, i); + return o; + } + } + + /* + * Layout of Value3 + * + * | valueheader | o | i | x | y | array | + * ^-------^ + * Point + * ^---------------^ + * Value1 + * + * ^-------------------^ + * Value2 + + */ + static value class Value3 { + Object o; + Value2 v; + + Value3() { + this.v = Value2.create(Value1.create(Point.createPoint(0,0), new Point[0]), 0); + this.o = new Object(); + } + + static Value3 create(Value2 v, Object ref) { + Value3 o = Value3.default; + o = __WithField(o.v, v); + o = __WithField(o.o, ref); + return o; + } + + Value3 patchObject(Object x) { + long offset = U.objectFieldOffset(Value3.class, "o"); + + Object o1 = U.getObject(o, offset); + Value3 value = U.withReference(Value3.class, this, offset, x); + Object o2 = U.getObject(value, offset); + return value; + } + + Value3 patchValue(Value2 x) { + long offset = U.objectFieldOffset(Value3.class, "v"); + + Value2 o1 = U.getValue(this, offset, Value2.class); + Value3 value = U.withValue(Value3.class, this, U.valueHeaderSize(Value3.class), offset, Value2.class, x); + Value2 o2 = U.getValue(value, offset, Value2.class); + return value; + } + + Value3 patchPoint(Point x) { + long offset0 = U.objectFieldOffset(Value3.class, "v"); + long offset1 = U.objectFieldOffset(Value2.class, "v"); + long offset2 = U.objectFieldOffset(Value1.class, "point"); + + long offset = offset0 + offset1 - U.valueHeaderSize(Value2.class) + offset2 - U.valueHeaderSize(Value3.class); + return U.withValue(Value3.class, this, U.valueHeaderSize(Value3.class), offset, Point.class, x); + } + + Value3 patchInt(int i) { + long offset0 = U.objectFieldOffset(Value3.class, "v"); + long offset1 = U.objectFieldOffset(Value2.class, "i"); + + long offset = offset0 + offset1 - U.valueHeaderSize(Value2.class); + Value2 v = U.getValue(this, offset0, Value2.class); + v = U.withInt(Value2.class, v, offset1, i); + return U.withValue(Value3.class, this, U.valueHeaderSize(Value3.class), offset, Value2.class, v); + } + } + + public static void main(String[] args) throws Throwable { + printValueClass(Value3.class, 0); + + Value1 v1 = Value1.create(Point.createPoint(10,10), Point.createPoint(20,20), Point.createPoint(30,30)); + Value2 v2 = Value2.create(v1, 20); + Value3 v3 = Value3.create(v2, List.of("Value3")); + + // patch Value3::o + Value3 v4 = v3.patchObject(Set.of("Value3")); + assertEquals(v4.o, Set.of("Value3")); + + Value1 nv1 = Value1.create(Point.createPoint(70,70), Point.createPoint(80,80), Point.createPoint(90,90)); + Value2 nv2 = Value2.create(nv1, 100); + Value3 nv3 = Value3.create(nv2, List.of("Value1", "Value2", "Value3")); + + // patch Value3::v + Value3 v5 = v3.patchValue(nv2); + assertEquals(v5.v, nv2); + + // patch Value3::v + Value3 v6 = v5.patchObject(List.of("Value1", "Value2", "Value3")); + assertEquals(v6, nv3); + + // patch Value1::point in a value instance of Value3 + Value3 v7 = v6.patchPoint(Point.createPoint(100, 100)); + assertEquals(v7.v.v.point, Point.createPoint(100, 100)); + + // patch Value2::i in a value instance of Value3 + Value3 v8 = v7.patchInt(999); + assertEquals(v8.v.i, 999); + + // test private buffer + testBuffer(); + } + + static void testBuffer() { + Value1 v1 = Value1.create(Point.createPoint(10,10), Point.createPoint(20,20), Point.createPoint(30,30)); + Value2 v2 = Value2.create(v1, 20); + Value3 v3 = Value3.create(v2, List.of("Value3")); + long off_o = U.objectFieldOffset(Value3.class, "o"); + long off_v = U.objectFieldOffset(Value3.class, "v"); + long off_i = U.objectFieldOffset(Value2.class, "i"); + long off_v2 = U.objectFieldOffset(Value2.class, "v"); + + long off_point = U.objectFieldOffset(Value1.class, "point"); + + Value3 v = v3; + try { + v = U.startPrivateBuffer(v); + // patch v3.o + U.putObject(v, off_o, List.of("Value1", "Value2", "Value3")); + // patch v3.v.i; + U.putInt(v, off_v + off_i - U.valueHeaderSize(Value2.class), 999); + // patch v3.v.v.point + U.putValue(v, off_v + off_v2 - U.valueHeaderSize(Value2.class) + off_point - U.valueHeaderSize(Value1.class), + Point.class, Point.createPoint(100, 100)); + } finally { + v = U.finishPrivateBuffer(v); + } + + assertEquals(v.v.v.point, Point.createPoint(100, 100)); + assertEquals(v.v.i, 999); + assertEquals(v.o, List.of("Value1", "Value2", "Value3")); + assertEquals(v.v.v.array, v1.array); + } + + static void assertEquals(Object o1, Object o2) { + if (o1 != o2 && !o1.equals(o2)) { + throw new AssertionError(o1 + " != " + o2); + } + } + + static void printValueClass(Class vc, int level) { + String indent = ""; + for (int i=0; i < level; i++) { + indent += " "; + } + System.out.format("%s%s header size %d%n", indent, vc, U.valueHeaderSize(vc)); + for (Field f : vc.getDeclaredFields()) { + System.out.format("%s%s: %s%s offset %d%n", indent, f.getName(), + U.isFlattened(f) ? "flattened " : "", f.getType(), + U.objectFieldOffset(vc, f.getName())); + if (U.isFlattened(f)) { + printValueClass(f.getType(), level+1); + } + } + } +}