Path: blob/master/src/jdk.dynalink/share/classes/jdk/dynalink/beans/OverloadedDynamicMethod.java
41161 views
/*1* Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26* This file is available under and governed by the GNU General Public27* License version 2 only, as published by the Free Software Foundation.28* However, the following notice accompanied the original version of this29* file, and Oracle licenses the original version of this file under the BSD30* license:31*/32/*33Copyright 2009-2013 Attila Szegedi3435Redistribution and use in source and binary forms, with or without36modification, are permitted provided that the following conditions are37met:38* Redistributions of source code must retain the above copyright39notice, this list of conditions and the following disclaimer.40* Redistributions in binary form must reproduce the above copyright41notice, this list of conditions and the following disclaimer in the42documentation and/or other materials provided with the distribution.43* Neither the name of the copyright holder nor the names of44contributors may be used to endorse or promote products derived from45this software without specific prior written permission.4647THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS48IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED49TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A50PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER51BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR52CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF53SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR54BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,55WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR56OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF57ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.58*/5960package jdk.dynalink.beans;6162import java.lang.invoke.MethodHandle;63import java.lang.invoke.MethodType;64import java.security.AccessControlContext;65import java.security.AccessController;66import java.security.PrivilegedAction;67import java.text.Collator;68import java.util.ArrayList;69import java.util.IdentityHashMap;70import java.util.LinkedList;71import java.util.List;72import java.util.Map;73import java.util.Set;74import jdk.dynalink.CallSiteDescriptor;75import jdk.dynalink.SecureLookupSupplier;76import jdk.dynalink.beans.ApplicableOverloadedMethods.ApplicabilityTest;77import jdk.dynalink.internal.AccessControlContextFactory;78import jdk.dynalink.internal.InternalTypeUtilities;79import jdk.dynalink.linker.LinkerServices;8081/**82* Represents a group of {@link SingleDynamicMethod} objects that represents all overloads of a particular name (or all83* constructors) for a particular class. Correctly handles overload resolution, variable arity methods, and caller84* sensitive methods within the overloads.85*/86class OverloadedDynamicMethod extends DynamicMethod {87/**88* Holds a list of all methods.89*/90private final LinkedList<SingleDynamicMethod> methods = new LinkedList<>();9192/**93* Creates a new overloaded dynamic method.94*95* @param clazz the class this method belongs to96* @param name the name of the method97*/98OverloadedDynamicMethod(final Class<?> clazz, final String name) {99super(getClassAndMethodName(clazz, name));100}101102@Override103SingleDynamicMethod getMethodForExactParamTypes(final String paramTypes) {104final LinkedList<SingleDynamicMethod> matchingMethods = new LinkedList<>();105for(final SingleDynamicMethod method: methods) {106final SingleDynamicMethod matchingMethod = method.getMethodForExactParamTypes(paramTypes);107if(matchingMethod != null) {108matchingMethods.add(matchingMethod);109}110}111switch(matchingMethods.size()) {112case 0: {113return null;114}115case 1: {116return matchingMethods.getFirst();117}118default: {119throw new BootstrapMethodError("Can't choose among " + matchingMethods + " for argument types "120+ paramTypes + " for method " + getName());121}122}123}124125@Override126MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {127final MethodType callSiteType = callSiteDescriptor.getMethodType();128// First, find all methods applicable to the call site by subtyping (JLS 15.12.2.2)129final ApplicableOverloadedMethods subtypingApplicables = getApplicables(callSiteType,130ApplicableOverloadedMethods.APPLICABLE_BY_SUBTYPING);131// Next, find all methods applicable by method invocation conversion to the call site (JLS 15.12.2.3).132final ApplicableOverloadedMethods methodInvocationApplicables = getApplicables(callSiteType,133ApplicableOverloadedMethods.APPLICABLE_BY_METHOD_INVOCATION_CONVERSION);134// Finally, find all methods applicable by variable arity invocation. (JLS 15.12.2.4).135final ApplicableOverloadedMethods variableArityApplicables = getApplicables(callSiteType,136ApplicableOverloadedMethods.APPLICABLE_BY_VARIABLE_ARITY);137138// Find the methods that are maximally specific based on the call site signature139List<SingleDynamicMethod> maximallySpecifics = subtypingApplicables.findMaximallySpecificMethods();140if(maximallySpecifics.isEmpty()) {141maximallySpecifics = methodInvocationApplicables.findMaximallySpecificMethods();142if(maximallySpecifics.isEmpty()) {143maximallySpecifics = variableArityApplicables.findMaximallySpecificMethods();144}145}146147// Now, get a list of the rest of the methods; those that are *not* applicable to the call site signature based148// on JLS rules. As paradoxical as that might sound, we have to consider these for dynamic invocation, as they149// might match more concrete types passed in invocations. That's why we provisionally call them "invokables".150// This is typical for very generic signatures at call sites. Typical example: call site specifies151// (Object, Object), and we have a method whose parameter types are (String, int). None of the JLS applicability152// rules will trigger, but we must consider the method, as it can be the right match for a concrete invocation.153@SuppressWarnings({ "unchecked", "rawtypes" })154final List<SingleDynamicMethod> invokables = (List)methods.clone();155invokables.removeAll(subtypingApplicables.getMethods());156invokables.removeAll(methodInvocationApplicables.getMethods());157invokables.removeAll(variableArityApplicables.getMethods());158invokables.removeIf(m -> !isApplicableDynamically(linkerServices, callSiteType, m));159160// If no additional methods can apply at invocation time, and there's more than one maximally specific method161// based on call site signature, that is a link-time ambiguity. In a static scenario, javac would report an162// ambiguity error.163if(invokables.isEmpty() && maximallySpecifics.size() > 1) {164throw new BootstrapMethodError("Can't choose among " + maximallySpecifics + " for argument types "165+ callSiteType);166}167168// Merge them all.169invokables.addAll(maximallySpecifics);170switch(invokables.size()) {171case 0: {172// No overloads can ever match the call site type173return null;174}175case 1: {176// Very lucky, we ended up with a single candidate method handle based on the call site signature; we177// can link it very simply by delegating to the SingleDynamicMethod.178return invokables.iterator().next().getInvocation(callSiteDescriptor, linkerServices);179}180default: {181// We have more than one candidate. We have no choice but to link to a method that resolves overloads on182// every invocation (alternatively, we could opportunistically link the one method that resolves for the183// current arguments, but we'd need to install a fairly complex guard for that and when it'd fail, we'd184// go back all the way to candidate selection. Note that we're resolving any potential caller sensitive185// methods here to their handles, as the OverloadedMethod instance is specific to a call site, so it186// has an already determined Lookup.187final List<MethodHandle> methodHandles = new ArrayList<>(invokables.size());188for(final SingleDynamicMethod method: invokables) {189methodHandles.add(method.getTarget(callSiteDescriptor));190}191return new OverloadedMethod(methodHandles, this, getCallSiteClassLoader(callSiteDescriptor), callSiteType, linkerServices, callSiteDescriptor).getInvoker();192}193}194}195196@SuppressWarnings("removal")197private static final AccessControlContext GET_CALL_SITE_CLASS_LOADER_CONTEXT =198AccessControlContextFactory.createAccessControlContext(199"getClassLoader", SecureLookupSupplier.GET_LOOKUP_PERMISSION_NAME);200201@SuppressWarnings("removal")202private static ClassLoader getCallSiteClassLoader(final CallSiteDescriptor callSiteDescriptor) {203return AccessController.doPrivileged(204(PrivilegedAction<ClassLoader>) () -> callSiteDescriptor.getLookup().lookupClass().getClassLoader(),205GET_CALL_SITE_CLASS_LOADER_CONTEXT);206}207208@Override209public boolean contains(final SingleDynamicMethod m) {210for(final SingleDynamicMethod method: methods) {211if(method.contains(m)) {212return true;213}214}215return false;216}217218@Override219public boolean isConstructor() {220assert !methods.isEmpty();221return methods.getFirst().isConstructor();222}223224@Override225public String toString() {226// First gather the names and sort them. This makes it consistent and easier to read.227final List<String> names = new ArrayList<>(methods.size());228int len = 0;229for (final SingleDynamicMethod m: methods) {230final String name = m.getName();231len += name.length();232names.add(name);233}234// Case insensitive sorting, so e.g. "Object" doesn't come before "boolean".235final Collator collator = Collator.getInstance();236collator.setStrength(Collator.SECONDARY);237names.sort(collator);238239final String className = getClass().getName();240// Class name length + length of signatures + 2 chars/per signature for indentation and newline +241// 3 for brackets and initial newline242final int totalLength = className.length() + len + 2 * names.size() + 3;243final StringBuilder b = new StringBuilder(totalLength);244b.append('[').append(className).append('\n');245for(final String name: names) {246b.append(' ').append(name).append('\n');247}248b.append(']');249assert b.length() == totalLength;250return b.toString();251}252253private static boolean isApplicableDynamically(final LinkerServices linkerServices, final MethodType callSiteType,254final SingleDynamicMethod m) {255final MethodType methodType = m.getMethodType();256final boolean varArgs = m.isVarArgs();257final int fixedArgLen = methodType.parameterCount() - (varArgs ? 1 : 0);258final int callSiteArgLen = callSiteType.parameterCount();259260// Arity checks261if(varArgs) {262if(callSiteArgLen < fixedArgLen) {263return false;264}265} else if(callSiteArgLen != fixedArgLen) {266return false;267}268269// Fixed arguments type checks, starting from 1, as receiver type doesn't participate270for(int i = 1; i < fixedArgLen; ++i) {271if(!isApplicableDynamically(linkerServices, callSiteType.parameterType(i), methodType.parameterType(i))) {272return false;273}274}275if(!varArgs) {276// Not vararg; both arity and types matched.277return true;278}279280final Class<?> varArgArrayType = methodType.parameterType(fixedArgLen);281final Class<?> varArgType = varArgArrayType.getComponentType();282283if(fixedArgLen == callSiteArgLen - 1) {284// Exactly one vararg; check both array type matching and array component type matching.285final Class<?> callSiteArgType = callSiteType.parameterType(fixedArgLen);286return isApplicableDynamically(linkerServices, callSiteArgType, varArgArrayType)287|| isApplicableDynamically(linkerServices, callSiteArgType, varArgType);288}289290// Either zero, or more than one vararg; check if all actual vararg types match the vararg array component type.291for(int i = fixedArgLen; i < callSiteArgLen; ++i) {292if(!isApplicableDynamically(linkerServices, callSiteType.parameterType(i), varArgType)) {293return false;294}295}296297return true;298}299300private static boolean isApplicableDynamically(final LinkerServices linkerServices, final Class<?> callSiteType,301final Class<?> methodType) {302return isPotentiallyConvertible(callSiteType, methodType)303|| linkerServices.canConvert(callSiteType, methodType);304}305306private ApplicableOverloadedMethods getApplicables(final MethodType callSiteType, final ApplicabilityTest test) {307return new ApplicableOverloadedMethods(methods, callSiteType, test);308}309310/**311* Add a method to this overloaded method's set.312*313* @param method a method to add314*/315public void addMethod(final SingleDynamicMethod method) {316assert constructorFlagConsistent(method);317methods.add(method);318}319320private boolean constructorFlagConsistent(final SingleDynamicMethod method) {321return methods.isEmpty() || methods.getFirst().isConstructor() == method.isConstructor();322}323324/**325* Determines whether one type can be potentially converted to another type at runtime. Allows a conversion between326* any subtype and supertype in either direction, and also allows a conversion between any two primitive types, as327* well as between any primitive type and any reference type that can hold a boxed primitive.328*329* @param callSiteType the parameter type at the call site330* @param methodType the parameter type in the method declaration331* @return true if callSiteType is potentially convertible to the methodType.332*/333private static boolean isPotentiallyConvertible(final Class<?> callSiteType, final Class<?> methodType) {334// Widening or narrowing reference conversion335if(InternalTypeUtilities.areAssignable(callSiteType, methodType)) {336return true;337}338if(callSiteType.isPrimitive()) {339// Allow any conversion among primitives, as well as from any340// primitive to any type that can receive a boxed primitive.341// TODO: narrow this a bit, i.e. allow, say, boolean to Character?342// MethodHandles.convertArguments() allows it, so we might need to343// too.344return methodType.isPrimitive() || isAssignableFromBoxedPrimitive(methodType);345}346if(methodType.isPrimitive()) {347// Allow conversion from any reference type that can contain a348// boxed primitive to any primitive.349// TODO: narrow this a bit too?350return isAssignableFromBoxedPrimitive(callSiteType);351}352return false;353}354355private static final Set<Class<?>> PRIMITIVE_WRAPPER_TYPES = createPrimitiveWrapperTypes();356357private static Set<Class<?>> createPrimitiveWrapperTypes() {358final Map<Class<?>, Class<?>> classes = new IdentityHashMap<>();359addClassHierarchy(classes, Boolean.class);360addClassHierarchy(classes, Byte.class);361addClassHierarchy(classes, Character.class);362addClassHierarchy(classes, Short.class);363addClassHierarchy(classes, Integer.class);364addClassHierarchy(classes, Long.class);365addClassHierarchy(classes, Float.class);366addClassHierarchy(classes, Double.class);367return classes.keySet();368}369370private static void addClassHierarchy(final Map<Class<?>, Class<?>> map, final Class<?> clazz) {371if(clazz == null) {372return;373}374map.put(clazz, clazz);375addClassHierarchy(map, clazz.getSuperclass());376for(final Class<?> itf: clazz.getInterfaces()) {377addClassHierarchy(map, itf);378}379}380381/**382* Returns true if the class can be assigned from any boxed primitive.383*384* @param clazz the class385* @return true if the class can be assigned from any boxed primitive. Basically, it is true if the class is any386* primitive wrapper class, or a superclass or superinterface of any primitive wrapper class.387*/388private static boolean isAssignableFromBoxedPrimitive(final Class<?> clazz) {389return PRIMITIVE_WRAPPER_TYPES.contains(clazz);390}391}392393394