JavaSuperAdapterLinker.java revision 953:221a84ef44c0
1/* 2 * Copyright (c) 2010, 2013, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.nashorn.internal.runtime.linker; 27 28import static jdk.nashorn.internal.lookup.Lookup.EMPTY_GETTER; 29import static jdk.nashorn.internal.runtime.linker.JavaAdapterBytecodeGenerator.SUPER_PREFIX; 30 31import java.lang.invoke.MethodHandle; 32import java.lang.invoke.MethodHandles; 33import java.lang.invoke.MethodType; 34import jdk.internal.dynalink.CallSiteDescriptor; 35import jdk.internal.dynalink.beans.BeansLinker; 36import jdk.internal.dynalink.linker.GuardedInvocation; 37import jdk.internal.dynalink.linker.LinkRequest; 38import jdk.internal.dynalink.linker.LinkerServices; 39import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker; 40import jdk.internal.dynalink.support.CallSiteDescriptorFactory; 41import jdk.internal.dynalink.support.Lookup; 42import jdk.nashorn.internal.runtime.ScriptRuntime; 43 44/** 45 * A linker for instances of {@code JavaSuperAdapter}. Only links {@code getMethod} calls, by forwarding them to the 46 * bean linker for the adapter class and prepending {@code super$} to method names. 47 * 48 */ 49final class JavaSuperAdapterLinker implements TypeBasedGuardingDynamicLinker { 50 private static final String GET_METHOD = "getMethod"; 51 private static final String DYN_GET_METHOD = "dyn:" + GET_METHOD; 52 private static final String DYN_GET_METHOD_FIXED = DYN_GET_METHOD + ":" + SUPER_PREFIX; 53 54 private static final MethodHandle ADD_PREFIX_TO_METHOD_NAME; 55 private static final MethodHandle BIND_DYNAMIC_METHOD; 56 private static final MethodHandle GET_ADAPTER; 57 private static final MethodHandle IS_ADAPTER_OF_CLASS; 58 59 static { 60 final Lookup lookup = new Lookup(MethodHandles.lookup()); 61 ADD_PREFIX_TO_METHOD_NAME = lookup.findOwnStatic("addPrefixToMethodName", Object.class, Object.class); 62 BIND_DYNAMIC_METHOD = lookup.findOwnStatic("bindDynamicMethod", Object.class, Object.class, Object.class); 63 GET_ADAPTER = lookup.findVirtual(JavaSuperAdapter.class, "getAdapter", MethodType.methodType(Object.class)); 64 IS_ADAPTER_OF_CLASS = lookup.findOwnStatic("isAdapterOfClass", boolean.class, Class.class, Object.class); 65 } 66 67 @Override 68 public boolean canLinkType(final Class<?> type) { 69 return type == JavaSuperAdapter.class; 70 } 71 72 @Override 73 public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) 74 throws Exception { 75 final Object objSuperAdapter = linkRequest.getReceiver(); 76 if(!(objSuperAdapter instanceof JavaSuperAdapter)) { 77 return null; 78 } 79 80 final CallSiteDescriptor descriptor = linkRequest.getCallSiteDescriptor(); 81 if(!CallSiteDescriptorFactory.tokenizeOperators(descriptor).contains(GET_METHOD)) { 82 // We only handle getMethod 83 return null; 84 } 85 86 final Object adapter = ((JavaSuperAdapter)objSuperAdapter).getAdapter(); 87 88 // Replace argument (javaSuperAdapter, ...) => (adapter, ...) when delegating to BeansLinker 89 final Object[] args = linkRequest.getArguments(); 90 args[0] = adapter; 91 92 // Use R(T0, ...) => R(adapter.class, ...) call site type when delegating to BeansLinker. 93 final MethodType type = descriptor.getMethodType(); 94 final Class<?> adapterClass = adapter.getClass(); 95 final boolean hasFixedName = descriptor.getNameTokenCount() > 2; 96 final String opName = hasFixedName ? (DYN_GET_METHOD_FIXED + descriptor.getNameToken( 97 CallSiteDescriptor.NAME_OPERAND)) : DYN_GET_METHOD; 98 99 final CallSiteDescriptor newDescriptor = NashornCallSiteDescriptor.get(descriptor.getLookup(), opName, 100 type.changeParameterType(0, adapterClass), 0); 101 102 // Delegate to BeansLinker 103 final GuardedInvocation guardedInv = NashornBeansLinker.getGuardedInvocation( 104 BeansLinker.getLinkerForClass(adapterClass), linkRequest.replaceArguments(newDescriptor, args), 105 linkerServices); 106 107 final MethodHandle guard = IS_ADAPTER_OF_CLASS.bindTo(adapterClass); 108 if(guardedInv == null) { 109 // Short circuit the lookup here for non-existent methods by linking an empty getter. If we just returned 110 // null instead, BeansLinker would find final methods on the JavaSuperAdapter instead: getClass() and 111 // wait(). 112 return new GuardedInvocation(MethodHandles.dropArguments(EMPTY_GETTER, 1,type.parameterList().subList(1, 113 type.parameterCount())), guard).asType(descriptor); 114 } 115 116 final MethodHandle invocation = guardedInv.getInvocation(); 117 final MethodType invType = invocation.type(); 118 // For invocation typed R(T0, ...) create a dynamic method binder of type Object(R, T0) 119 final MethodHandle typedBinder = BIND_DYNAMIC_METHOD.asType(MethodType.methodType(Object.class, 120 invType.returnType(), invType.parameterType(0))); 121 // For invocation typed R(T0, T1, ...) create a dynamic method binder of type Object(R, T0, T1, ...) 122 final MethodHandle droppingBinder = MethodHandles.dropArguments(typedBinder, 2, 123 invType.parameterList().subList(1, invType.parameterCount())); 124 // Finally, fold the invocation into the binder to produce a method handle that will bind every returned 125 // DynamicMethod object from dyn:getMethod calls to the actual receiver 126 // Object(R(T0, T1, ...), T0, T1, ...) 127 final MethodHandle bindingInvocation = MethodHandles.foldArguments(droppingBinder, invocation); 128 129 final MethodHandle typedGetAdapter = asFilterType(GET_ADAPTER, 0, invType, type); 130 final MethodHandle adaptedInvocation; 131 if(hasFixedName) { 132 adaptedInvocation = MethodHandles.filterArguments(bindingInvocation, 0, typedGetAdapter); 133 } else { 134 // Add a filter that'll prepend "super$" to each name passed to the variable-name "dyn:getMethod". 135 final MethodHandle typedAddPrefix = asFilterType(ADD_PREFIX_TO_METHOD_NAME, 1, invType, type); 136 adaptedInvocation = MethodHandles.filterArguments(bindingInvocation, 0, typedGetAdapter, typedAddPrefix); 137 } 138 139 return guardedInv.replaceMethods(adaptedInvocation, guard).asType(descriptor); 140 } 141 142 /** 143 * Adapts the type of a method handle used as a filter in a position from a source method type to a target method type. 144 * @param filter the filter method handle 145 * @param pos the position in the argument list that it's filtering 146 * @param targetType the target method type for filtering 147 * @param sourceType the source method type for filtering 148 * @return a type adapted filter 149 */ 150 private static MethodHandle asFilterType(final MethodHandle filter, final int pos, final MethodType targetType, final MethodType sourceType) { 151 return filter.asType(MethodType.methodType(targetType.parameterType(pos), sourceType.parameterType(pos))); 152 } 153 154 @SuppressWarnings("unused") 155 private static Object addPrefixToMethodName(final Object name) { 156 return SUPER_PREFIX.concat(String.valueOf(name)); 157 } 158 159 /** 160 * Used to transform the return value of getMethod; transform a {@code DynamicMethod} into a 161 * {@code BoundDynamicMethod} while also accounting for the possibility of a non-existent method. 162 * @param dynamicMethod the dynamic method to bind 163 * @param boundThis the adapter underlying a super adapter, to which the dynamic method is bound. 164 * @return a dynamic method bound to the adapter instance. 165 */ 166 @SuppressWarnings("unused") 167 private static Object bindDynamicMethod(final Object dynamicMethod, final Object boundThis) { 168 return dynamicMethod == null ? ScriptRuntime.UNDEFINED : Bootstrap.bindDynamicMethod(dynamicMethod, boundThis); 169 } 170 171 /** 172 * Used as the guard of linkages, as the receiver is not guaranteed to be a JavaSuperAdapter. 173 * @param clazz the class the receiver's adapter is tested against. 174 * @param obj receiver 175 * @return true if the receiver is a super adapter, and its underlying adapter is of the specified class 176 */ 177 @SuppressWarnings("unused") 178 private static boolean isAdapterOfClass(final Class<?> clazz, final Object obj) { 179 return obj instanceof JavaSuperAdapter && clazz == (((JavaSuperAdapter)obj).getAdapter()).getClass(); 180 } 181} 182