aboutsummaryrefslogtreecommitdiff
path: root/src/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java')
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java132
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)));
+ }
+}