changeset 1114:ad912b034639

8051778: support bind on all Nashorn callables Reviewed-by: hannesw, lagergren
author attila
date Thu, 27 Nov 2014 13:04:46 +0100
parents a723569d0559
children 64962ecb8b85
files src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFunction.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/ScriptFunctionImpl.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/IteratorAction.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BoundCallable.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BoundDynamicMethod.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java test/script/basic/JDK-8051778.js test/script/basic/JDK-8051778.js.EXPECTED
diffstat 12 files changed, 379 insertions(+), 184 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFunction.java	Fri Nov 21 17:44:57 2014 +0100
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFunction.java	Thu Nov 27 13:04:46 2014 +0100
@@ -48,6 +48,7 @@
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
+import jdk.nashorn.internal.runtime.linker.Bootstrap;
 
 /**
  * ECMA 15.3 Function Objects
@@ -204,11 +205,7 @@
      * @return function with bound arguments
      */
     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
-    public static ScriptFunction bind(final Object self, final Object... args) {
-        if (!(self instanceof ScriptFunction)) {
-            throw typeError("not.a.function", ScriptRuntime.safeToString(self));
-        }
-
+    public static Object bind(final Object self, final Object... args) {
         final Object thiz = (args.length == 0) ? UNDEFINED : args[0];
 
         Object[] arguments;
@@ -219,7 +216,7 @@
             arguments = ScriptRuntime.EMPTY_ARRAY;
         }
 
-        return ((ScriptFunctionImpl)self).makeBoundFunction(thiz, arguments);
+        return Bootstrap.bindCallable(self, thiz, arguments);
     }
 
     /**
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java	Fri Nov 21 17:44:57 2014 +0100
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java	Thu Nov 27 13:04:46 2014 +0100
@@ -28,6 +28,7 @@
 import static jdk.nashorn.internal.lookup.Lookup.MH;
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
+
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
@@ -804,7 +805,7 @@
             // name and object linked with BeansLinker. (Actually, an even stronger assumption is true: return value is
             // constant for any given method name and object's class.)
             return MethodHandles.dropArguments(MethodHandles.constant(Object.class,
-                    Bootstrap.bindDynamicMethod(methodGetter.invoke(source), source)), 0, Object.class);
+                    Bootstrap.bindCallable(methodGetter.invoke(source), source, null)), 0, Object.class);
         } catch(RuntimeException|Error e) {
             throw e;
         } catch(final Throwable t) {
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Fri Nov 21 17:44:57 2014 +0100
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Thu Nov 27 13:04:46 2014 +0100
@@ -30,7 +30,6 @@
 
 import java.lang.invoke.MethodHandle;
 import java.util.ArrayList;
-
 import jdk.nashorn.internal.runtime.AccessorProperty;
 import jdk.nashorn.internal.runtime.GlobalFunctions;
 import jdk.nashorn.internal.runtime.Property;
@@ -237,13 +236,13 @@
 
     /**
      * Same as {@link ScriptFunction#makeBoundFunction(Object, Object[])}. The only reason we override it is so that we
-     * can expose it to methods in this package.
+     * can expose it.
      * @param self the self to bind to this function. Can be null (in which case, null is bound as this).
      * @param args additional arguments to bind to this function. Can be null or empty to not bind additional arguments.
      * @return a function with the specified self and parameters bound.
      */
     @Override
