blob: 9aeefd72e370508dec1958a85a7a7ea9f9671cd6 [file] [log] [blame]
/*
* Copyright (c) 2010, 2013, 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 jdk.nashorn.internal.runtime.linker;
import static jdk.nashorn.internal.lookup.Lookup.EMPTY_GETTER;
import static jdk.nashorn.internal.runtime.linker.JavaAdapterBytecodeGenerator.SUPER_PREFIX;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.beans.BeansLinker;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
import jdk.internal.dynalink.support.Lookup;
import jdk.nashorn.internal.runtime.ScriptRuntime;
/**
* A linker for instances of {@code JavaSuperAdapter}. Only links {@code getMethod} calls, by forwarding them to the
* bean linker for the adapter class and prepending {@code super$} to method names.
*
*/
final class JavaSuperAdapterLinker implements TypeBasedGuardingDynamicLinker {
private static final String GET_METHOD = "getMethod";
private static final String DYN_GET_METHOD = "dyn:" + GET_METHOD;
private static final String DYN_GET_METHOD_FIXED = DYN_GET_METHOD + ":" + SUPER_PREFIX;
private static final MethodHandle ADD_PREFIX_TO_METHOD_NAME;
private static final MethodHandle BIND_DYNAMIC_METHOD;
private static final MethodHandle GET_ADAPTER;
private static final MethodHandle IS_ADAPTER_OF_CLASS;
static {
final Lookup lookup = new Lookup(MethodHandles.lookup());
ADD_PREFIX_TO_METHOD_NAME = lookup.findOwnStatic("addPrefixToMethodName", Object.class, Object.class);
BIND_DYNAMIC_METHOD = lookup.findOwnStatic("bindDynamicMethod", Object.class, Object.class, Object.class);
GET_ADAPTER = lookup.findVirtual(JavaSuperAdapter.class, "getAdapter", MethodType.methodType(Object.class));
IS_ADAPTER_OF_CLASS = lookup.findOwnStatic("isAdapterOfClass", boolean.class, Class.class, Object.class);
}
@Override
public boolean canLinkType(final Class<?> type) {
return type == JavaSuperAdapter.class;
}
@Override
public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices)
throws Exception {
final Object objSuperAdapter = linkRequest.getReceiver();
if(!(objSuperAdapter instanceof JavaSuperAdapter)) {
return null;
}
final CallSiteDescriptor descriptor = linkRequest.getCallSiteDescriptor();
if(!CallSiteDescriptorFactory.tokenizeOperators(descriptor).contains(GET_METHOD)) {
// We only handle getMethod
return null;
}
final Object adapter = ((JavaSuperAdapter)objSuperAdapter).getAdapter();
// Replace argument (javaSuperAdapter, ...) => (adapter, ...) when delegating to BeansLinker
final Object[] args = linkRequest.getArguments();
args[0] = adapter;
// Use R(T0, ...) => R(adapter.class, ...) call site type when delegating to BeansLinker.
final MethodType type = descriptor.getMethodType();
final Class<?> adapterClass = adapter.getClass();
final boolean hasFixedName = descriptor.getNameTokenCount() > 2;
final String opName = hasFixedName ? (DYN_GET_METHOD_FIXED + descriptor.getNameToken(
CallSiteDescriptor.NAME_OPERAND)) : DYN_GET_METHOD;
final CallSiteDescriptor newDescriptor = NashornCallSiteDescriptor.get(descriptor.getLookup(), opName,
type.changeParameterType(0, adapterClass), 0);
// Delegate to BeansLinker
final GuardedInvocation guardedInv = NashornBeansLinker.getGuardedInvocation(
BeansLinker.getLinkerForClass(adapterClass), linkRequest.replaceArguments(newDescriptor, args),
linkerServices);
final MethodHandle guard = IS_ADAPTER_OF_CLASS.bindTo(adapterClass);
if(guardedInv == null) {
// Short circuit the lookup here for non-existent methods by linking an empty getter. If we just returned
// null instead, BeansLinker would find final methods on the JavaSuperAdapter instead: getClass() and
// wait().
return new GuardedInvocation(MethodHandles.dropArguments(EMPTY_GETTER, 1,type.parameterList().subList(1,
type.parameterCount())), guard).asType(descriptor);
}
final MethodHandle invocation = guardedInv.getInvocation();
final MethodType invType = invocation.type();
// For invocation typed R(T0, ...) create a dynamic method binder of type Object(R, T0)
final MethodHandle typedBinder = BIND_DYNAMIC_METHOD.asType(MethodType.methodType(Object.class,
invType.returnType(), invType.parameterType(0)));
// For invocation typed R(T0, T1, ...) create a dynamic method binder of type Object(R, T0, T1, ...)
final MethodHandle droppingBinder = MethodHandles.dropArguments(typedBinder, 2,
invType.parameterList().subList(1, invType.parameterCount()));
// Finally, fold the invocation into the binder to produce a method handle that will bind every returned
// DynamicMethod object from dyn:getMethod calls to the actual receiver
// Object(R(T0, T1, ...), T0, T1, ...)
final MethodHandle bindingInvocation = MethodHandles.foldArguments(droppingBinder, invocation);
final MethodHandle typedGetAdapter = asFilterType(GET_ADAPTER, 0, invType, type);
final MethodHandle adaptedInvocation;
if(hasFixedName) {
adaptedInvocation = MethodHandles.filterArguments(bindingInvocation, 0, typedGetAdapter);
} else {
// Add a filter that'll prepend "super$" to each name passed to the variable-name "dyn:getMethod".
final MethodHandle typedAddPrefix = asFilterType(ADD_PREFIX_TO_METHOD_NAME, 1, invType, type);
adaptedInvocation = MethodHandles.filterArguments(bindingInvocation, 0, typedGetAdapter, typedAddPrefix);
}
return guardedInv.replaceMethods(adaptedInvocation, guard).asType(descriptor);
}
/**
* Adapts the type of a method handle used as a filter in a position from a source method type to a target method type.
* @param filter the filter method handle
* @param pos the position in the argument list that it's filtering
* @param targetType the target method type for filtering
* @param sourceType the source method type for filtering
* @return a type adapted filter
*/
private static MethodHandle asFilterType(final MethodHandle filter, final int pos, final MethodType targetType, final MethodType sourceType) {
return filter.asType(MethodType.methodType(targetType.parameterType(pos), sourceType.parameterType(pos)));
}
@SuppressWarnings("unused")
private static Object addPrefixToMethodName(final Object name) {
return SUPER_PREFIX.concat(String.valueOf(name));
}
/**
* Used to transform the return value of getMethod; transform a {@code DynamicMethod} into a
* {@code BoundDynamicMethod} while also accounting for the possibility of a non-existent method.
* @param dynamicMethod the dynamic method to bind
* @param boundThis the adapter underlying a super adapter, to which the dynamic method is bound.
* @return a dynamic method bound to the adapter instance.
*/
@SuppressWarnings("unused")
private static Object bindDynamicMethod(final Object dynamicMethod, final Object boundThis) {
return dynamicMethod == null ? ScriptRuntime.UNDEFINED : Bootstrap.bindCallable(dynamicMethod, boundThis, null);
}
/**
* Used as the guard of linkages, as the receiver is not guaranteed to be a JavaSuperAdapter.
* @param clazz the class the receiver's adapter is tested against.
* @param obj receiver
* @return true if the receiver is a super adapter, and its underlying adapter is of the specified class
*/
@SuppressWarnings("unused")
private static boolean isAdapterOfClass(final Class<?> clazz, final Object obj) {
return obj instanceof JavaSuperAdapter && clazz == (((JavaSuperAdapter)obj).getAdapter()).getClass();
}
}