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 }