-    protected ScriptFunction makeBoundFunction(final Object self, final Object[] args) {
+    public ScriptFunction makeBoundFunction(final Object self, final Object[] args) {
         return super.makeBoundFunction(self, args);
     }
 
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/IteratorAction.java	Fri Nov 21 17:44:57 2014 +0100
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/IteratorAction.java	Thu Nov 27 13:04:46 2014 +0100
@@ -25,11 +25,7 @@
 
 package jdk.nashorn.internal.runtime.arrays;
 
-import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
-
-import jdk.nashorn.api.scripting.JSObject;
 import jdk.nashorn.internal.runtime.Context;
-import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
 import jdk.nashorn.internal.runtime.linker.Bootstrap;
 
@@ -98,17 +94,7 @@
      * @return result of apply
      */
     public final T apply() {
-        final boolean strict;
-        if (callbackfn instanceof ScriptFunction) {
-            strict = ((ScriptFunction)callbackfn).isStrict();
-        } else if (callbackfn instanceof JSObject &&
-            ((JSObject)callbackfn).isFunction()) {
-            strict = ((JSObject)callbackfn).isStrictFunction();
-        } else if (Bootstrap.isDynamicMethod(callbackfn) || Bootstrap.isFunctionalInterfaceObject(callbackfn)) {
-            strict = false;
-        } else {
-            throw typeError("not.a.function", ScriptRuntime.safeToString(callbackfn));
-        }
+        final boolean strict = Bootstrap.isStrictCallable(callbackfn);
 
         // for non-strict callback, need to translate undefined thisArg to be global object
         thisArg = (thisArg == ScriptRuntime.UNDEFINED && !strict)? Context.getGlobal() : thisArg;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Fri Nov 21 17:44:57 2014 +0100
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Thu Nov 27 13:04:46 2014 +0100
@@ -26,6 +26,7 @@
 package jdk.nashorn.internal.runtime.linker;
 
 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
+import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 
 import java.lang.invoke.CallSite;
 import java.lang.invoke.ConstantCallSite;
@@ -50,6 +51,8 @@
 import jdk.nashorn.internal.codegen.RuntimeCallSite;
 import jdk.nashorn.internal.lookup.MethodHandleFactory;
 import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
+import jdk.nashorn.internal.objects.ScriptFunctionImpl;
+import jdk.nashorn.internal.runtime.ECMAException;
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.OptimisticReturnFilters;
 import jdk.nashorn.internal.runtime.ScriptFunction;
@@ -94,7 +97,7 @@
             new NashornLinker(),
             new NashornPrimitiveLinker(),
             new NashornStaticClassLinker(),
-            new BoundDynamicMethodLinker(),
+            new BoundCallableLinker(),
             new JavaSuperAdapterLinker(),
             new JSObjectLinker(nashornBeansLinker),
             new BrowserJSObjectLinker(nashornBeansLinker),
@@ -136,19 +139,47 @@
         }
 
         return obj instanceof ScriptFunction ||
-            ((obj instanceof JSObject) && ((JSObject)obj).isFunction()) ||
-            isDynamicMethod(obj) ||
+            isJSObjectFunction(obj) ||
+            BeansLinker.isDynamicMethod(obj) ||
+            obj instanceof BoundCallable ||
             isFunctionalInterfaceObject(obj) ||
             obj instanceof StaticClass;
     }
 
     /**
+     * Returns true if the given object is a strict callable
+     * @param callable the callable object to be checked for strictness
+     * @return true if the obj is a strict callable, false if it is a non-strict callable.
+     * @throws ECMAException with {@code TypeError} if the object is not a callable.
+     */
+    public static boolean isStrictCallable(final Object callable) {
+        if (callable instanceof ScriptFunction) {
+            return ((ScriptFunction)callable).isStrict();
+        } else if (isJSObjectFunction(callable)) {
+            return ((JSObject)callable).isStrictFunction();
+        } else if (callable instanceof BoundCallable) {
+            return isStrictCallable(((BoundCallable)callable).getCallable());
+        } else if (BeansLinker.isDynamicMethod(callable) || callable instanceof StaticClass) {
+            return false;
+        }
+        throw notFunction(callable);
+    }
+
+    private static ECMAException notFunction(final Object obj) {
+        return typeError("not.a.function", ScriptRuntime.safeToString(obj));
+    }
+
+    private static boolean isJSObjectFunction(final Object obj) {
+        return obj instanceof JSObject && ((JSObject)obj).isFunction();
+    }
+
+    /**
      * Returns if the given object is a dynalink Dynamic method
      * @param obj object to be checked
      * @return true if the obj is a dynamic method
      */
     public static boolean isDynamicMethod(final Object obj) {
-        return obj instanceof BoundDynamicMethod || BeansLinker.isDynamicMethod(obj);
+        return BeansLinker.isDynamicMethod(obj instanceof BoundCallable ? ((BoundCallable)obj).getCallable() : obj);
     }
 
     /**
@@ -370,14 +401,22 @@
     }
 
     /**
-     * Binds a bean dynamic method (returned by invoking {@code dyn:getMethod} on an object linked with
-     * {@code BeansLinker} to a receiver.
-     * @param dynamicMethod the dynamic method to bind
+     * Binds any object Nashorn can use as a [[Callable]] to a receiver and optionally arguments.
+     * @param callable the callable to bind
      * @param boundThis the bound "this" value.
-     * @return a bound dynamic method.
+     * @param boundArgs the bound arguments. Can be either null or empty array to signify no arguments are bound.
+     * @return a bound callable.
+     * @throws ECMAException with {@code TypeError} if the object is not a callable.
      */
