1 /* 2 * Copyright (c) 2019, 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 24 /* 25 * @test 26 * @bug 8246774 27 * @summary Basic tests for ObjectMethods 28 * @run testng 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 org.testng.annotations.Test; 38 import static java.lang.invoke.MethodType.methodType; 39 import static org.testng.Assert.assertEquals; 40 import static org.testng.Assert.assertThrows; 41 import static org.testng.Assert.assertFalse; 42 import static org.testng.Assert.assertTrue; 43 44 @Test 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 public void testEqualsC() throws Throwable { 84 CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "equals", C.EQUALS_DESC, C.class, C.NAME_LIST, C.ACCESSORS); 85 MethodHandle handle = cs.dynamicInvoker(); 86 C c = new C(5, 5); 87 assertTrue((boolean)handle.invokeExact(c, (Object)c)); 88 assertTrue((boolean)handle.invokeExact(c, (Object)new C(5, 5))); 89 assertFalse((boolean)handle.invokeExact(c, (Object)new C(5, 4))); 90 assertFalse((boolean)handle.invokeExact(c, (Object)new C(4, 5))); 91 assertFalse((boolean)handle.invokeExact(c, (Object)null)); 92 assertFalse((boolean)handle.invokeExact(c, new Object())); 93 } 94 95 public void testEqualsEmpty() throws Throwable { 96 CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "equals", Empty.EQUALS_DESC, Empty.class, Empty.NAME_LIST, Empty.ACCESSORS); 97 MethodHandle handle = cs.dynamicInvoker(); 98 Empty e = new Empty(); 99 assertTrue((boolean)handle.invokeExact(e, (Object)e)); 100 assertTrue((boolean)handle.invokeExact(e, (Object)new Empty())); 101 assertFalse((boolean)handle.invokeExact(e, (Object)null)); 102 assertFalse((boolean)handle.invokeExact(e, new Object())); 103 } 104 105 public void testHashCodeC() throws Throwable { 106 CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "hashCode", C.HASHCODE_DESC, C.class, "x;y", C.ACCESSORS); 107 MethodHandle handle = cs.dynamicInvoker(); 108 C c = new C(6, 7); 109 int hc = (int)handle.invokeExact(c); 110 assertEquals(hc, hashCombiner(c.x(), c.y())); 111 112 assertEquals((int)handle.invokeExact(new C(100, 1)), hashCombiner(100, 1)); 113 assertEquals((int)handle.invokeExact(new C(0, 0)), hashCombiner(0, 0)); 114 assertEquals((int)handle.invokeExact(new C(-1, 100)), hashCombiner(-1, 100)); 115 assertEquals((int)handle.invokeExact(new C(100, 1)), hashCombiner(100, 1)); 116 assertEquals((int)handle.invokeExact(new C(100, -1)), hashCombiner(100, -1)); 117 } 118 119 public void testHashCodeEmpty() throws Throwable { 120 CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "hashCode", Empty.HASHCODE_DESC, Empty.class, "", Empty.ACCESSORS); 121 MethodHandle handle = cs.dynamicInvoker(); 122 Empty e = new Empty(); 123 assertEquals((int)handle.invokeExact(e), 0); 124 } 125 126 public void testToStringC() throws Throwable { 127 CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "toString", C.TO_STRING_DESC, C.class, C.NAME_LIST, C.ACCESSORS); 128 MethodHandle handle = cs.dynamicInvoker(); 129 assertEquals((String)handle.invokeExact(new C(8, 9)), "C[x=8, y=9]" ); 130 assertEquals((String)handle.invokeExact(new C(10, 11)), "C[x=10, y=11]" ); 131 assertEquals((String)handle.invokeExact(new C(100, -9)), "C[x=100, y=-9]"); 132 assertEquals((String)handle.invokeExact(new C(0, 0)), "C[x=0, y=0]" ); 133 } 134 135 public void testToStringEmpty() throws Throwable { 136 CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "toString", Empty.TO_STRING_DESC, Empty.class, Empty.NAME_LIST, Empty.ACCESSORS); 137 MethodHandle handle = cs.dynamicInvoker(); 138 assertEquals((String)handle.invokeExact(new Empty()), "Empty[]"); 139 } 140 141 Class<NullPointerException> NPE = NullPointerException.class; 142 Class<IllegalArgumentException> IAE = IllegalArgumentException.class; 143 144 public void exceptions() { 145 assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "badName", C.EQUALS_DESC, C.class, C.NAME_LIST, C.ACCESSORS)); 146 assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "toString", C.TO_STRING_DESC, C.class, "x;y;z", C.ACCESSORS)); 147 assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "toString", C.TO_STRING_DESC, C.class, "x;y", new MethodHandle[]{})); 148 assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "toString", C.TO_STRING_DESC, this.getClass(), "x;y", C.ACCESSORS)); 149 150 assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "toString", C.EQUALS_DESC, C.class, "x;y", C.ACCESSORS)); 151 assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "hashCode", C.TO_STRING_DESC, C.class, "x;y", C.ACCESSORS)); 152 assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "equals", C.HASHCODE_DESC, C.class, "x;y", C.ACCESSORS)); 153 154 assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "toString", methodType(String.class, this.getClass()), C.class, "x;y", C.ACCESSORS)); 155 assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "toString", C.TO_STRING_DESC, C.class, "x;y", 156 new MethodHandle[]{ 157 MethodHandles.lookup().findGetter(C.class, "x", int.class), 158 MethodHandles.lookup().findGetter(this.getClass(), "y", int.class), 159 })); 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 // same field name and type as C::y 180 private int y; 181 182 // Based on the ObjectMethods internal implementation 183 private static int hashCombiner(int x, int y) { 184 return x*31 + y; 185 } 186 }