| /* |
| * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* @test |
| * @summary unit tests for java.lang.invoke.MethodHandles |
| * @library /lib/testlibrary /java/lang/invoke/common |
| * @compile MethodHandlesTest.java MethodHandlesInvokersTest.java remote/RemoteExample.java |
| * @run junit/othervm/timeout=2500 -XX:+IgnoreUnrecognizedVMOptions |
| * -XX:-VerifyDependencies |
| * -esa |
| * test.java.lang.invoke.MethodHandlesInvokersTest |
| */ |
| |
| package test.java.lang.invoke; |
| |
| import org.junit.*; |
| import test.java.lang.invoke.lib.CodeCacheOverflowProcessor; |
| |
| import java.lang.invoke.CallSite; |
| import java.lang.invoke.MethodHandle; |
| import java.lang.invoke.MethodHandles; |
| import java.lang.invoke.MethodType; |
| import java.lang.invoke.MutableCallSite; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import static org.junit.Assert.*; |
| |
| public class MethodHandlesInvokersTest extends MethodHandlesTest { |
| |
| @Test // SLOW |
| public void testInvokers() throws Throwable { |
| CodeCacheOverflowProcessor.runMHTest(this::testInvokers0); |
| } |
| |
| public void testInvokers0() throws Throwable { |
| if (CAN_SKIP_WORKING) return; |
| startTest("exactInvoker, genericInvoker, varargsInvoker, dynamicInvoker"); |
| // exactInvoker, genericInvoker, varargsInvoker[0..N], dynamicInvoker |
| Set<MethodType> done = new HashSet<>(); |
| for (int i = 0; i <= 6; i++) { |
| if (CAN_TEST_LIGHTLY && i > 3) break; |
| MethodType gtype = MethodType.genericMethodType(i); |
| for (Class<?> argType : new Class<?>[]{Object.class, Integer.class, int.class}) { |
| for (int j = -1; j < i; j++) { |
| MethodType type = gtype; |
| if (j < 0) |
| type = type.changeReturnType(argType); |
| else if (argType == void.class) |
| continue; |
| else |
| type = type.changeParameterType(j, argType); |
| if (done.add(type)) |
| testInvokersWithCatch(type); |
| MethodType vtype = type.changeReturnType(void.class); |
| if (done.add(vtype)) |
| testInvokersWithCatch(vtype); |
| } |
| } |
| } |
| } |
| |
| public void testInvokersWithCatch(MethodType type) throws Throwable { |
| try { |
| testInvokers(type); |
| } catch (Throwable ex) { |
| System.out.println("*** testInvokers on "+type+" => "); |
| ex.printStackTrace(System.out); |
| } |
| } |
| |
| public void testInvokers(MethodType type) throws Throwable { |
| if (verbosity >= 3) |
| System.out.println("test invokers for "+type); |
| int nargs = type.parameterCount(); |
| boolean testRetCode = type.returnType() != void.class; |
| MethodHandle target = PRIVATE.findStatic(MethodHandlesTest.class, "invokee", |
| MethodType.genericMethodType(0, true)); |
| assertTrue(target.isVarargsCollector()); |
| target = target.asType(type); |
| Object[] args = randomArgs(type.parameterArray()); |
| List<Object> targetPlusArgs = new ArrayList<>(Arrays.asList(args)); |
| targetPlusArgs.add(0, target); |
| int code = (Integer) invokee(args); |
| Object log = logEntry("invokee", args); |
| assertEquals(log.hashCode(), code); |
| assertCalled("invokee", args); |
| MethodHandle inv; |
| Object result; |
| // exact invoker |
| countTest(); |
| calledLog.clear(); |
| inv = MethodHandles.exactInvoker(type); |
| result = inv.invokeWithArguments(targetPlusArgs); |
| if (testRetCode) assertEquals(code, result); |
| assertCalled("invokee", args); |
| // generic invoker |
| countTest(); |
| inv = MethodHandles.invoker(type); |
| if (nargs <= 3 && type == type.generic()) { |
| calledLog.clear(); |
| switch (nargs) { |
| case 0: |
| result = inv.invokeExact(target); |
| break; |
| case 1: |
| result = inv.invokeExact(target, args[0]); |
| break; |
| case 2: |
| result = inv.invokeExact(target, args[0], args[1]); |
| break; |
| case 3: |
| result = inv.invokeExact(target, args[0], args[1], args[2]); |
| break; |
| } |
| if (testRetCode) assertEquals(code, result); |
| assertCalled("invokee", args); |
| } |
| calledLog.clear(); |
| result = inv.invokeWithArguments(targetPlusArgs); |
| if (testRetCode) assertEquals(code, result); |
| assertCalled("invokee", args); |
| // varargs invoker #0 |
| calledLog.clear(); |
| inv = MethodHandles.spreadInvoker(type, 0); |
| if (type.returnType() == Object.class) { |
| result = inv.invokeExact(target, args); |
| } else if (type.returnType() == void.class) { |
| result = null; inv.invokeExact(target, args); |
| } else { |
| result = inv.invokeWithArguments(target, (Object) args); |
| } |
| if (testRetCode) assertEquals(code, result); |
| assertCalled("invokee", args); |
| if (nargs >= 1 && type == type.generic()) { |
| // varargs invoker #1 |
| calledLog.clear(); |
| inv = MethodHandles.spreadInvoker(type, 1); |
| result = inv.invokeExact(target, args[0], Arrays.copyOfRange(args, 1, nargs)); |
| if (testRetCode) assertEquals(code, result); |
| assertCalled("invokee", args); |
| } |
| if (nargs >= 2 && type == type.generic()) { |
| // varargs invoker #2 |
| calledLog.clear(); |
| inv = MethodHandles.spreadInvoker(type, 2); |
| result = inv.invokeExact(target, args[0], args[1], Arrays.copyOfRange(args, 2, nargs)); |
| if (testRetCode) assertEquals(code, result); |
| assertCalled("invokee", args); |
| } |
| if (nargs >= 3 && type == type.generic()) { |
| // varargs invoker #3 |
| calledLog.clear(); |
| inv = MethodHandles.spreadInvoker(type, 3); |
| result = inv.invokeExact(target, args[0], args[1], args[2], Arrays.copyOfRange(args, 3, nargs)); |
| if (testRetCode) assertEquals(code, result); |
| assertCalled("invokee", args); |
| } |
| for (int k = 0; k <= nargs; k++) { |
| // varargs invoker #0..N |
| if (CAN_TEST_LIGHTLY && (k > 1 || k < nargs - 1)) continue; |
| countTest(); |
| calledLog.clear(); |
| inv = MethodHandles.spreadInvoker(type, k); |
| MethodType expType = (type.dropParameterTypes(k, nargs) |
| .appendParameterTypes(Object[].class) |
| .insertParameterTypes(0, MethodHandle.class)); |
| assertEquals(expType, inv.type()); |
| List<Object> targetPlusVarArgs = new ArrayList<>(targetPlusArgs); |
| List<Object> tailList = targetPlusVarArgs.subList(1+k, 1+nargs); |
| Object[] tail = tailList.toArray(); |
| tailList.clear(); tailList.add(tail); |
| result = inv.invokeWithArguments(targetPlusVarArgs); |
| if (testRetCode) assertEquals(code, result); |
| assertCalled("invokee", args); |
| } |
| |
| // dynamic invoker |
| countTest(); |
| CallSite site = new MutableCallSite(type); |
| inv = site.dynamicInvoker(); |
| |
| // see if we get the result of the original target: |
| try { |
| result = inv.invokeWithArguments(args); |
| assertTrue("should not reach here", false); |
| } catch (IllegalStateException ex) { |
| String msg = ex.getMessage(); |
| assertTrue(msg, msg.contains("site")); |
| } |
| |
| // set new target after invoker is created, to make sure we track target |
| site.setTarget(target); |
| calledLog.clear(); |
| result = inv.invokeWithArguments(args); |
| if (testRetCode) assertEquals(code, result); |
| assertCalled("invokee", args); |
| } |
| } |