-    public static Object bindDynamicMethod(final Object dynamicMethod, final Object boundThis) {
-        return new BoundDynamicMethod(dynamicMethod, boundThis);
+    public static Object bindCallable(final Object callable, final Object boundThis, final Object[] boundArgs) {
+        if (callable instanceof ScriptFunctionImpl) {
+            return ((ScriptFunctionImpl)callable).makeBoundFunction(boundThis, boundArgs);
+        } else if (callable instanceof BoundCallable) {
+            return ((BoundCallable)callable).bind(boundArgs);
+        } else if (isCallable(callable)) {
+            return new BoundCallable(callable, boundThis, boundArgs);
+        }
+        throw notFunction(callable);
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BoundCallable.java	Thu Nov 27 13:04:46 2014 +0100
@@ -0,0 +1,96 @@
+/*
+ * 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.util.Arrays;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
+
+/**
+ * Represents a Nashorn callable bound to a receiver and optionally arguments. Note that objects of this class
+ * are just the tuples of a callable and a bound this and arguments, without any behavior. All the behavior is
+ * defined in the {@code BoundCallableLinker}.
+ */
+public final class BoundCallable {
+    private final Object callable;
+    private final Object boundThis;
+    private final Object[] boundArgs;
+
+    BoundCallable(final Object callable, final Object boundThis, final Object[] boundArgs) {
+        this.callable = callable;
+        this.boundThis = boundThis;
+        this.boundArgs = isEmptyArray(boundArgs) ? ScriptRuntime.EMPTY_ARRAY : boundArgs.clone();
+    }
+
+    private BoundCallable(final BoundCallable original, final Object[] extraBoundArgs) {
+        this.callable = original.callable;
+        this.boundThis = original.boundThis;
+        this.boundArgs = original.concatenateBoundArgs(extraBoundArgs);
+    }
+
+    Object getCallable() {
+        return callable;
+    }
+
+    Object getBoundThis() {
+        return boundThis;
+    }
+
+    Object[] getBoundArgs() {
+        return boundArgs;
+    }
+
+    BoundCallable bind(final Object[] extraBoundArgs) {
+        if (isEmptyArray(extraBoundArgs)) {
+            return this;
+        }
+        return new BoundCallable(this, extraBoundArgs);
+    }
+
+    private Object[] concatenateBoundArgs(final Object[] extraBoundArgs) {
+        if (boundArgs.length == 0) {
+            return extraBoundArgs.clone();
+        }
+        final int origBoundArgsLen = boundArgs.length;
+        final int extraBoundArgsLen = extraBoundArgs.length;
+        final Object[] newBoundArgs = new Object[origBoundArgsLen + extraBoundArgsLen];
+        System.arraycopy(boundArgs, 0, newBoundArgs, 0, origBoundArgsLen);
+        System.arraycopy(extraBoundArgs, 0, newBoundArgs, origBoundArgsLen, extraBoundArgsLen);
+        return newBoundArgs;
+    }
+
+    private static boolean isEmptyArray(final Object[] a) {
+        return a == null || a.length == 0;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder b = new StringBuilder(callable.toString()).append(" on ").append(boundThis);
+        if (boundArgs.length != 0) {
+            b.append(" with ").append(Arrays.toString(boundArgs));
+        }
+        return b.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java	Thu Nov 27 13:04:46 2014 +0100
@@ -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)));
+    }
+}
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BoundDynamicMethod.java	Fri Nov 21 17:44:57 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/*
- * 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.util.Objects;
-import jdk.internal.dynalink.beans.BeansLinker;
-
-/**
- * Represents a Dynalink dynamic method bound to a receiver. Note that objects of this class are just the tuples of
- * a method and a bound this, without any behavior. All the behavior is defined in the {@code BoundDynamicMethodLinker}.
- */
-final class BoundDynamicMethod {
-    private final Object dynamicMethod;
-    private final Object boundThis;
-
-    BoundDynamicMethod(final Object dynamicMethod, final Object boundThis) {
-        assert BeansLinker.isDynamicMethod(dynamicMethod);
-        this.dynamicMethod = dynamicMethod;
-        this.boundThis = boundThis;
-    }
-
-    Object getDynamicMethod() {
-        return dynamicMethod;
-    }
-
-    Object getBoundThis() {
-        return boundThis;
-    }
-
-    @Override
-    public String toString() {
-        return dynamicMethod.toString() + " on " + Objects.toString(boundThis);
-    }
-}
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java	Fri Nov 21 17:44:57 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-/*
- * 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 jdk.internal.dynalink.CallSiteDescriptor;
-import jdk.internal.dynalink.beans.BeansLinker;
-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 {@code BoundDynamicMethod} objects. Passes through to Dynalink's BeansLinker for linking a dynamic method
- * (they only respond to "dyn:call"), and modifies the returned invocation to deal with the receiver binding.
- */
-final class BoundDynamicMethodLinker implements TypeBasedGuardingDynamicLinker {
-    @Override
-    public boolean canLinkType(final Class<?> type) {
-        return type == BoundDynamicMethod.class;
-    }
-
-    @Override
-    public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
-        final Object objBoundDynamicMethod = linkRequest.getReceiver();
-        if(!(objBoundDynamicMethod instanceof BoundDynamicMethod)) {
-            return null;
-        }
-
-        final BoundDynamicMethod boundDynamicMethod = (BoundDynamicMethod)objBoundDynamicMethod;
-        final Object dynamicMethod = boundDynamicMethod.getDynamicMethod();
-        final Object boundThis = boundDynamicMethod.getBoundThis();
-
-        // Replace arguments (boundDynamicMethod, this, ...) => (dynamicMethod, boundThis, ...) when delegating to
-        // BeansLinker
-        final Object[] args = linkRequest.getArguments();
-        args[0] = dynamicMethod;
-        args[1] = boundThis;
-
-        // Use R(T0, T1, ...) => R(dynamicMethod.class, boundThis.class, ...) call site type when delegating to
-        // BeansLinker.
-        final CallSiteDescriptor descriptor = linkRequest.getCallSiteDescriptor();
-        final MethodType type = descriptor.getMethodType();
-        final Class<?> dynamicMethodClass = dynamicMethod.getClass();
-        final CallSiteDescriptor newDescriptor = descriptor.changeMethodType(
-                type.changeParameterType(0, dynamicMethodClass).changeParameterType(1, boundThis.getClass()));
-
-        // Delegate to BeansLinker
-        final GuardedInvocation inv = NashornBeansLinker.getGuardedInvocation(BeansLinker.getLinkerForClass(dynamicMethodClass),
-                linkRequest.replaceArguments(newDescriptor, args), linkerServices);
-        if(inv == null) {
-            return null;
-        }
-
-        // Bind (dynamicMethod, boundThis) to the handle
-        final MethodHandle boundHandle = MethodHandles.insertArguments(inv.getInvocation(), 0, dynamicMethod, boundThis);
-        final Class<?> p0Type = type.parameterType(0);
-        // Ignore incoming (boundDynamicMethod, this)
-        final MethodHandle droppingHandle = MethodHandles.dropArguments(boundHandle, 0, p0Type, type.parameterType(1));
-        // Identity guard on boundDynamicMethod object
-        final MethodHandle newGuard = Guards.getIdentityGuard(boundDynamicMethod);
-
-        return inv.replaceMethods(droppingHandle, newGuard.asType(newGuard.type().changeParameterType(0, p0Type)));
-    }
-}
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java	Fri Nov 21 17:44:57 2014 +0100
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java	Thu Nov 27 13:04:46 2014 +0100
@@ -165,7 +165,7 @@
      */
     @SuppressWarnings("unused")
     private static Object bindDynamicMethod(final Object dynamicMethod, final Object boundThis) {
-        return dynamicMethod == null ? ScriptRuntime.UNDEFINED : Bootstrap.bindDynamicMethod(dynamicMethod, boundThis);
+        return dynamicMethod == null ? ScriptRuntime.UNDEFINED : Bootstrap.bindCallable(dynamicMethod, boundThis, null);
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/JDK-8051778.js	Thu Nov 27 13:04:46 2014 +0100
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2014 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.
+ * 
+ * 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.
+ */
+
+/**
+ * JDK-8051778: support bind on all Nashorn callables
+ *
+ * @test
+ * @run
+ */
+
+var bind = Function.prototype.bind;
+
+// Bind a POJO method
+var l = new java.util.ArrayList();
+var l_add_foo = bind.call(l.add, l, "foo");
+l_add_foo();
+print("l=" + l);
+
+// Bind a BoundCallable
+var l_add = bind.call(l.add, l);
+var l_add_foo2 = bind.call(l_add, null, "foo2");
+l_add_foo2();
+print("l=" + l);
+
+// Bind a POJO method retrieved from one instance to a different but 
+// compatible instance.
+var l2 = new java.util.ArrayList();
+var l2_size = bind.call(l.size, l2);
+print("l2_size()=" + l2_size());
+
+// Bind a Java type object (used as a constructor).
+var construct_two = bind.call(java.lang.Integer, null, 2);
+print("Bound Integer(2) constructor: " + new construct_two())
+
+// Bind a @FunctionalInterface proxying to an object literal. NOTE: the 
+// expected value of this.a is always "original" and never "bound". This
+// might seem counterintuitive, but we are not binding the apply()
+// function of the object literal that defines the BiFunction behaviour,
+// we are binding the SAM proxy object instead, and it is always
+// forwarding to the apply() function with "this" set to the object
+// literal. Basically, binding "this" for SAM proxies is useless; only
+// binding arguments makes sense.
+var f1 = new java.util.function.BiFunction() {
+    apply: function(x, y) {
+        return "BiFunction with literal: " + this.a + ", " + x + ", " + y;
+    },
+    a: "unbound"
+};
+print((bind.call(f1, {a: "bound"}))(1, 2))
+print((bind.call(f1, {a: "bound"}, 3))(4))
+print((bind.call(f1, {a: "bound"}, 5, 6))())
+
+// Bind a @FunctionalInterface proxying to a function. With the same 
+// reasoning as above (binding the proxy vs. binding the JS function), 
+// the value of this.a will always be undefined, and never "bound".
+var f2 = new java.util.function.BiFunction(
+    function(x, y) {
+        return "BiFunction with function: " + this.a + ", " + x + ", " + y;
+    }
+);
+print((bind.call(f2, {a: "bound"}))(7, 8))
+print((bind.call(f2, {a: "bound"}, 9))(10))
+print((bind.call(f2, {a: "bound"}, 11, 12))())
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/JDK-8051778.js.EXPECTED	Thu Nov 27 13:04:46 2014 +0100
@@ -0,0 +1,10 @@
+l=[foo]
+l=[foo, foo2]
+l2_size()=0
+Bound Integer(2) constructor: 2
+BiFunction with literal: unbound, 1, 2
+BiFunction with literal: unbound, 3, 4
+BiFunction with literal: unbound, 5, 6
+BiFunction with function: undefined, 7, 8
+BiFunction with function: undefined, 9, 10
+BiFunction with function: undefined, 11, 12