src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java

changeset 1181
cff6eb75ba9b
parent 1088
b49b6786afad
child 1182
3903ddaab26a
     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  

mercurial