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 }