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