diff options
Diffstat (limited to 'src/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java')
-rw-r--r-- | src/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/src/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java b/src/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java new file mode 100644 index 00000000..d52063bf --- /dev/null +++ b/src/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java @@ -0,0 +1,132 @@ +/* + * 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 java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.Arrays; +import jdk.internal.dynalink.CallSiteDescriptor; +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.Guards; + +/** + * Links {@link BoundCallable} objects. Passes through to linker services for linking a callable (for either + * "dyn:call" or "dyn:new"), and modifies the returned invocation to deal with the receiver and argument binding. + */ +final class BoundCallableLinker implements TypeBasedGuardingDynamicLinker { + @Override + public boolean canLinkType(final Class<?> type) { + return type == BoundCallable.class; + } + + @Override + public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception { + final Object objBoundCallable = linkRequest.getReceiver(); + if(!(objBoundCallable instanceof BoundCallable)) { + return null; + } + + final CallSiteDescriptor descriptor = linkRequest.getCallSiteDescriptor(); + if (descriptor.getNameTokenCount() < 2 || !"dyn".equals(descriptor.getNameToken(CallSiteDescriptor.SCHEME))) { + return null; + } + final String operation = descriptor.getNameToken(CallSiteDescriptor.OPERATOR); + // We need to distinguish "dyn:new" from "dyn:call" because "dyn:call" sites have parameter list of the form + // "callee, this, args", while "dyn:call" sites have "callee, args" -- they lack the "this" parameter. + final boolean isCall; + if ("new".equals(operation)) { + isCall = false; + } else if ("call".equals(operation)) { + isCall = true; + } else { + // Only dyn:call and dyn:new are supported. + return null; + } + final BoundCallable boundCallable = (BoundCallable)objBoundCallable; + final Object callable = boundCallable.getCallable(); + final Object boundThis = boundCallable.getBoundThis(); + + // We need to ask the linker services for a delegate invocation on the target callable. + + // Replace arguments (boundCallable[, this], args) => (callable[, boundThis], boundArgs, args) when delegating + final Object[] args = linkRequest.getArguments(); + final Object[] boundArgs = boundCallable.getBoundArgs(); + final int argsLen = args.length; + final int boundArgsLen = boundArgs.length; + final Object[] newArgs = new Object[argsLen + boundArgsLen]; + newArgs[0] = callable; + final int firstArgIndex; + if (isCall) { + newArgs[1] = boundThis; + firstArgIndex = 2; + } else { + firstArgIndex = 1; + } + System.arraycopy(boundArgs, 0, newArgs, firstArgIndex, boundArgsLen); + System.arraycopy(args, firstArgIndex, newArgs, firstArgIndex + boundArgsLen, argsLen - firstArgIndex); + + // Use R(T0, T1, T2, ...) => R(callable.class, boundThis.class, boundArg0.class, ..., boundArgn.class, T2, ...) + // call site type when delegating to underlying linker (for dyn:new, there's no this). + final MethodType type = descriptor.getMethodType(); + // Use R(T0, ...) => R(callable.class, ...) + MethodType newMethodType = descriptor.getMethodType().changeParameterType(0, callable.getClass()); + if (isCall) { + // R(callable.class, T1, ...) => R(callable.class, boundThis.class, ...) + newMethodType = newMethodType.changeParameterType(1, boundThis.getClass()); + } + // R(callable.class[, boundThis.class], T2, ...) => R(callable.class[, boundThis.class], boundArg0.class, ..., boundArgn.class, T2, ...) + for(int i = boundArgs.length; i-- > 0;) { + newMethodType = newMethodType.insertParameterTypes(firstArgIndex, boundArgs[i] == null ? Object.class : boundArgs[i].getClass()); + } + final CallSiteDescriptor newDescriptor = descriptor.changeMethodType(newMethodType); + + // Delegate to target's linker + final GuardedInvocation inv = linkerServices.getGuardedInvocation(linkRequest.replaceArguments(newDescriptor, newArgs)); + if(inv == null) { + return null; + } + + // Bind (callable[, boundThis], boundArgs) to the delegate handle + final MethodHandle boundHandle = MethodHandles.insertArguments(inv.getInvocation(), 0, + Arrays.copyOf(newArgs, firstArgIndex + boundArgs.length)); + final Class<?> p0Type = type.parameterType(0); + final MethodHandle droppingHandle; + if (isCall) { + // Ignore incoming boundCallable and this + droppingHandle = MethodHandles.dropArguments(boundHandle, 0, p0Type, type.parameterType(1)); + } else { + // Ignore incoming boundCallable + droppingHandle = MethodHandles.dropArguments(boundHandle, 0, p0Type); + } + // Identity guard on boundCallable object + final MethodHandle newGuard = Guards.getIdentityGuard(boundCallable); + return inv.replaceMethods(droppingHandle, newGuard.asType(newGuard.type().changeParameterType(0, p0Type))); + } +} |