# HG changeset patch # User attila # Date 1417089886 -3600 # Node ID a56051d3cdf5eb423052a52b10718117ee3697c1 # Parent f39081a16f71ff8cca7fb51cc16f015cade78177 8051778: support bind on all Nashorn callables Reviewed-by: hannesw, lagergren diff -r f39081a16f71 -r a56051d3cdf5 src/jdk/nashorn/internal/objects/NativeFunction.java --- a/src/jdk/nashorn/internal/objects/NativeFunction.java Thu Nov 27 18:02:28 2014 +0100 +++ b/src/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); } /** diff -r f39081a16f71 -r a56051d3cdf5 src/jdk/nashorn/internal/objects/NativeObject.java --- a/src/jdk/nashorn/internal/objects/NativeObject.java Thu Nov 27 18:02:28 2014 +0100 +++ b/src/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) { diff -r f39081a16f71 -r a56051d3cdf5 src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java --- a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java Thu Nov 27 18:02:28 2014 +0100 +++ b/src/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); } diff -r f39081a16f71 -r a56051d3cdf5 src/jdk/nashorn/internal/runtime/arrays/IteratorAction.java --- a/src/jdk/nashorn/internal/runtime/arrays/IteratorAction.java Thu Nov 27 18:02:28 2014 +0100 +++ b/src/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; diff -r f39081a16f71 -r a56051d3cdf5 src/jdk/nashorn/internal/runtime/linker/Bootstrap.java --- a/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Thu Nov 27 18:02:28 2014 +0100 +++ b/src/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); } /** diff -r f39081a16f71 -r a56051d3cdf5 src/jdk/nashorn/internal/runtime/linker/BoundCallable.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/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(); + } +} diff -r f39081a16f71 -r a56051d3cdf5 src/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/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))); + } +} diff -r f39081a16f71 -r a56051d3cdf5 src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethod.java --- a/src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethod.java Thu Nov 27 18:02:28 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); - } -} diff -r f39081a16f71 -r a56051d3cdf5 src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java --- a/src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java Thu Nov 27 18:02:28 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))); - } -} diff -r f39081a16f71 -r a56051d3cdf5 src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java --- a/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java Thu Nov 27 18:02:28 2014 +0100 +++ b/src/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); } /** diff -r f39081a16f71 -r a56051d3cdf5 test/script/basic/JDK-8051778.js --- /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))()) diff -r f39081a16f71 -r a56051d3cdf5 test/script/basic/JDK-8051778.js.EXPECTED --- /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