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