/* * Copyright (c) 2016, 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; import java.lang.invoke.*; import jdk.experimental.value.*; import jdk.test.lib.*; import static jdk.test.lib.Asserts.*; /* * @test VWithFieldTest * @summary vwithfield bytecode test * @library /testlibrary / * @run main/othervm -noverify -Xint runtime.valhalla.valuetypes.VWithFieldTest */ public class VWithFieldTest { static __ByValue final class Point { final private int x; final private int y; __ValueFactory static Point make(int x, int y) { Point p = __MakeDefault Point(); Asserts.assertEquals(p.x, 0, "invalid x default value"); Asserts.assertEquals(p.y, 0, "invalid y default value"); p.x = x; Asserts.assertEquals(p.x, x, "invalid x value"); Asserts.assertEquals(p.y, 0, "invalid y value"); p.y = y; Asserts.assertEquals(p.x, x, "invalid x value"); Asserts.assertEquals(p.y, y, "invalid y value"); return p; } Point () { x = 0; y = 0; } public int getX() { return x; } __ValueFactory static Point setX(Point p, int x) { p.x = x; return p; } public int getY() { return y; } __ValueFactory static Point setY(Point p, int y) { p.y = y; return p; } } public static void main(String[] args) { witherViaVvt(); witherViaAnonymousClass(); witherViaMvt(); } static void witherViaVvt() { creationTest(); witherTest(); } static void creationTest() { Point p = Point.make(10,20); Asserts.assertEquals(p.x, 10, "invalid x value"); Asserts.assertEquals(p.y, 20, "invalid y value"); } static void witherTest() { Point p1 = Point.make(2,12); Asserts.assertEquals(p1.x, 2, "invalid x value"); Asserts.assertEquals(p1.y, 12, "invalid y value"); Point p2 = Point.setX(p1,3); Asserts.assertEquals(p2.x, 3, "invalid x value"); Asserts.assertEquals(p2.y, 12, "invalid y value"); Point p3 = Point.setY(p2, 14); Asserts.assertEquals(p3.x, 3, "invalid x value"); Asserts.assertEquals(p3.y, 14, "invalid y value"); } @jvm.internal.value.DeriveValueType static final class PointVcc { // "package-private" access: Test access from anonymous class // "final": Test vwithfield opcode issued from outside the (DVT) class final int x; final int y; private PointVcc(int x, int y) { this.x = x; this.y = y; } /** * MVT fields are final by definition, want to maintain full control of field values * ACC_FINAL states only the code that owns the fields may do this. * * MVT needs a small hole, whereby the lookup klass->host_class == VCC, may modify * DVT klass->derive_value_type_klass(). See: LinkResolver::resolve_field(). * * But we are still left with this ugly code pattern, i.e. the method handle lookup * must be VCC..."ugly" but enforces the semantics of ACC_FINAL. * * ValueType.findWither() risks breaking code wishing to maintain value invariants, * by exposing the wither method handle to arbitrary code. * * - There appears to be a discrepancy between the ValueType.findWither() API and * "Shady Values" document. Missing "Class refc" (see MethodHandles.Lookup.findSetter()). * It could be argued that this dependency on hiding "refc" is too weak/dangerous. */ public static MethodHandle dvtCreator() { Class dvtClass = (Class) ValueType.forClass(PointVcc.class).valueClass(); return MethodHandleBuilder. loadCode(MethodHandles.lookup(), "vdefaultWithXWithY", MethodType.methodType(dvtClass, Integer.TYPE, Integer.TYPE), CODE-> { CODE .vdefault(dvtClass) .iload(0) .vwithfield(dvtClass, "x", "I") .iload(1) .vwithfield(dvtClass, "y", "I") .vreturn(); }); } public String toString() { return "x=" + x + " y=" + y; } } static void witherViaAnonymousClass() { try { Class vccClass = (Class) PointVcc.class; ValueType vt = ValueType.forClass(vccClass); Class dvtClass = vt.valueClass(); int x = 7; int y = 4711; MethodHandle boxMh = MethodHandleBuilder. loadCode(MethodHandles.lookup(), "boxPointVcc", MethodType.methodType(vccClass, dvtClass), CODE-> { CODE.vload(0).vbox(vccClass).areturn(); }); PointVcc point = (PointVcc) MethodHandles.filterReturnValue(PointVcc.dvtCreator(), boxMh).invoke(x, y); assertTrue(point.x == x); assertTrue(point.y == y); try { // Try to modify final fields from another class, should fail... PointVcc apoint = (PointVcc) MethodHandleBuilder. loadCode(MethodHandles.lookup(), "vdefaultWithXWithY", MethodType.methodType(vccClass, Integer.TYPE, Integer.TYPE), CODE-> { CODE .vdefault(dvtClass) .iload(0) .vwithfield(dvtClass, "x", "I") .iload(1) .vwithfield(dvtClass, "y", "I") .vbox(vccClass) .areturn(); }).invoke(x, y); throw new RuntimeException("Expected IllegalAccessError"); } catch (IllegalAccessError illAccErr) {} } catch (Throwable t) { throw new RuntimeException("witherViaAnonymousClass", t); } } static void witherViaMvt() { try { Class vccClass = (Class) PointVcc.class; ValueType vt = ValueType.forClass(vccClass); Class dvtClass = vt.valueClass(); int x = 4711; int y = 13; MethodHandle defValue = vt.defaultValueConstant(); MethodHandle setX = vt.findWither("x", Integer.TYPE); MethodHandle setY = vt.findWither("y", Integer.TYPE); MethodHandle boxValue = vt.box(); // Chain these VT methods up to be "VT.defaultValueConstant().setX(x).setY(y).box()"... MethodHandle createX = MethodHandles.collectArguments(setX, 0, defValue); MethodHandle updateY = MethodHandles.collectArguments(setY, 0, createX); MethodHandle boxed = MethodHandles.filterReturnValue(updateY, boxValue); PointVcc point = (PointVcc) boxed.invoke(x, y); assertTrue(point.x == x); assertTrue(point.y == y); } catch (Throwable t) { throw new RuntimeException("witherViaMvt", t); } } }