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