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