1 /*
   2  * Copyright (c) 2018, 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 package runtime.valhalla.valuetypes;
  24 
  25 import java.lang.invoke.*;
  26 
  27 import jdk.experimental.value.MethodHandleBuilder;
  28 
  29 /*
  30  * @test ObjectMethods
  31  * @summary Check object method implemented by the VM behave with value types
  32  * @modules java.base/jdk.experimental.bytecode
  33  *          java.base/jdk.experimental.value
  34  * @library /test/lib
  35  * @compile -XDenableValueTypes ObjectMethods.java
  36  * @run main/othervm -Xint -XX:+EnableValhalla -XX:+UseBiasedLocking -XX:+UseCompressedClassPointers runtime.valhalla.valuetypes.ObjectMethods
  37  * @run main/othervm -Xint -XX:+EnableValhalla -XX:-UseBiasedLocking -XX:-UseCompressedClassPointers runtime.valhalla.valuetypes.ObjectMethods
  38  * @run main/othervm -Xint -XX:+EnableValhalla -noverify runtime.valhalla.valuetypes.ObjectMethods noverify
  39  * @run main/othervm -Xcomp -XX:+EnableValhalla -XX:+UseBiasedLocking -XX:+UseCompressedClassPointers runtime.valhalla.valuetypes.ObjectMethods
  40  * @run main/othervm -Xcomp -XX:+EnableValhalla -XX:-UseBiasedLocking -XX:-UseCompressedClassPointers runtime.valhalla.valuetypes.ObjectMethods
  41  * @run main/othervm -Xcomp -XX:+EnableValhalla -noverify runtime.valhalla.valuetypes.ObjectMethods noverify
  42  */
  43 
  44 public class ObjectMethods {
  45 
  46     public static void main(String[] args) {
  47         testObjectMethods((args.length > 0 && args[0].equals("noverify")));
  48     }
  49 
  50     public static void testObjectMethods(boolean verifierDisabled) {
  51         MyInt val = MyInt.create(7);
  52         MyInt sameVal = MyInt.create(7);
  53 
  54         // Exercise all the Object native/VM methods...
  55 
  56         if (verifierDisabled) { // Just noverifier...
  57             checkMonitorExit(val);
  58             return;
  59         }
  60 
  61         // getClass()
  62         checkGetClass(val, MyInt.class);
  63 
  64         //hashCode()/identityHashCode()
  65         checkHashCodes(val, sameVal.hashCode());
  66 
  67         // clone()
  68         checkNotCloneable(val);
  69 
  70         // synchronized
  71         checkSynchronized(val);
  72 
  73         // wait/notify()
  74         checkWait(val);
  75         checkNotify(val);
  76 
  77         System.gc();
  78     }
  79 
  80 
  81     static void checkGetClass(Object val, Class<?> expectedClass) {
  82         Class<?> clazz = val.getClass();
  83         if (clazz == null) {
  84             throw new RuntimeException("getClass return null");
  85         } else if (clazz != expectedClass) {
  86             throw new RuntimeException("getClass (" + clazz + ") doesn't match " + expectedClass);
  87         }
  88     }
  89 
  90     // Just check we don't crash the VM
  91     static void checkHashCodes(Object val, int expectedHashCode) {
  92         if (val.hashCode() != expectedHashCode) {
  93             throw new RuntimeException("Hash code mismatch value: " + val.hashCode() +
  94                                        " expected: " + expectedHashCode);
  95         }
  96     }
  97 
  98     static void checkNotCloneable(MyInt val) {
  99         boolean sawCnse = false;
 100         try {
 101             val.attemptClone();
 102         } catch (CloneNotSupportedException cnse) {
 103             sawCnse = true;
 104         }
 105         if (!sawCnse) {
 106             throw new RuntimeException("clone() did not fail");
 107         }
 108         // Cloneable value type checked by "BadValueTypes" CFP tests
 109     }
 110 
 111     static void checkSynchronized(Object val) {
 112         boolean sawImse = false;
 113         try {
 114             synchronized (val) {
 115                 throw new IllegalStateException("Unreachable code, reached");
 116             }
 117         } catch (IllegalMonitorStateException imse) {
 118             sawImse = true;
 119         }
 120         if (!sawImse) {
 121             throw new RuntimeException("monitorenter did not fail");
 122         }
 123         // synchronized method modifiers tested by "BadValueTypes" CFP tests
 124         // jni monitor ops tested by "ValueWithJni"
 125     }
 126 
 127     // Check we haven't broken the mismatched monitor block check...
 128     static void checkMonitorExit(Object val) {
 129         boolean sawImse = false;
 130         try {
 131             MethodHandleBuilder.loadCode(MethodHandles.lookup(),
 132                                          "mismatchedMonitorExit",
 133                                          MethodType.methodType(Void.TYPE, Object.class),
 134                                          CODE->{
 135                                              CODE
 136                                                  .aload(0)
 137                                                  .monitorexit()
 138                                                  .return_();
 139                                          }).invokeExact(val);
 140             throw new IllegalStateException("Unreachable code, reached");
 141         } catch (Throwable t) {
 142             if (t instanceof IllegalMonitorStateException) {
 143                 sawImse = true;
 144             } else {
 145                 throw new RuntimeException(t);
 146             }
 147         }
 148         if (!sawImse) {
 149             throw new RuntimeException("monitorexit did not fail");
 150         }
 151     }
 152 
 153     static void checkWait(Object val) {
 154         boolean sawImse = false;
 155         try {
 156             val.wait();
 157         } catch (IllegalMonitorStateException imse) {
 158             sawImse = true;
 159         } catch (InterruptedException intExc) {
 160             throw new RuntimeException(intExc);
 161         }
 162         if (!sawImse) {
 163             throw new RuntimeException("wait() did not fail");
 164         }
 165 
 166         sawImse = false;
 167         try {
 168             val.wait(1l);
 169         } catch (IllegalMonitorStateException imse) {
 170             sawImse = true;
 171         } catch (InterruptedException intExc) {
 172             throw new RuntimeException(intExc);
 173         }
 174         if (!sawImse) {
 175             throw new RuntimeException("wait() did not fail");
 176         }
 177 
 178         sawImse = false;
 179         try {
 180             val.wait(0l, 100);
 181         } catch (IllegalMonitorStateException imse) {
 182             sawImse = true;
 183         } catch (InterruptedException intExc) {
 184             throw new RuntimeException(intExc);
 185         }
 186         if (!sawImse) {
 187             throw new RuntimeException("wait() did not fail");
 188         }
 189     }
 190 
 191     static void checkNotify(Object val) {
 192         boolean sawImse = false;
 193         try {
 194             val.notify();
 195         } catch (IllegalMonitorStateException imse) {
 196             sawImse = true;
 197         }
 198         if (!sawImse) {
 199             throw new RuntimeException("notify() did not fail");
 200         }
 201 
 202         sawImse = false;
 203         try {
 204             val.notifyAll();
 205         } catch (IllegalMonitorStateException imse) {
 206             sawImse = true;
 207         }
 208         if (!sawImse) {
 209             throw new RuntimeException("notifyAll() did not fail");
 210         }
 211     }
 212 
 213     static final __ByValue class MyInt {
 214         final int value;
 215         private MyInt() { value = 0; }
 216         public static MyInt create(int v) {
 217             MyInt mi = __MakeDefault MyInt();
 218             mi = __WithField(mi.value, v);
 219             return mi;
 220         }
 221         public Object attemptClone() throws CloneNotSupportedException {
 222             try { // Check it is not possible to clone...
 223                 MethodHandles.Lookup lookup = MethodHandles.lookup();
 224                 MethodHandle mh = lookup.findVirtual(getClass(),
 225                                                      "clone",
 226                                                      MethodType.methodType(Object.class));
 227                 return mh.invokeExact(this);
 228             } catch (Throwable t) {
 229                 if (t instanceof CloneNotSupportedException) {
 230                     throw (CloneNotSupportedException) t;
 231                 }
 232                 throw new RuntimeException(t);
 233             }
 234         }
 235     }
 236 
 237 }