1 /*
2  * Copyright (c) 2018, 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 /* @test
25  * @summary unit tests for java.lang.invoke.MethodHandles
26  * @library /test/lib /java/lang/invoke/common
27  * @compile MethodHandlesTest.java MethodHandlesInvokersTest.java remote/RemoteExample.java
28  * @run junit/othervm/timeout=2500 -XX:+IgnoreUnrecognizedVMOptions
29  *                                 -XX:-VerifyDependencies
30  *                                 -esa
31  *                                 test.java.lang.invoke.MethodHandlesInvokersTest
32  */
33 
34 package test.java.lang.invoke;
35 
36 import org.junit.*;
37 import test.java.lang.invoke.lib.CodeCacheOverflowProcessor;
38 
39 import java.lang.invoke.CallSite;
40 import java.lang.invoke.MethodHandle;
41 import java.lang.invoke.MethodHandles;
42 import java.lang.invoke.MethodType;
43 import java.lang.invoke.MutableCallSite;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.HashSet;
47 import java.util.List;
48 import java.util.Set;
49 
50 import static org.junit.Assert.*;
51 
52 public class MethodHandlesInvokersTest extends MethodHandlesTest {
53 
54     @Test  // SLOW
testInvokers()55     public void testInvokers() throws Throwable {
56         CodeCacheOverflowProcessor.runMHTest(this::testInvokers0);
57     }
58 
testInvokers0()59     public void testInvokers0() throws Throwable {
60         if (CAN_SKIP_WORKING)  return;
61         startTest("exactInvoker, genericInvoker, varargsInvoker, dynamicInvoker");
62         // exactInvoker, genericInvoker, varargsInvoker[0..N], dynamicInvoker
63         Set<MethodType> done = new HashSet<>();
64         for (int i = 0; i <= 6; i++) {
65             if (CAN_TEST_LIGHTLY && i > 3)  break;
66             MethodType gtype = MethodType.genericMethodType(i);
67             for (Class<?> argType : new Class<?>[]{Object.class, Integer.class, int.class}) {
68                 for (int j = -1; j < i; j++) {
69                     MethodType type = gtype;
70                     if (j < 0)
71                         type = type.changeReturnType(argType);
72                     else if (argType == void.class)
73                         continue;
74                     else
75                         type = type.changeParameterType(j, argType);
76                     if (done.add(type))
77                         testInvokersWithCatch(type);
78                     MethodType vtype = type.changeReturnType(void.class);
79                     if (done.add(vtype))
80                         testInvokersWithCatch(vtype);
81                 }
82             }
83         }
84     }
85 
testInvokersWithCatch(MethodType type)86     public void testInvokersWithCatch(MethodType type) throws Throwable {
87         try {
88             testInvokers(type);
89         } catch (Throwable ex) {
90             System.out.println("*** testInvokers on "+type+" => ");
91             ex.printStackTrace(System.out);
92         }
93     }
94 
testInvokers(MethodType type)95     public void testInvokers(MethodType type) throws Throwable {
96         if (verbosity >= 3)
97             System.out.println("test invokers for "+type);
98         int nargs = type.parameterCount();
99         boolean testRetCode = type.returnType() != void.class;
100         MethodHandle target = PRIVATE.findStatic(MethodHandlesTest.class, "invokee",
101                                                  MethodType.genericMethodType(0, true));
102         assertTrue(target.isVarargsCollector());
103         target = target.asType(type);
104         Object[] args = randomArgs(type.parameterArray());
105         List<Object> targetPlusArgs = new ArrayList<>(Arrays.asList(args));
106         targetPlusArgs.add(0, target);
107         int code = (Integer) invokee(args);
108         Object log = logEntry("invokee", args);
109         assertEquals(log.hashCode(), code);
110         assertCalled("invokee", args);
111         MethodHandle inv;
112         Object result;
113         // exact invoker
114         countTest();
115         calledLog.clear();
116         inv = MethodHandles.exactInvoker(type);
117         result = inv.invokeWithArguments(targetPlusArgs);
118         if (testRetCode)  assertEquals(code, result);
119         assertCalled("invokee", args);
120         // generic invoker
121         countTest();
122         inv = MethodHandles.invoker(type);
123         if (nargs <= 3 && type == type.generic()) {
124             calledLog.clear();
125             switch (nargs) {
126             case 0:
127                 result = inv.invokeExact(target);
128                 break;
129             case 1:
130                 result = inv.invokeExact(target, args[0]);
131                 break;
132             case 2:
133                 result = inv.invokeExact(target, args[0], args[1]);
134                 break;
135             case 3:
136                 result = inv.invokeExact(target, args[0], args[1], args[2]);
137                 break;
138             }
139             if (testRetCode)  assertEquals(code, result);
140             assertCalled("invokee", args);
141         }
142         calledLog.clear();
143         result = inv.invokeWithArguments(targetPlusArgs);
144         if (testRetCode)  assertEquals(code, result);
145         assertCalled("invokee", args);
146         // varargs invoker #0
147         calledLog.clear();
148         inv = MethodHandles.spreadInvoker(type, 0);
149         if (type.returnType() == Object.class) {
150             result = inv.invokeExact(target, args);
151         } else if (type.returnType() == void.class) {
152             result = null; inv.invokeExact(target, args);
153         } else {
154             result = inv.invokeWithArguments(target, (Object) args);
155         }
156         if (testRetCode)  assertEquals(code, result);
157         assertCalled("invokee", args);
158         if (nargs >= 1 && type == type.generic()) {
159             // varargs invoker #1
160             calledLog.clear();
161             inv = MethodHandles.spreadInvoker(type, 1);
162             result = inv.invokeExact(target, args[0], Arrays.copyOfRange(args, 1, nargs));
163             if (testRetCode)  assertEquals(code, result);
164             assertCalled("invokee", args);
165         }
166         if (nargs >= 2 && type == type.generic()) {
167             // varargs invoker #2
168             calledLog.clear();
169             inv = MethodHandles.spreadInvoker(type, 2);
170             result = inv.invokeExact(target, args[0], args[1], Arrays.copyOfRange(args, 2, nargs));
171             if (testRetCode)  assertEquals(code, result);
172             assertCalled("invokee", args);
173         }
174         if (nargs >= 3 && type == type.generic()) {
175             // varargs invoker #3
176             calledLog.clear();
177             inv = MethodHandles.spreadInvoker(type, 3);
178             result = inv.invokeExact(target, args[0], args[1], args[2], Arrays.copyOfRange(args, 3, nargs));
179             if (testRetCode)  assertEquals(code, result);
180             assertCalled("invokee", args);
181         }
182         for (int k = 0; k <= nargs; k++) {
183             // varargs invoker #0..N
184             if (CAN_TEST_LIGHTLY && (k > 1 || k < nargs - 1))  continue;
185             countTest();
186             calledLog.clear();
187             inv = MethodHandles.spreadInvoker(type, k);
188             MethodType expType = (type.dropParameterTypes(k, nargs)
189                                  .appendParameterTypes(Object[].class)
190                                  .insertParameterTypes(0, MethodHandle.class));
191             assertEquals(expType, inv.type());
192             List<Object> targetPlusVarArgs = new ArrayList<>(targetPlusArgs);
193             List<Object> tailList = targetPlusVarArgs.subList(1+k, 1+nargs);
194             Object[] tail = tailList.toArray();
195             tailList.clear(); tailList.add(tail);
196             result = inv.invokeWithArguments(targetPlusVarArgs);
197             if (testRetCode)  assertEquals(code, result);
198             assertCalled("invokee", args);
199         }
200 
201         // dynamic invoker
202         countTest();
203         CallSite site = new MutableCallSite(type);
204         inv = site.dynamicInvoker();
205 
206         // see if we get the result of the original target:
207         try {
208             result = inv.invokeWithArguments(args);
209             assertTrue("should not reach here", false);
210         } catch (IllegalStateException ex) {
211             String msg = ex.getMessage();
212             assertTrue(msg, msg.contains("site"));
213         }
214 
215         // set new target after invoker is created, to make sure we track target
216         site.setTarget(target);
217         calledLog.clear();
218         result = inv.invokeWithArguments(args);
219         if (testRetCode)  assertEquals(code, result);
220         assertCalled("invokee", args);
221     }
222 }
223