1 /*
   2  * Copyright (c) 2016, 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.valuetypes;
  25 
  26 import java.lang.invoke.*;
  27 import jdk.experimental.value.*;
  28 import jdk.test.lib.*;
  29 import static jdk.test.lib.Asserts.*;
  30 
  31 /*
  32  * @test VWithFieldTest
  33  * @summary vwithfield bytecode test
  34  * @library /testlibrary /
  35  * @run main/othervm -noverify -Xint runtime.valhalla.valuetypes.VWithFieldTest
  36  */
  37 
  38 public class VWithFieldTest {
  39 
  40     static __ByValue final class Point {
  41         final private int x;
  42         final private int y;
  43 
  44         __ValueFactory static Point make(int x, int y) {
  45             Point p = __MakeDefault Point();
  46             Asserts.assertEquals(p.x, 0, "invalid x default value");
  47             Asserts.assertEquals(p.y, 0, "invalid y default value");
  48             p.x = x;
  49             Asserts.assertEquals(p.x, x, "invalid x value");
  50             Asserts.assertEquals(p.y, 0, "invalid y value");
  51             p.y = y;
  52             Asserts.assertEquals(p.x, x, "invalid x value");
  53             Asserts.assertEquals(p.y, y, "invalid y value");
  54             return p;
  55         }
  56 
  57         Point () {
  58             x = 0;
  59             y = 0;
  60         }
  61 
  62         public int getX() {
  63             return x;
  64         }
  65 
  66         __ValueFactory static Point setX(Point p, int x) {
  67             p.x = x;
  68             return p;
  69         }
  70 
  71         public int getY() {
  72             return y;
  73         }
  74 
  75         __ValueFactory static Point setY(Point p, int y) {
  76             p.y = y;
  77             return p;
  78         }
  79     }
  80 
  81     public static void main(String[] args) {
  82         witherViaVvt();
  83         witherViaAnonymousClass();
  84         witherViaMvt();
  85     }
  86 
  87     static void witherViaVvt() {
  88         creationTest();
  89         witherTest();
  90     }
  91 
  92     static void creationTest() {
  93         Point p = Point.make(10,20);
  94         Asserts.assertEquals(p.x, 10, "invalid x value");
  95         Asserts.assertEquals(p.y, 20, "invalid y value");
  96     }
  97 
  98     static void witherTest() {
  99         Point p1 = Point.make(2,12);
 100         Asserts.assertEquals(p1.x, 2, "invalid x value");
 101         Asserts.assertEquals(p1.y, 12, "invalid y value");
 102         Point p2 = Point.setX(p1,3);
 103         Asserts.assertEquals(p2.x, 3, "invalid x value");
 104         Asserts.assertEquals(p2.y, 12, "invalid y value");
 105         Point p3 = Point.setY(p2, 14);
 106         Asserts.assertEquals(p3.x, 3, "invalid x value");
 107         Asserts.assertEquals(p3.y, 14, "invalid y value");
 108     }
 109 
 110 
 111     @jvm.internal.value.DeriveValueType
 112     static final class PointVcc {
 113         // "package-private" access: Test access from anonymous class
 114         // "final": Test vwithfield opcode issued from outside the (DVT) class
 115         final int x;
 116         final int y;
 117 
 118         private PointVcc(int x, int y) {
 119             this.x = x;
 120             this.y = y;
 121         }
 122 
 123         /**
 124          * MVT fields are final by definition, want to maintain full control of field values
 125          * ACC_FINAL states only the code that owns the fields may do this.
 126          *
 127          * MVT needs a small hole, whereby the lookup klass->host_class == VCC, may modify
 128          * DVT klass->derive_value_type_klass(). See: LinkResolver::resolve_field().
 129          *
 130          * But we are still left with this ugly code pattern, i.e. the method handle lookup
 131          * must be VCC..."ugly" but enforces the semantics of ACC_FINAL.
 132          *
 133          * ValueType.findWither() risks breaking code wishing to maintain value invariants,
 134          * by exposing the wither method handle to arbitrary code.
 135          *
 136          * - There appears to be a discrepancy between the ValueType.findWither() API and
 137          *   "Shady Values" document. Missing "Class<?> refc" (see MethodHandles.Lookup.findSetter()).
 138          *   It could be argued that this dependency on hiding "refc" is too weak/dangerous.
 139          */
 140         public static MethodHandle dvtCreator() {
 141             Class<?> dvtClass = (Class<?>) ValueType.forClass(PointVcc.class).valueClass();
 142             return MethodHandleBuilder.
 143                 loadCode(MethodHandles.lookup(),
 144                          "vdefaultWithXWithY",
 145                          MethodType.methodType(dvtClass, Integer.TYPE, Integer.TYPE),
 146                          CODE-> {
 147                              CODE
 148                                  .vdefault(dvtClass)
 149                                  .iload(0)
 150                                  .vwithfield(dvtClass, "x", "I")
 151                                  .iload(1)
 152                                  .vwithfield(dvtClass, "y", "I")
 153                                  .vreturn();
 154                          });
 155         }
 156 
 157         public String toString() {
 158             return "x=" + x + " y=" + y;
 159         }
 160     }
 161 
 162     static void witherViaAnonymousClass() {
 163         try {
 164             Class<?> vccClass = (Class<?>) PointVcc.class;
 165             ValueType<?> vt = ValueType.forClass(vccClass);
 166             Class<?> dvtClass = vt.valueClass();
 167 
 168             int x = 7;
 169             int y = 4711;
 170 
 171             MethodHandle boxMh = MethodHandleBuilder.
 172                     loadCode(MethodHandles.lookup(),
 173                              "boxPointVcc",
 174                              MethodType.methodType(vccClass, dvtClass),
 175                              CODE-> { CODE.vload(0).vbox(vccClass).areturn(); });
 176             PointVcc point = (PointVcc) MethodHandles.filterReturnValue(PointVcc.dvtCreator(),
 177                                                                         boxMh).invoke(x, y);
 178             assertTrue(point.x == x);
 179             assertTrue(point.y == y);
 180 
 181             try { // Try to modify final fields from another class, should fail...
 182                 PointVcc apoint = (PointVcc) MethodHandleBuilder.
 183                     loadCode(MethodHandles.lookup(),
 184                              "vdefaultWithXWithY",
 185                              MethodType.methodType(vccClass, Integer.TYPE, Integer.TYPE),
 186                              CODE-> {
 187                                  CODE
 188                                  .vdefault(dvtClass)
 189                                  .iload(0)
 190                                  .vwithfield(dvtClass, "x", "I")
 191                                  .iload(1)
 192                                  .vwithfield(dvtClass, "y", "I")
 193                                  .vbox(vccClass)
 194                                  .areturn();
 195                              }).invoke(x, y);
 196                 throw new RuntimeException("Expected IllegalAccessError");
 197             }
 198             catch (IllegalAccessError illAccErr) {}
 199         }
 200         catch (Throwable t) {
 201             throw new RuntimeException("witherViaAnonymousClass", t);
 202         }
 203     }
 204 
 205     static void witherViaMvt() {
 206         try {
 207             Class<?> vccClass = (Class<?>) PointVcc.class;
 208             ValueType<?> vt = ValueType.forClass(vccClass);
 209             Class<?> dvtClass = vt.valueClass();
 210 
 211             int x = 4711;
 212             int y = 13;
 213 
 214             MethodHandle defValue = vt.defaultValueConstant();
 215             MethodHandle setX = vt.findWither("x", Integer.TYPE);
 216             MethodHandle setY = vt.findWither("y", Integer.TYPE);
 217             MethodHandle boxValue = vt.box();
 218 
 219             // Chain these VT methods up to be "VT.defaultValueConstant().setX(x).setY(y).box()"...
 220             MethodHandle createX = MethodHandles.collectArguments(setX, 0, defValue);
 221             MethodHandle updateY = MethodHandles.collectArguments(setY, 0, createX);
 222             MethodHandle boxed   = MethodHandles.filterReturnValue(updateY, boxValue);
 223 
 224             PointVcc point = (PointVcc) boxed.invoke(x, y);
 225             assertTrue(point.x == x);
 226             assertTrue(point.y == y);
 227         }
 228         catch (Throwable t) {
 229             throw new RuntimeException("witherViaMvt", t);
 230         }
 231     }
 232 }