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  * @enablePreview
 34  * @compile 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 = new MyInt(7);
 48         MyInt sameVal = new MyInt(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.class);
 59 
 60         //hashCode()/identityHashCode()
 61         checkHashCodes(val, sameVal.hashCode());
 62 
 63         // clone() - test the default implementation from j.l.Object
 64         checkNotCloneable(val);
 65         checkCloneable(new MyCloneableInt(78));
 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     }
112 
113     static void checkCloneable(MyCloneableInt val) {
114         boolean sawCnse = false;
115         MyCloneableInt val2 = null;
116         try {
117             val2 = (MyCloneableInt)val.attemptClone();
118         } catch (CloneNotSupportedException cnse) {
119             sawCnse = true;
120         }
121         if (sawCnse) {
122             throw new RuntimeException("clone() did fail");
123         }
124         if (val != val2) {
125             throw new RuntimeException("Cloned value is not identical to the original");
126         }
127     }
128 
129     static void checkSynchronized(Object val) {
130         boolean sawIe = false;
131         try {
132             synchronized (val) {
133                 throw new IdentityException("Unreachable code, reached");
134             }
135         } catch (IdentityException ie) {
136             sawIe = true;
137         }
138         if (!sawIe) {
139             throw new RuntimeException("monitorenter did not fail");
140         }
141         // synchronized method modifiers tested by "BadInlineTypes" CFP tests
142         // jni monitor ops tested by "InlineWithJni"
143     }
144 
145     // Check we haven't broken the mismatched monitor block check...
146     static void checkMonitorExit(Object val) {
147         boolean sawIe = false;
148         try {
149             InstructionHelper.buildMethodHandle(MethodHandles.lookup(),
150                                                 "mismatchedMonitorExit",
151                                                 MethodType.methodType(Void.TYPE, Object.class),
152                                                 CODE-> {
153                                                     CODE
154                                                     .aload(0)
155                                                     .monitorexit();
156                                                     CODE.return_();
157                                                 }).invokeExact(val);
158             throw new IllegalStateException("Unreachable code, reached");
159         } catch (Throwable t) {
160             if (t instanceof IllegalMonitorStateException) {
161                 sawIe = true;
162             } else {
163                 throw new RuntimeException(t);
164             }
165         }
166         if (!sawIe) {
167             throw new RuntimeException("monitorexit did not fail");
168         }
169     }
170 
171     static void checkWait(Object val) {
172         boolean sawImse = false;
173         try {
174             val.wait();
175         } catch (IllegalMonitorStateException imse) {
176             sawImse = true;
177         } catch (InterruptedException intExc) {
178             throw new RuntimeException(intExc);
179         }
180         if (!sawImse) {
181             throw new RuntimeException("wait() did not fail");
182         }
183 
184         sawImse = false;
185         try {
186             val.wait(1l);
187         } catch (IllegalMonitorStateException imse) {
188             sawImse = true;
189         } catch (InterruptedException intExc) {
190             throw new RuntimeException(intExc);
191         }
192         if (!sawImse) {
193             throw new RuntimeException("wait() did not fail");
194         }
195 
196         sawImse = false;
197         try {
198             val.wait(0l, 100);
199         } catch (IllegalMonitorStateException imse) {
200             sawImse = true;
201         } catch (InterruptedException intExc) {
202             throw new RuntimeException(intExc);
203         }
204         if (!sawImse) {
205             throw new RuntimeException("wait() did not fail");
206         }
207     }
208 
209     static void checkNotify(Object val) {
210         boolean sawImse = false;
211         try {
212             val.notify();
213         } catch (IllegalMonitorStateException imse) {
214             sawImse = true;
215         }
216         if (!sawImse) {
217             throw new RuntimeException("notify() did not fail");
218         }
219 
220         sawImse = false;
221         try {
222             val.notifyAll();
223         } catch (IllegalMonitorStateException imse) {
224             sawImse = true;
225         }
226         if (!sawImse) {
227             throw new RuntimeException("notifyAll() did not fail");
228         }
229     }
230 
231     static value class MyInt {
232         int value;
233         public MyInt(int v) { value = v; }
234         public Object attemptClone() throws CloneNotSupportedException {
235             try { // Check it is not possible to clone...
236                 MethodHandles.Lookup lookup = MethodHandles.lookup();
237                 MethodHandle mh = lookup.findVirtual(getClass(),
238                                                      "clone",
239                                                      MethodType.methodType(Object.class));
240                 return mh.invokeExact(this);
241             } catch (Throwable t) {
242                 if (t instanceof CloneNotSupportedException) {
243                     throw (CloneNotSupportedException) t;
244                 }
245                 throw new RuntimeException(t);
246             }
247         }
248     }
249 
250     static value class MyCloneableInt implements Cloneable {
251         int value;
252         public MyCloneableInt(int v) { value = v; }
253         public Object attemptClone() throws CloneNotSupportedException {
254             try { // Check it is not possible to clone...
255                 MethodHandles.Lookup lookup = MethodHandles.lookup();
256                 MethodHandle mh = lookup.findVirtual(getClass(),
257                                                      "clone",
258                                                      MethodType.methodType(Object.class));
259                 return mh.invokeExact(this);
260             } catch (Throwable t) {
261                 if (t instanceof CloneNotSupportedException) {
262                     throw (CloneNotSupportedException) t;
263                 }
264                 throw new RuntimeException(t);
265             }
266         }
267     }
268 
269 }