1.1 --- a/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java Wed Jan 14 16:26:32 2015 -0800 1.2 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java Tue Jan 13 16:38:29 2015 +0100 1.3 @@ -25,20 +25,29 @@ 1.4 1.5 package jdk.nashorn.internal.runtime.linker; 1.6 1.7 +import static jdk.nashorn.internal.lookup.Lookup.MH; 1.8 +import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 1.9 + 1.10 import java.lang.invoke.MethodHandle; 1.11 import java.lang.invoke.MethodHandles; 1.12 import java.lang.invoke.MethodType; 1.13 +import java.lang.reflect.Method; 1.14 +import java.lang.reflect.Modifier; 1.15 +import jdk.internal.dynalink.CallSiteDescriptor; 1.16 import jdk.internal.dynalink.beans.BeansLinker; 1.17 import jdk.internal.dynalink.linker.ConversionComparator.Comparison; 1.18 import jdk.internal.dynalink.linker.GuardedInvocation; 1.19 import jdk.internal.dynalink.linker.GuardingDynamicLinker; 1.20 import jdk.internal.dynalink.linker.LinkRequest; 1.21 import jdk.internal.dynalink.linker.LinkerServices; 1.22 +import jdk.internal.dynalink.support.Guards; 1.23 import jdk.internal.dynalink.support.Lookup; 1.24 import jdk.nashorn.api.scripting.ScriptUtils; 1.25 import jdk.nashorn.internal.objects.NativeArray; 1.26 import jdk.nashorn.internal.runtime.ConsString; 1.27 +import jdk.nashorn.internal.runtime.Context; 1.28 import jdk.nashorn.internal.runtime.ScriptObject; 1.29 +import jdk.nashorn.internal.runtime.ScriptRuntime; 1.30 import jdk.nashorn.internal.runtime.options.Options; 1.31 1.32 /** 1.33 @@ -68,19 +77,49 @@ 1.34 FILTER_CONSSTRING = lookup.findOwnStatic("consStringFilter", Object.class, Object.class); 1.35 } 1.36 1.37 + // cache of @FunctionalInterface method of implementor classes 1.38 + private static final ClassValue<Method> FUNCTIONAL_IFACE_METHOD = new ClassValue<Method>() { 1.39 + @Override 1.40 + protected Method computeValue(final Class<?> type) { 1.41 + return findFunctionalInterfaceMethod(type); 1.42 + } 1.43 + }; 1.44 + 1.45 private final BeansLinker beansLinker = new BeansLinker(); 1.46 1.47 @Override 1.48 public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception { 1.49 - if (linkRequest.getReceiver() instanceof ConsString) { 1.50 + final Object self = linkRequest.getReceiver(); 1.51 + final CallSiteDescriptor desc = linkRequest.getCallSiteDescriptor(); 1.52 + if (self instanceof ConsString) { 1.53 // In order to treat ConsString like a java.lang.String we need a link request with a string receiver. 1.54 final Object[] arguments = linkRequest.getArguments(); 1.55 arguments[0] = ""; 1.56 - final LinkRequest forgedLinkRequest = linkRequest.replaceArguments(linkRequest.getCallSiteDescriptor(), arguments); 1.57 + final LinkRequest forgedLinkRequest = linkRequest.replaceArguments(desc, arguments); 1.58 final GuardedInvocation invocation = getGuardedInvocation(beansLinker, forgedLinkRequest, linkerServices); 1.59 // If an invocation is found we add a filter that makes it work for both Strings and ConsStrings. 1.60 return invocation == null ? null : invocation.filterArguments(0, FILTER_CONSSTRING); 1.61 } 1.62 + 1.63 + if ("call".equals(desc.getNameToken(CallSiteDescriptor.OPERATOR))) { 1.64 + // Support dyn:call on any object that supports some @FunctionalInterface 1.65 + // annotated interface. This way Java method, constructor references or 1.66 + // implementations of java.util.function.* interfaces can be called as though 1.67 + // those are script functions. 1.68 + final Method m = getFunctionalInterfaceMethod(self.getClass()); 1.69 + if (m != null) { 1.70 + final MethodType callType = desc.getMethodType(); 1.71 + // 'callee' and 'thiz' passed from script + actual arguments 1.72 + if (callType.parameterCount() != m.getParameterCount() + 2) { 1.73 + throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self)); 1.74 + } 1.75 + return new GuardedInvocation( 1.76 + // drop 'thiz' passed from the script. 1.77 + MH.dropArguments(desc.getLookup().unreflect(m), 1, callType.parameterType(1)), 1.78 + Guards.getInstanceOfGuard(m.getDeclaringClass())).asTypeSafeReturn( 1.79 + new NashornBeansLinkerServices(linkerServices), callType); 1.80 + } 1.81 + } 1.82 return getGuardedInvocation(beansLinker, linkRequest, linkerServices); 1.83 } 1.84 1.85 @@ -137,6 +176,38 @@ 1.86 return arg instanceof ConsString ? arg.toString() : arg; 1.87 } 1.88 1.89 + private static Method findFunctionalInterfaceMethod(final Class<?> clazz) { 1.90 + if (clazz == null) { 1.91 + return null; 1.92 + } 1.93 + 1.94 + for (final Class<?> iface : clazz.getInterfaces()) { 1.95 + // check accessiblity up-front 1.96 + if (! Context.isAccessibleClass(iface)) { 1.97 + continue; 1.98 + } 1.99 + 1.100 + // check for @FunctionalInterface 1.101 + if (iface.isAnnotationPresent(FunctionalInterface.class)) { 1.102 + // return the first abstract method 1.103 + for (final Method m : iface.getMethods()) { 1.104 + if (Modifier.isAbstract(m.getModifiers())) { 1.105 + return m; 1.106 + } 1.107 + } 1.108 + } 1.109 + } 1.110 + 1.111 + // did not find here, try super class 1.112 + return findFunctionalInterfaceMethod(clazz.getSuperclass()); 1.113 + } 1.114 + 1.115 + // Returns @FunctionalInterface annotated interface's single abstract 1.116 + // method. If not found, returns null. 1.117 + static Method getFunctionalInterfaceMethod(final Class<?> clazz) { 1.118 + return FUNCTIONAL_IFACE_METHOD.get(clazz); 1.119 + } 1.120 + 1.121 private static class NashornBeansLinkerServices implements LinkerServices { 1.122 private final LinkerServices linkerServices; 1.123