nashorn/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java
changeset 18841 9bbc4b8832b2
parent 16245 6a1c6c8bc113
child 19630 99f53f31008e
equal deleted inserted replaced
18840:3e7bff1b7b59 18841:9bbc4b8832b2
    82 */
    82 */
    83 
    83 
    84 package jdk.internal.dynalink.beans;
    84 package jdk.internal.dynalink.beans;
    85 
    85 
    86 import java.lang.invoke.MethodHandle;
    86 import java.lang.invoke.MethodHandle;
       
    87 import java.lang.invoke.MethodHandles;
    87 import java.lang.invoke.MethodType;
    88 import java.lang.invoke.MethodType;
       
    89 import java.util.ArrayList;
    88 import java.util.Iterator;
    90 import java.util.Iterator;
    89 import java.util.LinkedList;
    91 import java.util.LinkedList;
    90 import java.util.List;
    92 import java.util.List;
       
    93 import jdk.internal.dynalink.CallSiteDescriptor;
    91 import jdk.internal.dynalink.beans.ApplicableOverloadedMethods.ApplicabilityTest;
    94 import jdk.internal.dynalink.beans.ApplicableOverloadedMethods.ApplicabilityTest;
    92 import jdk.internal.dynalink.linker.LinkerServices;
    95 import jdk.internal.dynalink.linker.LinkerServices;
    93 import jdk.internal.dynalink.support.TypeUtilities;
    96 import jdk.internal.dynalink.support.TypeUtilities;
    94 
    97 
    95 /**
    98 /**
    96  * Represents an overloaded method.
    99  * Represents a group of {@link SingleDynamicMethod} objects that represents all overloads of a particular name (or all
       
   100  * constructors) for a particular class. Correctly handles overload resolution, variable arity methods, and caller
       
   101  * sensitive methods within the overloads.
    97  *
   102  *
    98  * @author Attila Szegedi
   103  * @author Attila Szegedi
    99  */
   104  */
   100 class OverloadedDynamicMethod extends DynamicMethod {
   105 class OverloadedDynamicMethod extends DynamicMethod {
   101     /**
   106     /**
   102      * Holds a list of all methods.
   107      * Holds a list of all methods.
   103      */
   108      */
   104     private final LinkedList<MethodHandle> methods;
   109     private final LinkedList<SingleDynamicMethod> methods;
   105     private final ClassLoader classLoader;
   110     private final ClassLoader classLoader;
   106 
   111 
   107     /**
   112     /**
   108      * Creates a new overloaded dynamic method.
   113      * Creates a new overloaded dynamic method.
   109      *
   114      *
   110      * @param clazz the class this method belongs to
   115      * @param clazz the class this method belongs to
   111      * @param name the name of the method
   116      * @param name the name of the method
   112      */
   117      */
   113     OverloadedDynamicMethod(Class<?> clazz, String name) {
   118     OverloadedDynamicMethod(Class<?> clazz, String name) {
   114         this(new LinkedList<MethodHandle>(), clazz.getClassLoader(), getClassAndMethodName(clazz, name));
   119         this(new LinkedList<SingleDynamicMethod>(), clazz.getClassLoader(), getClassAndMethodName(clazz, name));
   115     }
   120     }
   116 
   121 
   117     private OverloadedDynamicMethod(LinkedList<MethodHandle> methods, ClassLoader classLoader, String name) {
   122     private OverloadedDynamicMethod(LinkedList<SingleDynamicMethod> methods, ClassLoader classLoader, String name) {
   118         super(name);
   123         super(name);
   119         this.methods = methods;
   124         this.methods = methods;
   120         this.classLoader = classLoader;
   125         this.classLoader = classLoader;
   121     }
   126     }
   122 
   127 
   123     @Override
   128     @Override
   124     SimpleDynamicMethod getMethodForExactParamTypes(String paramTypes) {
   129     SingleDynamicMethod getMethodForExactParamTypes(String paramTypes) {
   125         final LinkedList<MethodHandle> matchingMethods = new LinkedList<>();
   130         final LinkedList<SingleDynamicMethod> matchingMethods = new LinkedList<>();
   126         for(MethodHandle method: methods) {
   131         for(SingleDynamicMethod method: methods) {
   127             if(typeMatchesDescription(paramTypes, method.type())) {
   132             final SingleDynamicMethod matchingMethod = method.getMethodForExactParamTypes(paramTypes);
   128                 matchingMethods.add(method);
   133             if(matchingMethod != null) {
       
   134                 matchingMethods.add(matchingMethod);
   129             }
   135             }
   130         }
   136         }
   131         switch(matchingMethods.size()) {
   137         switch(matchingMethods.size()) {
   132             case 0: {
   138             case 0: {
   133                 return null;
   139                 return null;
   134             }
   140             }
   135             case 1: {
   141             case 1: {
   136                 final MethodHandle target = matchingMethods.get(0);
   142                 return matchingMethods.getFirst();
   137                 return new SimpleDynamicMethod(target, SimpleDynamicMethod.getMethodNameWithSignature(target, getName()));
       
   138             }
   143             }
   139             default: {
   144             default: {
   140                 throw new BootstrapMethodError("Can't choose among " + matchingMethods + " for argument types "
   145                 throw new BootstrapMethodError("Can't choose among " + matchingMethods + " for argument types "
   141                         + paramTypes + " for method " + getName());
   146                         + paramTypes + " for method " + getName());
   142             }
   147             }
   143         }
   148         }
   144     }
   149     }
   145 
   150 
   146     @Override
   151     @Override
   147     public MethodHandle getInvocation(final MethodType callSiteType, final LinkerServices linkerServices) {
   152     public MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
       
   153         final MethodType callSiteType = callSiteDescriptor.getMethodType();
   148         // First, find all methods applicable to the call site by subtyping (JLS 15.12.2.2)
   154         // First, find all methods applicable to the call site by subtyping (JLS 15.12.2.2)
   149         final ApplicableOverloadedMethods subtypingApplicables = getApplicables(callSiteType,
   155         final ApplicableOverloadedMethods subtypingApplicables = getApplicables(callSiteType,
   150                 ApplicableOverloadedMethods.APPLICABLE_BY_SUBTYPING);
   156                 ApplicableOverloadedMethods.APPLICABLE_BY_SUBTYPING);
   151         // Next, find all methods applicable by method invocation conversion to the call site (JLS 15.12.2.3).
   157         // Next, find all methods applicable by method invocation conversion to the call site (JLS 15.12.2.3).
   152         final ApplicableOverloadedMethods methodInvocationApplicables = getApplicables(callSiteType,
   158         final ApplicableOverloadedMethods methodInvocationApplicables = getApplicables(callSiteType,
   154         // Finally, find all methods applicable by variable arity invocation. (JLS 15.12.2.4).
   160         // Finally, find all methods applicable by variable arity invocation. (JLS 15.12.2.4).
   155         final ApplicableOverloadedMethods variableArityApplicables = getApplicables(callSiteType,
   161         final ApplicableOverloadedMethods variableArityApplicables = getApplicables(callSiteType,
   156                 ApplicableOverloadedMethods.APPLICABLE_BY_VARIABLE_ARITY);
   162                 ApplicableOverloadedMethods.APPLICABLE_BY_VARIABLE_ARITY);
   157 
   163 
   158         // Find the methods that are maximally specific based on the call site signature
   164         // Find the methods that are maximally specific based on the call site signature
   159         List<MethodHandle> maximallySpecifics = subtypingApplicables.findMaximallySpecificMethods();
   165         List<SingleDynamicMethod> maximallySpecifics = subtypingApplicables.findMaximallySpecificMethods();
   160         if(maximallySpecifics.isEmpty()) {
   166         if(maximallySpecifics.isEmpty()) {
   161             maximallySpecifics = methodInvocationApplicables.findMaximallySpecificMethods();
   167             maximallySpecifics = methodInvocationApplicables.findMaximallySpecificMethods();
   162             if(maximallySpecifics.isEmpty()) {
   168             if(maximallySpecifics.isEmpty()) {
   163                 maximallySpecifics = variableArityApplicables.findMaximallySpecificMethods();
   169                 maximallySpecifics = variableArityApplicables.findMaximallySpecificMethods();
   164             }
   170             }
   169         // might match more concrete types passed in invocations. That's why we provisionally call them "invokables".
   175         // might match more concrete types passed in invocations. That's why we provisionally call them "invokables".
   170         // This is typical for very generic signatures at call sites. Typical example: call site specifies
   176         // This is typical for very generic signatures at call sites. Typical example: call site specifies
   171         // (Object, Object), and we have a method whose parameter types are (String, int). None of the JLS applicability
   177         // (Object, Object), and we have a method whose parameter types are (String, int). None of the JLS applicability
   172         // rules will trigger, but we must consider the method, as it can be the right match for a concrete invocation.
   178         // rules will trigger, but we must consider the method, as it can be the right match for a concrete invocation.
   173         @SuppressWarnings({ "unchecked", "rawtypes" })
   179         @SuppressWarnings({ "unchecked", "rawtypes" })
   174         final List<MethodHandle> invokables = (List)methods.clone();
   180         final List<SingleDynamicMethod> invokables = (List)methods.clone();
   175         invokables.removeAll(subtypingApplicables.getMethods());
   181         invokables.removeAll(subtypingApplicables.getMethods());
   176         invokables.removeAll(methodInvocationApplicables.getMethods());
   182         invokables.removeAll(methodInvocationApplicables.getMethods());
   177         invokables.removeAll(variableArityApplicables.getMethods());
   183         invokables.removeAll(variableArityApplicables.getMethods());
   178         for(final Iterator<MethodHandle> it = invokables.iterator(); it.hasNext();) {
   184         for(final Iterator<SingleDynamicMethod> it = invokables.iterator(); it.hasNext();) {
   179             final MethodHandle m = it.next();
   185             final SingleDynamicMethod m = it.next();
   180             if(!isApplicableDynamically(linkerServices, callSiteType, m)) {
   186             if(!isApplicableDynamically(linkerServices, callSiteType, m)) {
   181                 it.remove();
   187                 it.remove();
   182             }
   188             }
   183         }
   189         }
   184 
   190 
   197                 // No overloads can ever match the call site type
   203                 // No overloads can ever match the call site type
   198                 return null;
   204                 return null;
   199             }
   205             }
   200             case 1: {
   206             case 1: {
   201                 // Very lucky, we ended up with a single candidate method handle based on the call site signature; we
   207                 // Very lucky, we ended up with a single candidate method handle based on the call site signature; we
   202                 // can link it very simply by delegating to a SimpleDynamicMethod.
   208                 // can link it very simply by delegating to the SingleDynamicMethod.
   203                 final MethodHandle mh = invokables.iterator().next();
   209                 invokables.iterator().next().getInvocation(callSiteDescriptor, linkerServices);
   204                 return new SimpleDynamicMethod(mh).getInvocation(callSiteType, linkerServices);
       
   205             }
   210             }
   206             default: {
   211             default: {
   207                 // We have more than one candidate. We have no choice but to link to a method that resolves overloads on
   212                 // We have more than one candidate. We have no choice but to link to a method that resolves overloads on
   208                 // every invocation (alternatively, we could opportunistically link the one method that resolves for the
   213                 // every invocation (alternatively, we could opportunistically link the one method that resolves for the
   209                 // current arguments, but we'd need to install a fairly complex guard for that and when it'd fail, we'd
   214                 // current arguments, but we'd need to install a fairly complex guard for that and when it'd fail, we'd
   210                 // go back all the way to candidate selection.
   215                 // go back all the way to candidate selection. Note that we're resolving any potential caller sensitive
   211                 // TODO: cache per call site type
   216                 // methods here to their handles, as the OverloadedMethod instance is specific to a call site, so it
   212                 return new OverloadedMethod(invokables, this, callSiteType, linkerServices).getInvoker();
   217                 // has an already determined Lookup.
       
   218                 final List<MethodHandle> methodHandles = new ArrayList<>(invokables.size());
       
   219                 final MethodHandles.Lookup lookup = callSiteDescriptor.getLookup();
       
   220                 for(SingleDynamicMethod method: invokables) {
       
   221                     methodHandles.add(method.getTarget(lookup));
       
   222                 }
       
   223                 return new OverloadedMethod(methodHandles, this, callSiteType, linkerServices).getInvoker();
   213             }
   224             }
   214         }
   225         }
   215 
   226 
   216     }
   227     }
   217 
   228 
   218     @Override
   229     @Override
   219     public boolean contains(MethodHandle mh) {
   230     public boolean contains(SingleDynamicMethod m) {
   220         final MethodType type = mh.type();
   231         for(SingleDynamicMethod method: methods) {
   221         for(MethodHandle method: methods) {
   232             if(method.contains(m)) {
   222             if(typesEqualNoReceiver(type, method.type())) {
       
   223                 return true;
   233                 return true;
   224             }
   234             }
   225         }
   235         }
   226         return false;
   236         return false;
   227     }
       
   228 
       
   229     private static boolean typesEqualNoReceiver(MethodType type1, MethodType type2) {
       
   230         final int pc = type1.parameterCount();
       
   231         if(pc != type2.parameterCount()) {
       
   232             return false;
       
   233         }
       
   234         for(int i = 1; i < pc; ++i) { // i = 1: ignore receiver
       
   235             if(type1.parameterType(i) != type2.parameterType(i)) {
       
   236                 return false;
       
   237             }
       
   238         }
       
   239         return true;
       
   240     }
   237     }
   241 
   238 
   242     ClassLoader getClassLoader() {
   239     ClassLoader getClassLoader() {
   243         return classLoader;
   240         return classLoader;
   244     }
   241     }
   245 
   242 
   246     private static boolean isApplicableDynamically(LinkerServices linkerServices, MethodType callSiteType,
   243     private static boolean isApplicableDynamically(LinkerServices linkerServices, MethodType callSiteType,
   247             MethodHandle m) {
   244             SingleDynamicMethod m) {
   248         final MethodType methodType = m.type();
   245         final MethodType methodType = m.getMethodType();
   249         final boolean varArgs = m.isVarargsCollector();
   246         final boolean varArgs = m.isVarArgs();
   250         final int fixedArgLen = methodType.parameterCount() - (varArgs ? 1 : 0);
   247         final int fixedArgLen = methodType.parameterCount() - (varArgs ? 1 : 0);
   251         final int callSiteArgLen = callSiteType.parameterCount();
   248         final int callSiteArgLen = callSiteType.parameterCount();
   252 
   249 
   253         // Arity checks
   250         // Arity checks
   254         if(varArgs) {
   251         if(varArgs) {
   299     private ApplicableOverloadedMethods getApplicables(MethodType callSiteType, ApplicabilityTest test) {
   296     private ApplicableOverloadedMethods getApplicables(MethodType callSiteType, ApplicabilityTest test) {
   300         return new ApplicableOverloadedMethods(methods, callSiteType, test);
   297         return new ApplicableOverloadedMethods(methods, callSiteType, test);
   301     }
   298     }
   302 
   299 
   303     /**
   300     /**
   304      * Add a method identified by a {@link SimpleDynamicMethod} to this overloaded method's set.
       
   305      *
       
   306      * @param method the method to add.
       
   307      */
       
   308     void addMethod(SimpleDynamicMethod method) {
       
   309         addMethod(method.getTarget());
       
   310     }
       
   311 
       
   312     /**
       
   313      * Add a method to this overloaded method's set.
   301      * Add a method to this overloaded method's set.
   314      *
   302      *
   315      * @param method a method to add
   303      * @param method a method to add
   316      */
   304      */
   317     public void addMethod(MethodHandle method) {
   305     public void addMethod(SingleDynamicMethod method) {
   318         methods.add(method);
   306         methods.add(method);
   319     }
   307     }
   320 }
   308 }