nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java
changeset 34456 84eaea8d0574
parent 34096 5ac6287ec71a
parent 34455 cc9f05d3caf0
child 34457 81a65a2faef3
equal deleted inserted replaced
34096:5ac6287ec71a 34456:84eaea8d0574
     1 /*
       
     2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 /*
       
    27  * This file is available under and governed by the GNU General Public
       
    28  * License version 2 only, as published by the Free Software Foundation.
       
    29  * However, the following notice accompanied the original version of this
       
    30  * file, and Oracle licenses the original version of this file under the BSD
       
    31  * license:
       
    32  */
       
    33 /*
       
    34    Copyright 2009-2013 Attila Szegedi
       
    35 
       
    36    Licensed under both the Apache License, Version 2.0 (the "Apache License")
       
    37    and the BSD License (the "BSD License"), with licensee being free to
       
    38    choose either of the two at their discretion.
       
    39 
       
    40    You may not use this file except in compliance with either the Apache
       
    41    License or the BSD License.
       
    42 
       
    43    If you choose to use this file in compliance with the Apache License, the
       
    44    following notice applies to you:
       
    45 
       
    46        You may obtain a copy of the Apache License at
       
    47 
       
    48            http://www.apache.org/licenses/LICENSE-2.0
       
    49 
       
    50        Unless required by applicable law or agreed to in writing, software
       
    51        distributed under the License is distributed on an "AS IS" BASIS,
       
    52        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
       
    53        implied. See the License for the specific language governing
       
    54        permissions and limitations under the License.
       
    55 
       
    56    If you choose to use this file in compliance with the BSD License, the
       
    57    following notice applies to you:
       
    58 
       
    59        Redistribution and use in source and binary forms, with or without
       
    60        modification, are permitted provided that the following conditions are
       
    61        met:
       
    62        * Redistributions of source code must retain the above copyright
       
    63          notice, this list of conditions and the following disclaimer.
       
    64        * Redistributions in binary form must reproduce the above copyright
       
    65          notice, this list of conditions and the following disclaimer in the
       
    66          documentation and/or other materials provided with the distribution.
       
    67        * Neither the name of the copyright holder nor the names of
       
    68          contributors may be used to endorse or promote products derived from
       
    69          this software without specific prior written permission.
       
    70 
       
    71        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
       
    72        IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
       
    73        TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
       
    74        PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
       
    75        BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
       
    76        CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
       
    77        SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
       
    78        BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
       
    79        WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
       
    80        OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
       
    81        ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    82 */
       
    83 
       
    84 package jdk.internal.dynalink.beans;
       
    85 
       
    86 import java.lang.invoke.MethodHandle;
       
    87 import java.lang.invoke.MethodType;
       
    88 import java.security.AccessControlContext;
       
    89 import java.security.AccessController;
       
    90 import java.security.PrivilegedAction;
       
    91 import java.text.Collator;
       
    92 import java.util.ArrayList;
       
    93 import java.util.Collections;
       
    94 import java.util.IdentityHashMap;
       
    95 import java.util.Iterator;
       
    96 import java.util.LinkedList;
       
    97 import java.util.List;
       
    98 import java.util.Map;
       
    99 import java.util.Set;
       
   100 import jdk.internal.dynalink.CallSiteDescriptor;
       
   101 import jdk.internal.dynalink.beans.ApplicableOverloadedMethods.ApplicabilityTest;
       
   102 import jdk.internal.dynalink.internal.AccessControlContextFactory;
       
   103 import jdk.internal.dynalink.internal.InternalTypeUtilities;
       
   104 import jdk.internal.dynalink.linker.LinkerServices;
       
   105 
       
   106 /**
       
   107  * Represents a group of {@link SingleDynamicMethod} objects that represents all overloads of a particular name (or all
       
   108  * constructors) for a particular class. Correctly handles overload resolution, variable arity methods, and caller
       
   109  * sensitive methods within the overloads.
       
   110  */
       
   111 class OverloadedDynamicMethod extends DynamicMethod {
       
   112     /**
       
   113      * Holds a list of all methods.
       
   114      */
       
   115     private final LinkedList<SingleDynamicMethod> methods;
       
   116     private final ClassLoader classLoader;
       
   117 
       
   118     /**
       
   119      * Creates a new overloaded dynamic method.
       
   120      *
       
   121      * @param clazz the class this method belongs to
       
   122      * @param name the name of the method
       
   123      */
       
   124     OverloadedDynamicMethod(final Class<?> clazz, final String name) {
       
   125         this(new LinkedList<SingleDynamicMethod>(), clazz.getClassLoader(), getClassAndMethodName(clazz, name));
       
   126     }
       
   127 
       
   128     private OverloadedDynamicMethod(final LinkedList<SingleDynamicMethod> methods, final ClassLoader classLoader, final String name) {
       
   129         super(name);
       
   130         this.methods = methods;
       
   131         this.classLoader = classLoader;
       
   132     }
       
   133 
       
   134     @Override
       
   135     SingleDynamicMethod getMethodForExactParamTypes(final String paramTypes) {
       
   136         final LinkedList<SingleDynamicMethod> matchingMethods = new LinkedList<>();
       
   137         for(final SingleDynamicMethod method: methods) {
       
   138             final SingleDynamicMethod matchingMethod = method.getMethodForExactParamTypes(paramTypes);
       
   139             if(matchingMethod != null) {
       
   140                 matchingMethods.add(matchingMethod);
       
   141             }
       
   142         }
       
   143         switch(matchingMethods.size()) {
       
   144             case 0: {
       
   145                 return null;
       
   146             }
       
   147             case 1: {
       
   148                 return matchingMethods.getFirst();
       
   149             }
       
   150             default: {
       
   151                 throw new BootstrapMethodError("Can't choose among " + matchingMethods + " for argument types "
       
   152                         + paramTypes + " for method " + getName());
       
   153             }
       
   154         }
       
   155     }
       
   156 
       
   157     @Override
       
   158     public MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
       
   159         final MethodType callSiteType = callSiteDescriptor.getMethodType();
       
   160         // First, find all methods applicable to the call site by subtyping (JLS 15.12.2.2)
       
   161         final ApplicableOverloadedMethods subtypingApplicables = getApplicables(callSiteType,
       
   162                 ApplicableOverloadedMethods.APPLICABLE_BY_SUBTYPING);
       
   163         // Next, find all methods applicable by method invocation conversion to the call site (JLS 15.12.2.3).
       
   164         final ApplicableOverloadedMethods methodInvocationApplicables = getApplicables(callSiteType,
       
   165                 ApplicableOverloadedMethods.APPLICABLE_BY_METHOD_INVOCATION_CONVERSION);
       
   166         // Finally, find all methods applicable by variable arity invocation. (JLS 15.12.2.4).
       
   167         final ApplicableOverloadedMethods variableArityApplicables = getApplicables(callSiteType,
       
   168                 ApplicableOverloadedMethods.APPLICABLE_BY_VARIABLE_ARITY);
       
   169 
       
   170         // Find the methods that are maximally specific based on the call site signature
       
   171         List<SingleDynamicMethod> maximallySpecifics = subtypingApplicables.findMaximallySpecificMethods();
       
   172         if(maximallySpecifics.isEmpty()) {
       
   173             maximallySpecifics = methodInvocationApplicables.findMaximallySpecificMethods();
       
   174             if(maximallySpecifics.isEmpty()) {
       
   175                 maximallySpecifics = variableArityApplicables.findMaximallySpecificMethods();
       
   176             }
       
   177         }
       
   178 
       
   179         // Now, get a list of the rest of the methods; those that are *not* applicable to the call site signature based
       
   180         // on JLS rules. As paradoxical as that might sound, we have to consider these for dynamic invocation, as they
       
   181         // might match more concrete types passed in invocations. That's why we provisionally call them "invokables".
       
   182         // This is typical for very generic signatures at call sites. Typical example: call site specifies
       
   183         // (Object, Object), and we have a method whose parameter types are (String, int). None of the JLS applicability
       
   184         // rules will trigger, but we must consider the method, as it can be the right match for a concrete invocation.
       
   185         @SuppressWarnings({ "unchecked", "rawtypes" })
       
   186         final List<SingleDynamicMethod> invokables = (List)methods.clone();
       
   187         invokables.removeAll(subtypingApplicables.getMethods());
       
   188         invokables.removeAll(methodInvocationApplicables.getMethods());
       
   189         invokables.removeAll(variableArityApplicables.getMethods());
       
   190         for(final Iterator<SingleDynamicMethod> it = invokables.iterator(); it.hasNext();) {
       
   191             final SingleDynamicMethod m = it.next();
       
   192             if(!isApplicableDynamically(linkerServices, callSiteType, m)) {
       
   193                 it.remove();
       
   194             }
       
   195         }
       
   196 
       
   197         // If no additional methods can apply at invocation time, and there's more than one maximally specific method
       
   198         // based on call site signature, that is a link-time ambiguity. In a static scenario, javac would report an
       
   199         // ambiguity error.
       
   200         if(invokables.isEmpty() && maximallySpecifics.size() > 1) {
       
   201             throw new BootstrapMethodError("Can't choose among " + maximallySpecifics + " for argument types "
       
   202                     + callSiteType);
       
   203         }
       
   204 
       
   205         // Merge them all.
       
   206         invokables.addAll(maximallySpecifics);
       
   207         switch(invokables.size()) {
       
   208             case 0: {
       
   209                 // No overloads can ever match the call site type
       
   210                 return null;
       
   211             }
       
   212             case 1: {
       
   213                 // Very lucky, we ended up with a single candidate method handle based on the call site signature; we
       
   214                 // can link it very simply by delegating to the SingleDynamicMethod.
       
   215                 return invokables.iterator().next().getInvocation(callSiteDescriptor, linkerServices);
       
   216             }
       
   217             default: {
       
   218                 // We have more than one candidate. We have no choice but to link to a method that resolves overloads on
       
   219                 // every invocation (alternatively, we could opportunistically link the one method that resolves for the
       
   220                 // current arguments, but we'd need to install a fairly complex guard for that and when it'd fail, we'd
       
   221                 // go back all the way to candidate selection. Note that we're resolving any potential caller sensitive
       
   222                 // methods here to their handles, as the OverloadedMethod instance is specific to a call site, so it
       
   223                 // has an already determined Lookup.
       
   224                 final List<MethodHandle> methodHandles = new ArrayList<>(invokables.size());
       
   225                 for(final SingleDynamicMethod method: invokables) {
       
   226                     methodHandles.add(method.getTarget(callSiteDescriptor));
       
   227                 }
       
   228                 return new OverloadedMethod(methodHandles, this, getCallSiteClassLoader(callSiteDescriptor), callSiteType, linkerServices).getInvoker();
       
   229             }
       
   230         }
       
   231     }
       
   232 
       
   233     private static final AccessControlContext GET_CALL_SITE_CLASS_LOADER_CONTEXT =
       
   234             AccessControlContextFactory.createAccessControlContext(
       
   235                     "getClassLoader", CallSiteDescriptor.GET_LOOKUP_PERMISSION_NAME);
       
   236 
       
   237     private static ClassLoader getCallSiteClassLoader(final CallSiteDescriptor callSiteDescriptor) {
       
   238         return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
       
   239             @Override
       
   240             public ClassLoader run() {
       
   241                 return callSiteDescriptor.getLookup().lookupClass().getClassLoader();
       
   242             }
       
   243         }, GET_CALL_SITE_CLASS_LOADER_CONTEXT);
       
   244     }
       
   245 
       
   246     @Override
       
   247     public boolean contains(final SingleDynamicMethod m) {
       
   248         for(final SingleDynamicMethod method: methods) {
       
   249             if(method.contains(m)) {
       
   250                 return true;
       
   251             }
       
   252         }
       
   253         return false;
       
   254     }
       
   255 
       
   256     @Override
       
   257     public boolean isConstructor() {
       
   258         assert !methods.isEmpty();
       
   259         return methods.getFirst().isConstructor();
       
   260     }
       
   261 
       
   262     @Override
       
   263     public String toString() {
       
   264         // First gather the names and sort them. This makes it consistent and easier to read.
       
   265         final List<String> names = new ArrayList<>(methods.size());
       
   266         int len = 0;
       
   267         for (final SingleDynamicMethod m: methods) {
       
   268             final String name = m.getName();
       
   269             len += name.length();
       
   270             names.add(name);
       
   271         }
       
   272         // Case insensitive sorting, so e.g. "Object" doesn't come before "boolean".
       
   273         final Collator collator = Collator.getInstance();
       
   274         collator.setStrength(Collator.SECONDARY);
       
   275         Collections.sort(names, collator);
       
   276 
       
   277         final String className = getClass().getName();
       
   278         // Class name length + length of signatures + 2 chars/per signature for indentation and newline +
       
   279         // 3 for brackets and initial newline
       
   280         final int totalLength = className.length() + len + 2 * names.size() + 3;
       
   281         final StringBuilder b = new StringBuilder(totalLength);
       
   282         b.append('[').append(className).append('\n');
       
   283         for(final String name: names) {
       
   284             b.append(' ').append(name).append('\n');
       
   285         }
       
   286         b.append(']');
       
   287         assert b.length() == totalLength;
       
   288         return b.toString();
       
   289     };
       
   290 
       
   291     ClassLoader getClassLoader() {
       
   292         return classLoader;
       
   293     }
       
   294 
       
   295     private static boolean isApplicableDynamically(final LinkerServices linkerServices, final MethodType callSiteType,
       
   296             final SingleDynamicMethod m) {
       
   297         final MethodType methodType = m.getMethodType();
       
   298         final boolean varArgs = m.isVarArgs();
       
   299         final int fixedArgLen = methodType.parameterCount() - (varArgs ? 1 : 0);
       
   300         final int callSiteArgLen = callSiteType.parameterCount();
       
   301 
       
   302         // Arity checks
       
   303         if(varArgs) {
       
   304             if(callSiteArgLen < fixedArgLen) {
       
   305                 return false;
       
   306             }
       
   307         } else if(callSiteArgLen != fixedArgLen) {
       
   308             return false;
       
   309         }
       
   310 
       
   311         // Fixed arguments type checks, starting from 1, as receiver type doesn't participate
       
   312         for(int i = 1; i < fixedArgLen; ++i) {
       
   313             if(!isApplicableDynamically(linkerServices, callSiteType.parameterType(i), methodType.parameterType(i))) {
       
   314                 return false;
       
   315             }
       
   316         }
       
   317         if(!varArgs) {
       
   318             // Not vararg; both arity and types matched.
       
   319             return true;
       
   320         }
       
   321 
       
   322         final Class<?> varArgArrayType = methodType.parameterType(fixedArgLen);
       
   323         final Class<?> varArgType = varArgArrayType.getComponentType();
       
   324 
       
   325         if(fixedArgLen == callSiteArgLen - 1) {
       
   326             // Exactly one vararg; check both array type matching and array component type matching.
       
   327             final Class<?> callSiteArgType = callSiteType.parameterType(fixedArgLen);
       
   328             return isApplicableDynamically(linkerServices, callSiteArgType, varArgArrayType)
       
   329                     || isApplicableDynamically(linkerServices, callSiteArgType, varArgType);
       
   330         }
       
   331 
       
   332         // Either zero, or more than one vararg; check if all actual vararg types match the vararg array component type.
       
   333         for(int i = fixedArgLen; i < callSiteArgLen; ++i) {
       
   334             if(!isApplicableDynamically(linkerServices, callSiteType.parameterType(i), varArgType)) {
       
   335                 return false;
       
   336             }
       
   337         }
       
   338 
       
   339         return true;
       
   340     }
       
   341 
       
   342     private static boolean isApplicableDynamically(final LinkerServices linkerServices, final Class<?> callSiteType,
       
   343             final Class<?> methodType) {
       
   344         return isPotentiallyConvertible(callSiteType, methodType)
       
   345                 || linkerServices.canConvert(callSiteType, methodType);
       
   346     }
       
   347 
       
   348     private ApplicableOverloadedMethods getApplicables(final MethodType callSiteType, final ApplicabilityTest test) {
       
   349         return new ApplicableOverloadedMethods(methods, callSiteType, test);
       
   350     }
       
   351 
       
   352     /**
       
   353      * Add a method to this overloaded method's set.
       
   354      *
       
   355      * @param method a method to add
       
   356      */
       
   357     public void addMethod(final SingleDynamicMethod method) {
       
   358         assert constructorFlagConsistent(method);
       
   359         methods.add(method);
       
   360     }
       
   361 
       
   362     private boolean constructorFlagConsistent(final SingleDynamicMethod method) {
       
   363         return methods.isEmpty()? true : (methods.getFirst().isConstructor() == method.isConstructor());
       
   364     }
       
   365 
       
   366     /**
       
   367      * Determines whether one type can be potentially converted to another type at runtime. Allows a conversion between
       
   368      * any subtype and supertype in either direction, and also allows a conversion between any two primitive types, as
       
   369      * well as between any primitive type and any reference type that can hold a boxed primitive.
       
   370      *
       
   371      * @param callSiteType the parameter type at the call site
       
   372      * @param methodType the parameter type in the method declaration
       
   373      * @return true if callSiteType is potentially convertible to the methodType.
       
   374      */
       
   375     private static boolean isPotentiallyConvertible(final Class<?> callSiteType, final Class<?> methodType) {
       
   376         // Widening or narrowing reference conversion
       
   377         if(InternalTypeUtilities.areAssignable(callSiteType, methodType)) {
       
   378             return true;
       
   379         }
       
   380         if(callSiteType.isPrimitive()) {
       
   381             // Allow any conversion among primitives, as well as from any
       
   382             // primitive to any type that can receive a boxed primitive.
       
   383             // TODO: narrow this a bit, i.e. allow, say, boolean to Character?
       
   384             // MethodHandles.convertArguments() allows it, so we might need to
       
   385             // too.
       
   386             return methodType.isPrimitive() || isAssignableFromBoxedPrimitive(methodType);
       
   387         }
       
   388         if(methodType.isPrimitive()) {
       
   389             // Allow conversion from any reference type that can contain a
       
   390             // boxed primitive to any primitive.
       
   391             // TODO: narrow this a bit too?
       
   392             return isAssignableFromBoxedPrimitive(callSiteType);
       
   393         }
       
   394         return false;
       
   395     }
       
   396 
       
   397     private static final Set<Class<?>> PRIMITIVE_WRAPPER_TYPES = createPrimitiveWrapperTypes();
       
   398 
       
   399     private static Set<Class<?>> createPrimitiveWrapperTypes() {
       
   400         final Map<Class<?>, Class<?>> classes = new IdentityHashMap<>();
       
   401         addClassHierarchy(classes, Boolean.class);
       
   402         addClassHierarchy(classes, Byte.class);
       
   403         addClassHierarchy(classes, Character.class);
       
   404         addClassHierarchy(classes, Short.class);
       
   405         addClassHierarchy(classes, Integer.class);
       
   406         addClassHierarchy(classes, Long.class);
       
   407         addClassHierarchy(classes, Float.class);
       
   408         addClassHierarchy(classes, Double.class);
       
   409         return classes.keySet();
       
   410     }
       
   411 
       
   412     private static void addClassHierarchy(final Map<Class<?>, Class<?>> map, final Class<?> clazz) {
       
   413         if(clazz == null) {
       
   414             return;
       
   415         }
       
   416         map.put(clazz, clazz);
       
   417         addClassHierarchy(map, clazz.getSuperclass());
       
   418         for(final Class<?> itf: clazz.getInterfaces()) {
       
   419             addClassHierarchy(map, itf);
       
   420         }
       
   421     }
       
   422 
       
   423     /**
       
   424      * Returns true if the class can be assigned from any boxed primitive.
       
   425      *
       
   426      * @param clazz the class
       
   427      * @return true if the class can be assigned from any boxed primitive. Basically, it is true if the class is any
       
   428      * primitive wrapper class, or a superclass or superinterface of any primitive wrapper class.
       
   429      */
       
   430     private static boolean isAssignableFromBoxedPrimitive(final Class<?> clazz) {
       
   431         return PRIMITIVE_WRAPPER_TYPES.contains(clazz);
       
   432     }
       
   433 }