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 
26 package jdk.nashorn.internal.runtime.linker;
27 
28 import static jdk.dynalink.StandardNamespace.METHOD;
29 import static jdk.dynalink.StandardOperation.GET;
30 import static jdk.nashorn.internal.runtime.linker.JavaAdapterBytecodeGenerator.SUPER_PREFIX;
31 
32 import java.lang.invoke.MethodHandle;
33 import java.lang.invoke.MethodHandles;
34 import java.lang.invoke.MethodType;
35 import jdk.dynalink.CallSiteDescriptor;
36 import jdk.dynalink.Operation;
37 import jdk.dynalink.beans.BeansLinker;
38 import jdk.dynalink.linker.GuardedInvocation;
39 import jdk.dynalink.linker.LinkRequest;
40 import jdk.dynalink.linker.LinkerServices;
41 import jdk.dynalink.linker.TypeBasedGuardingDynamicLinker;
42 import jdk.dynalink.linker.support.Lookup;
43 import jdk.nashorn.internal.runtime.ScriptRuntime;
44 
45 /**
46  * A linker for instances of {@code JavaSuperAdapter}. Only links {@code getMethod} calls, by forwarding them to the
47  * bean linker for the adapter class and prepending {@code super$} to method names.
48  *
49  */
50 final class JavaSuperAdapterLinker implements TypeBasedGuardingDynamicLinker {
51     private static final MethodHandle ADD_PREFIX_TO_METHOD_NAME;
52     private static final MethodHandle BIND_DYNAMIC_METHOD;
53     private static final MethodHandle GET_ADAPTER;
54     private static final MethodHandle IS_ADAPTER_OF_CLASS;
55 
56     static {
57         final Lookup lookup = new Lookup(MethodHandles.lookup());
58         ADD_PREFIX_TO_METHOD_NAME = lookup.findOwnStatic("addPrefixToMethodName", Object.class, Object.class);
59         BIND_DYNAMIC_METHOD = lookup.findOwnStatic("bindDynamicMethod", Object.class, Object.class, Object.class);
60         GET_ADAPTER = lookup.findVirtual(JavaSuperAdapter.class, "getAdapter", MethodType.methodType(Object.class));
61         IS_ADAPTER_OF_CLASS = lookup.findOwnStatic("isAdapterOfClass", boolean.class, Class.class, Object.class);
62     }
63 
64     private static final Operation GET_METHOD = GET.withNamespace(METHOD);
65 
66     private final BeansLinker beansLinker;
67 
JavaSuperAdapterLinker(final BeansLinker beansLinker)68     JavaSuperAdapterLinker(final BeansLinker beansLinker) {
69         this.beansLinker = beansLinker;
70     }
71 
72     @Override
canLinkType(final Class<?> type)73     public boolean canLinkType(final Class<?> type) {
74         return type == JavaSuperAdapter.class;
75     }
76 
77     @Override
getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices)78     public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices)
79             throws Exception {
80         final Object objSuperAdapter = linkRequest.getReceiver();
81         if(!(objSuperAdapter instanceof JavaSuperAdapter)) {
82             return null;
83         }
84 
85         final CallSiteDescriptor descriptor = linkRequest.getCallSiteDescriptor();
86 
87         if(!NashornCallSiteDescriptor.contains(descriptor, GET, METHOD)) {
88             // We only handle GET:METHOD
89             return null;
90         }
91 
92         final Object adapter = ((JavaSuperAdapter)objSuperAdapter).getAdapter();
93 
94         // Replace argument (javaSuperAdapter, ...) => (adapter, ...) when delegating to BeansLinker
95         final Object[] args = linkRequest.getArguments();
96         args[0] = adapter;
97 
98         // Use R(T0, ...) => R(adapter.class, ...) call site type when delegating to BeansLinker.
99         final MethodType type = descriptor.getMethodType();
100         final Class<?> adapterClass = adapter.getClass();
101         final String name = NashornCallSiteDescriptor.getOperand(descriptor);
102         final Operation newOp = name == null ? GET_METHOD : GET_METHOD.named(SUPER_PREFIX + name);
103 
104         final CallSiteDescriptor newDescriptor = new CallSiteDescriptor(
105                 NashornCallSiteDescriptor.getLookupInternal(descriptor), newOp,
106                 type.changeParameterType(0, adapterClass));
107 
108         // Delegate to BeansLinker
109         final GuardedInvocation guardedInv = NashornBeansLinker.getGuardedInvocation(
110                 beansLinker, linkRequest.replaceArguments(newDescriptor, args),
111                 linkerServices);
112         // Even for non-existent methods, Bootstrap's BeansLinker will link a
113         // noSuchMember handler.
114         assert guardedInv != null;
115 
116         final MethodHandle guard = IS_ADAPTER_OF_CLASS.bindTo(adapterClass);
117 
118         final MethodHandle invocation = guardedInv.getInvocation();
119         final MethodType invType = invocation.type();
120         // For invocation typed R(T0, ...) create a dynamic method binder of type Object(R, T0)
121         final MethodHandle typedBinder = BIND_DYNAMIC_METHOD.asType(MethodType.methodType(Object.class,
122                 invType.returnType(), invType.parameterType(0)));
123         // For invocation typed R(T0, T1, ...) create a dynamic method binder of type Object(R, T0, T1, ...)
124         final MethodHandle droppingBinder = MethodHandles.dropArguments(typedBinder, 2,
125                 invType.parameterList().subList(1, invType.parameterCount()));
126         // Finally, fold the invocation into the binder to produce a method handle that will bind every returned
127         // DynamicMethod object from StandardOperation.GET_METHOD calls to the actual receiver
128         // Object(R(T0, T1, ...), T0, T1, ...)
129         final MethodHandle bindingInvocation = MethodHandles.foldArguments(droppingBinder, invocation);
130 
131         final MethodHandle typedGetAdapter = asFilterType(GET_ADAPTER, 0, invType, type);
132         final MethodHandle adaptedInvocation;
133         if(name != null) {
134             adaptedInvocation = MethodHandles.filterArguments(bindingInvocation, 0, typedGetAdapter);
135         } else {
136             // Add a filter that'll prepend "super$" to each name passed to the variable-name StandardOperation.GET_METHOD.
137             final MethodHandle typedAddPrefix = asFilterType(ADD_PREFIX_TO_METHOD_NAME, 1, invType, type);
138             adaptedInvocation = MethodHandles.filterArguments(bindingInvocation, 0, typedGetAdapter, typedAddPrefix);
139         }
140 
141         return guardedInv.replaceMethods(adaptedInvocation, guard).asType(descriptor);
142     }
143 
144     /**
145      * Adapts the type of a method handle used as a filter in a position from a source method type to a target method type.
146      * @param filter the filter method handle
147      * @param pos the position in the argument list that it's filtering
148      * @param targetType the target method type for filtering
149      * @param sourceType the source method type for filtering
150      * @return a type adapted filter
151      */
asFilterType(final MethodHandle filter, final int pos, final MethodType targetType, final MethodType sourceType)152     private static MethodHandle asFilterType(final MethodHandle filter, final int pos, final MethodType targetType, final MethodType sourceType) {
153         return filter.asType(MethodType.methodType(targetType.parameterType(pos), sourceType.parameterType(pos)));
154     }
155 
156     @SuppressWarnings("unused")
addPrefixToMethodName(final Object name)157     private static Object addPrefixToMethodName(final Object name) {
158         return SUPER_PREFIX.concat(String.valueOf(name));
159     }
160 
161     /**
162      * Used to transform the return value of getMethod; transform a {@code DynamicMethod} into a
163      * {@code BoundDynamicMethod} while also accounting for the possibility of a non-existent method.
164      * @param dynamicMethod the dynamic method to bind
165      * @param boundThis the adapter underlying a super adapter, to which the dynamic method is bound.
166      * @return a dynamic method bound to the adapter instance.
167      */
168     @SuppressWarnings("unused")
bindDynamicMethod(final Object dynamicMethod, final Object boundThis)169     private static Object bindDynamicMethod(final Object dynamicMethod, final Object boundThis) {
170         return dynamicMethod == ScriptRuntime.UNDEFINED ? ScriptRuntime.UNDEFINED : Bootstrap.bindCallable(dynamicMethod, boundThis, null);
171     }
172 
173     /**
174      * Used as the guard of linkages, as the receiver is not guaranteed to be a JavaSuperAdapter.
175      * @param clazz the class the receiver's adapter is tested against.
176      * @param obj receiver
177      * @return true if the receiver is a super adapter, and its underlying adapter is of the specified class
178      */
179     @SuppressWarnings("unused")
isAdapterOfClass(final Class<?> clazz, final Object obj)180     private static boolean isAdapterOfClass(final Class<?> clazz, final Object obj) {
181         return obj instanceof JavaSuperAdapter && clazz == (((JavaSuperAdapter)obj).getAdapter()).getClass();
182     }
183 }
184