| /* |
| * Copyright (c) 2009, 2011, 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. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * 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. |
| */ |
| |
| package java.lang.invoke; |
| |
| import sun.invoke.util.*; |
| import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; |
| |
| /** |
| * Adapters which manage inexact MethodHandle.invoke calls. |
| * The JVM calls one of these when the exact type match fails. |
| * @author jrose |
| */ |
| class InvokeGeneric { |
| // erased type for the call, which originates from an inexact invoke site |
| private final MethodType erasedCallerType; |
| // an invoker of type (MT, MH; A...) -> R |
| private final MethodHandle initialInvoker; |
| |
| /** Compute and cache information for this adapter, so that it can |
| * call out to targets of the erasure-family of the given erased type. |
| */ |
| /*non-public*/ InvokeGeneric(MethodType erasedCallerType) throws ReflectiveOperationException { |
| assert(erasedCallerType.equals(erasedCallerType.erase())); |
| this.erasedCallerType = erasedCallerType; |
| this.initialInvoker = makeInitialInvoker(); |
| assert initialInvoker.type().equals(erasedCallerType |
| .insertParameterTypes(0, MethodType.class, MethodHandle.class)) |
| : initialInvoker.type(); |
| } |
| |
| private static MethodHandles.Lookup lookup() { |
| return IMPL_LOOKUP; |
| } |
| |
| /** Return the adapter information for this type's erasure. */ |
| /*non-public*/ static MethodHandle generalInvokerOf(MethodType erasedCallerType) throws ReflectiveOperationException { |
| InvokeGeneric gen = new InvokeGeneric(erasedCallerType); |
| return gen.initialInvoker; |
| } |
| |
| private MethodHandle makeInitialInvoker() throws ReflectiveOperationException { |
| // postDispatch = #(MH'; MT, MH; A...){MH'(MT, MH; A)} |
| MethodHandle postDispatch = makePostDispatchInvoker(); |
| MethodHandle invoker; |
| if (returnConversionPossible()) { |
| invoker = MethodHandles.foldArguments(postDispatch, |
| dispatcher("dispatchWithConversion")); |
| } else { |
| invoker = MethodHandles.foldArguments(postDispatch, dispatcher("dispatch")); |
| } |
| return invoker; |
| } |
| |
| private static final Class<?>[] EXTRA_ARGS = { MethodType.class, MethodHandle.class }; |
| private MethodHandle makePostDispatchInvoker() { |
| // Take (MH'; MT, MH; A...) and run MH'(MT, MH; A...). |
| MethodType invokerType = erasedCallerType.insertParameterTypes(0, EXTRA_ARGS); |
| return invokerType.invokers().exactInvoker(); |
| } |
| private MethodHandle dropDispatchArguments(MethodHandle targetInvoker) { |
| assert(targetInvoker.type().parameterType(0) == MethodHandle.class); |
| return MethodHandles.dropArguments(targetInvoker, 1, EXTRA_ARGS); |
| } |
| |
| private MethodHandle dispatcher(String dispatchName) throws ReflectiveOperationException { |
| return lookup().bind(this, dispatchName, |
| MethodType.methodType(MethodHandle.class, |
| MethodType.class, MethodHandle.class)); |
| } |
| |
| static final boolean USE_AS_TYPE_PATH = true; |
| |
| /** Return a method handle to invoke on the callerType, target, and remaining arguments. |
| * The method handle must finish the call. |
| * This is the first look at the caller type and target. |
| */ |
| private MethodHandle dispatch(MethodType callerType, MethodHandle target) { |
| MethodType targetType = target.type(); |
| if (USE_AS_TYPE_PATH || target.isVarargsCollector()) { |
| MethodHandle newTarget = target.asType(callerType); |
| targetType = callerType; |
| Invokers invokers = targetType.invokers(); |
| MethodHandle invoker = invokers.erasedInvokerWithDrops; |
| if (invoker == null) { |
| invokers.erasedInvokerWithDrops = invoker = |
| dropDispatchArguments(invokers.erasedInvoker()); |
| } |
| return invoker.bindTo(newTarget); |
| } |
| throw new RuntimeException("NYI"); |
| } |
| |
| private MethodHandle dispatchWithConversion(MethodType callerType, MethodHandle target) { |
| MethodHandle finisher = dispatch(callerType, target); |
| if (returnConversionNeeded(callerType, target)) |
| finisher = addReturnConversion(finisher, callerType.returnType()); //FIXME: slow |
| return finisher; |
| } |
| |
| private boolean returnConversionPossible() { |
| Class<?> needType = erasedCallerType.returnType(); |
| return !needType.isPrimitive(); |
| } |
| private boolean returnConversionNeeded(MethodType callerType, MethodHandle target) { |
| Class<?> needType = callerType.returnType(); |
| if (needType == erasedCallerType.returnType()) |
| return false; // no conversions possible, since must be primitive or Object |
| Class<?> haveType = target.type().returnType(); |
| if (VerifyType.isNullConversion(haveType, needType) && !needType.isInterface()) |
| return false; |
| return true; |
| } |
| private MethodHandle addReturnConversion(MethodHandle finisher, Class<?> type) { |
| // FIXME: This is slow because it creates a closure node on every call that requires a return cast. |
| MethodType finisherType = finisher.type(); |
| MethodHandle caster = ValueConversions.identity(type); |
| caster = caster.asType(caster.type().changeParameterType(0, finisherType.returnType())); |
| finisher = MethodHandles.filterReturnValue(finisher, caster); |
| return finisher.asType(finisherType); |
| } |
| |
| public String toString() { |
| return "InvokeGeneric"+erasedCallerType; |
| } |
| } |