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         record NamePlusType(String mn, MethodType mt) {}
155         List<NamePlusType> namePlusTypeList = List.of(
156                 new NamePlusType("toString", C.TO_STRING_DESC),
157                 new NamePlusType("equals", C.EQUALS_DESC),
158                 new NamePlusType("hashCode", C.HASHCODE_DESC)
159         );
160 
161         for (NamePlusType npt : namePlusTypeList) {
162             assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), C.class, "x;y", null));
163             assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), C.class, "x;y", new MethodHandle[]{null}));
164             assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), C.class, null,  C.ACCESSORS));
165             assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), npt.mt(), null,    "x;y", C.ACCESSORS));
166             assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, npt.mn(), null,     C.class, "x;y", C.ACCESSORS));
167             assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, null,     npt.mt(), C.class, "x;y", C.ACCESSORS));
168             assertThrows(NPE, () -> ObjectMethods.bootstrap(null, npt.mn(),     npt.mt(), C.class, "x;y", C.ACCESSORS));
169         }
170     }
171 
172     // Based on the ObjectMethods internal implementation
173     private static int hashCombiner(int x, int y) {
174         return x*31 + y;
175     }
176 }