JavaSuperAdapterLinker.java revision 1485:77d303d8a943
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.NamedOperation;
36import jdk.internal.dynalink.Operation;
37import jdk.internal.dynalink.StandardOperation;
38import jdk.internal.dynalink.beans.BeansLinker;
39import jdk.internal.dynalink.linker.GuardedInvocation;
40import jdk.internal.dynalink.linker.LinkRequest;
41import jdk.internal.dynalink.linker.LinkerServices;
42import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
43import jdk.internal.dynalink.linker.support.Lookup;
44import jdk.nashorn.internal.runtime.ScriptRuntime;
45
46/**
47 * A linker for instances of {@code JavaSuperAdapter}. Only links {@code getMethod} calls, by forwarding them to the
48 * bean linker for the adapter class and prepending {@code super$} to method names.
49 *
50 */
51final class JavaSuperAdapterLinker implements TypeBasedGuardingDynamicLinker {
52    private static final MethodHandle ADD_PREFIX_TO_METHOD_NAME;
53    private static final MethodHandle BIND_DYNAMIC_METHOD;
54    private static final MethodHandle GET_ADAPTER;
55    private static final MethodHandle IS_ADAPTER_OF_CLASS;
56
57    static {
58        final Lookup lookup = new Lookup(MethodHandles.lookup());
59        ADD_PREFIX_TO_METHOD_NAME = lookup.findOwnStatic("addPrefixToMethodName", Object.class, Object.class);
60        BIND_DYNAMIC_METHOD = lookup.findOwnStatic("bindDynamicMethod", Object.class, Object.class, Object.class);
61        GET_ADAPTER = lookup.findVirtual(JavaSuperAdapter.class, "getAdapter", MethodType.methodType(Object.class));
62        IS_ADAPTER_OF_CLASS = lookup.findOwnStatic("isAdapterOfClass", boolean.class, Class.class, Object.class);
63    }
64
65    @Override
66    public boolean canLinkType(final Class<?> type) {
67        return type == JavaSuperAdapter.class;
68    }
69
70    @Override
71    public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices)
72            throws Exception {
73        final Object objSuperAdapter = linkRequest.getReceiver();
74        if(!(objSuperAdapter instanceof JavaSuperAdapter)) {
75            return null;
76        }
77
78        final CallSiteDescriptor descriptor = linkRequest.getCallSiteDescriptor();
79
80        if(!NashornCallSiteDescriptor.contains(descriptor, StandardOperation.GET_METHOD)) {
81            // We only handle GET_METHOD
82            return null;
83        }
84
85        final Object adapter = ((JavaSuperAdapter)objSuperAdapter).getAdapter();
86
87        // Replace argument (javaSuperAdapter, ...) => (adapter, ...) when delegating to BeansLinker
88        final Object[] args = linkRequest.getArguments();
89        args[0] = adapter;
90
91        // Use R(T0, ...) => R(adapter.class, ...) call site type when delegating to BeansLinker.
92        final MethodType type = descriptor.getMethodType();
93        final Class<?> adapterClass = adapter.getClass();
94        final String name = NashornCallSiteDescriptor.getOperand(descriptor);
95        final Operation newOp = name == null ? StandardOperation.GET_METHOD :
96            new NamedOperation(StandardOperation.GET_METHOD, SUPER_PREFIX + name);
97
98        final CallSiteDescriptor newDescriptor = new CallSiteDescriptor(
99                NashornCallSiteDescriptor.getLookupInternal(descriptor), newOp,
100                type.changeParameterType(0, adapterClass));
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 StandardOperation.GET_METHOD 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(name != null) {
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 StandardOperation.GET_METHOD.
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.bindCallable(dynamicMethod, boundThis, null);
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