1 /*
  2  * Copyright (c) 2019, 2026, 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 
 24 /*
 25  * @test
 26  * @bug 8246774
 27  * @summary Basic tests for ObjectMethods
 28  * @run junit ObjectMethodsTest
 29  */
 30 
 31 import java.util.List;
 32 import java.lang.invoke.CallSite;
 33 import java.lang.invoke.MethodHandle;
 34 import java.lang.invoke.MethodHandles;
 35 import java.lang.invoke.MethodType;
 36 import java.lang.runtime.ObjectMethods;
 37 import static java.lang.invoke.MethodType.methodType;
 38 
 39 import static org.junit.jupiter.api.Assertions.assertEquals;
 40 import static org.junit.jupiter.api.Assertions.assertThrows;
 41 import static org.junit.jupiter.api.Assertions.assertFalse;
 42 import static org.junit.jupiter.api.Assertions.assertTrue;
 43 import org.junit.jupiter.api.Test;
 44 
 45 public class ObjectMethodsTest {
 46 
 47     public static class C {
 48         static final MethodType EQUALS_DESC = methodType(boolean.class, C.class, Object.class);
 49         static final MethodType HASHCODE_DESC = methodType(int.class, C.class);
 50         static final MethodType TO_STRING_DESC = methodType(String.class, C.class);
 51 
 52         static final MethodHandle[] ACCESSORS = accessors();
 53         static final String NAME_LIST = "x;y";
 54         private static MethodHandle[] accessors() {
 55             try {
 56                 return  new MethodHandle[]{
 57                         MethodHandles.lookup().findGetter(C.class, "x", int.class),
 58                         MethodHandles.lookup().findGetter(C.class, "y", int.class),
 59                 };
 60             } catch (Exception e) {
 61                 throw new AssertionError(e);
 62             }
 63         }
 64 
 65         private final int x;
 66         private final int y;
 67         C (int x, int y) { this.x = x; this.y = y; }
 68         public int x() { return x; }
 69         public int y() { return y; }
 70     }
 71 
 72     static class Empty {
 73         static final MethodType EQUALS_DESC = methodType(boolean.class, Empty.class, Object.class);
 74         static final MethodType HASHCODE_DESC = methodType(int.class, Empty.class);
 75         static final MethodType TO_STRING_DESC = methodType(String.class, Empty.class);
 76         static final MethodHandle[] ACCESSORS = new MethodHandle[] { };
 77         static final String NAME_LIST = "";
 78         Empty () {  }
 79     }
 80 
 81     static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
 82 
 83     @Test
 84     public void testEqualsC() throws Throwable {
 85         CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "equals", C.EQUALS_DESC, C.class, C.NAME_LIST, C.ACCESSORS);
 86         MethodHandle handle = cs.dynamicInvoker();
 87         C c = new C(5, 5);
 88         assertTrue((boolean)handle.invokeExact(c, (Object)c));
 89         assertTrue((boolean)handle.invokeExact(c, (Object)new C(5, 5)));
 90         assertFalse((boolean)handle.invokeExact(c, (Object)new C(5, 4)));
 91         assertFalse((boolean)handle.invokeExact(c, (Object)new C(4, 5)));
 92         assertFalse((boolean)handle.invokeExact(c, (Object)null));
 93         assertFalse((boolean)handle.invokeExact(c, new Object()));
 94     }
 95 
 96     @Test
 97     public void testEqualsEmpty() throws Throwable {
 98         CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "equals", Empty.EQUALS_DESC, Empty.class, Empty.NAME_LIST, Empty.ACCESSORS);
 99         MethodHandle handle = cs.dynamicInvoker();
100         Empty e = new Empty();
101         assertTrue((boolean)handle.invokeExact(e, (Object)e));
102         assertTrue((boolean)handle.invokeExact(e, (Object)new Empty()));
103         assertFalse((boolean)handle.invokeExact(e, (Object)null));
104         assertFalse((boolean)handle.invokeExact(e, new Object()));
105     }
106 
107     @Test
108     public void testHashCodeC() throws Throwable {
109         CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "hashCode", C.HASHCODE_DESC, C.class, "x;y", C.ACCESSORS);
110         MethodHandle handle = cs.dynamicInvoker();
111         C c = new C(6, 7);
112         int hc = (int)handle.invokeExact(c);
113         assertEquals(hashCombiner(c.x(), c.y()), hc);
114 
115         assertEquals(hashCombiner(100, 1), (int)handle.invokeExact(new C(100, 1)));
116         assertEquals(hashCombiner(0, 0), (int)handle.invokeExact(new C(0, 0)));
117         assertEquals(hashCombiner(-1, 100), (int)handle.invokeExact(new C(-1, 100)));
118         assertEquals(hashCombiner(100, 1), (int)handle.invokeExact(new C(100, 1)));
119         assertEquals(hashCombiner(100, -1), (int)handle.invokeExact(new C(100, -1)));
120     }
121 
122     @Test
123     public void testHashCodeEmpty() throws Throwable {
124         CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "hashCode", Empty.HASHCODE_DESC, Empty.class, "", Empty.ACCESSORS);
125         MethodHandle handle = cs.dynamicInvoker();
126         Empty e = new Empty();
127         assertEquals(0, (int)handle.invokeExact(e));
128     }
129 
130     @Test
131     public void testToStringC() throws Throwable {
132         CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "toString", C.TO_STRING_DESC, C.class, C.NAME_LIST, C.ACCESSORS);
133         MethodHandle handle = cs.dynamicInvoker();
134         assertEquals("C[x=8, y=9]", (String)handle.invokeExact(new C(8, 9))   );
135         assertEquals("C[x=10, y=11]", (String)handle.invokeExact(new C(10, 11)) );
136         assertEquals("C[x=100, y=-9]", (String)handle.invokeExact(new C(100, -9)));
137         assertEquals("C[x=0, y=0]", (String)handle.invokeExact(new C(0, 0))   );
138     }
139 
140     @Test
141     public void testToStringEmpty() throws Throwable {
142         CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "toString", Empty.TO_STRING_DESC, Empty.class, Empty.NAME_LIST, Empty.ACCESSORS);
143         MethodHandle handle = cs.dynamicInvoker();
144         assertEquals("Empty[]", (String)handle.invokeExact(new Empty()));
145     }
146 
147     Class<NullPointerException> NPE = NullPointerException.class;
148     Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
149 
150     @Test
151     public void exceptions()  {
152         assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "badName",  C.EQUALS_DESC,    C.class,         C.NAME_LIST, C.ACCESSORS));
153         assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "toString", C.TO_STRING_DESC, C.class,         "x;y;z",     C.ACCESSORS));
154         assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "toString", C.TO_STRING_DESC, C.class,         "x;y",       new MethodHandle[]{}));
155         assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "toString", C.TO_STRING_DESC, this.getClass(), "x;y",       C.ACCESSORS));
156 
157         assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "toString", C.EQUALS_DESC,    C.class, "x;y", C.ACCESSORS));
158         assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "hashCode", C.TO_STRING_DESC, C.class, "x;y", C.ACCESSORS));
159         assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "equals",   C.HASHCODE_DESC,  C.class, "x;y", C.ACCESSORS));
160 
161         record NamePlusType(String mn, MethodType mt) {}
162         List<NamePlusType> namePlusTypeList = List.of(
163                 new NamePlusType("toString", C.TO_STRING_DESC),
164                 new NamePlusType("equals", C.EQUALS_DESC),
165                 new NamePlusType("hashCode", C.HASHCODE_DESC)
166         );
167 
168         for (NamePlusType npt : namePlusTypeList) {
169             assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), C.class, "x;y", null));
170             assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), C.class, "x;y", new MethodHandle[]{null}));
171             assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), C.class, null,  C.ACCESSORS));
172             assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), null,    "x;y", C.ACCESSORS));
173             assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), null,     C.class, "x;y", C.ACCESSORS));
174             assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, null,     npt.mt(), C.class, "x;y", C.ACCESSORS));
175             assertThrows(NPE, () -> ObjectMethods.bootstrap(null, npt.mn(),     npt.mt(), C.class, "x;y", C.ACCESSORS));
176         }
177     }
178 
179     // Based on the ObjectMethods internal implementation
180     private static int hashCombiner(int x, int y) {
181         return x*31 + y;
182     }
183 }