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 84package jdk.dynalink.beans; 85 86import java.lang.invoke.MethodHandle; 87import java.lang.invoke.MethodType; 88import java.util.Iterator; 89import java.util.LinkedList; 90import java.util.List; 91import jdk.dynalink.linker.ConversionComparator.Comparison; 92import jdk.dynalink.linker.LinkerServices; 93import jdk.dynalink.linker.support.TypeUtilities; 94 95/** 96 * Utility class that encapsulates the algorithm for choosing the maximally specific methods. 97 */ 98class MaximallySpecific { 99 /** 100 * Given a list of methods, returns a list of maximally specific methods. 101 * 102 * @param methods the list of methods 103 * @param varArgs whether to assume the methods are varargs 104 * @return the list of maximally specific methods. 105 */ 106 static List<SingleDynamicMethod> getMaximallySpecificMethods(final List<SingleDynamicMethod> methods, final boolean varArgs) { 107 return getMaximallySpecificSingleDynamicMethods(methods, varArgs, null, null); 108 } 109 110 private abstract static class MethodTypeGetter<T> { 111 abstract MethodType getMethodType(T t); 112 } 113 114 private static final MethodTypeGetter<MethodHandle> METHOD_HANDLE_TYPE_GETTER = 115 new MethodTypeGetter<MethodHandle>() { 116 @Override 117 MethodType getMethodType(final MethodHandle t) { 118 return t.type(); 119 } 120 }; 121 122 private static final MethodTypeGetter<SingleDynamicMethod> DYNAMIC_METHOD_TYPE_GETTER = 123 new MethodTypeGetter<SingleDynamicMethod>() { 124 @Override 125 MethodType getMethodType(final SingleDynamicMethod t) { 126 return t.getMethodType(); 127 } 128 }; 129 130 /** 131 * Given a list of methods handles, returns a list of maximally specific methods, applying language-runtime 132 * specific conversion preferences. 133 * 134 * @param methods the list of method handles 135 * @param varArgs whether to assume the method handles are varargs 136 * @param argTypes concrete argument types for the invocation 137 * @return the list of maximally specific method handles. 138 */ 139 static List<MethodHandle> getMaximallySpecificMethodHandles(final List<MethodHandle> methods, final boolean varArgs, 140 final Class<?>[] argTypes, final LinkerServices ls) { 141 return getMaximallySpecificMethods(methods, varArgs, argTypes, ls, METHOD_HANDLE_TYPE_GETTER); 142 } 143 144 /** 145 * Given a list of methods, returns a list of maximally specific methods, applying language-runtime specific 146 * conversion preferences. 147 * 148 * @param methods the list of methods 149 * @param varArgs whether to assume the methods are varargs 150 * @param argTypes concrete argument types for the invocation 151 * @return the list of maximally specific methods. 152 */ 153 static List<SingleDynamicMethod> getMaximallySpecificSingleDynamicMethods(final List<SingleDynamicMethod> methods, 154 final boolean varArgs, final Class<?>[] argTypes, final LinkerServices ls) { 155 return getMaximallySpecificMethods(methods, varArgs, argTypes, ls, DYNAMIC_METHOD_TYPE_GETTER); 156 } 157 158 /** 159 * Given a list of methods, returns a list of maximally specific methods, applying language-runtime specific 160 * conversion preferences. 161 * 162 * @param methods the list of methods 163 * @param varArgs whether to assume the methods are varargs 164 * @param argTypes concrete argument types for the invocation 165 * @return the list of maximally specific methods. 166 */ 167 private static <T> List<T> getMaximallySpecificMethods(final List<T> methods, final boolean varArgs, 168 final Class<?>[] argTypes, final LinkerServices ls, final MethodTypeGetter<T> methodTypeGetter) { 169 if(methods.size() < 2) { 170 return methods; 171 } 172 final LinkedList<T> maximals = new LinkedList<>(); 173 for(final T m: methods) { 174 final MethodType methodType = methodTypeGetter.getMethodType(m); 175 boolean lessSpecific = false; 176 for(final Iterator<T> maximal = maximals.iterator(); maximal.hasNext();) { 177 final T max = maximal.next(); 178 switch(isMoreSpecific(methodType, methodTypeGetter.getMethodType(max), varArgs, argTypes, ls)) { 179 case TYPE_1_BETTER: { 180 maximal.remove(); 181 break; 182 } 183 case TYPE_2_BETTER: { 184 lessSpecific = true; 185 break; 186 } 187 case INDETERMINATE: { 188 // do nothing 189 break; 190 } 191 default: { 192 throw new AssertionError(); 193 } 194 } 195 } 196 if(!lessSpecific) { 197 maximals.addLast(m); 198 } 199 } 200 return maximals; 201 } 202 203 private static Comparison isMoreSpecific(final MethodType t1, final MethodType t2, final boolean varArgs, final Class<?>[] argTypes, 204 final LinkerServices ls) { 205 final int pc1 = t1.parameterCount(); 206 final int pc2 = t2.parameterCount(); 207 assert varArgs || (pc1 == pc2) && (argTypes == null || argTypes.length == pc1); 208 assert (argTypes == null) == (ls == null); 209 final int maxPc = Math.max(Math.max(pc1, pc2), argTypes == null ? 0 : argTypes.length); 210 boolean t1MoreSpecific = false; 211 boolean t2MoreSpecific = false; 212 // NOTE: Starting from 1 as overloaded method resolution doesn't depend on 0th element, which is the type of 213 // 'this'. We're only dealing with instance methods here, not static methods. Actually, static methods will have 214 // a fake 'this' of type StaticClass. 215 for(int i = 1; i < maxPc; ++i) { 216 final Class<?> c1 = getParameterClass(t1, pc1, i, varArgs); 217 final Class<?> c2 = getParameterClass(t2, pc2, i, varArgs); 218 if(c1 != c2) { 219 final Comparison cmp = compare(c1, c2, argTypes, i, ls); 220 if(cmp == Comparison.TYPE_1_BETTER && !t1MoreSpecific) { 221 t1MoreSpecific = true; 222 if(t2MoreSpecific) { 223 return Comparison.INDETERMINATE; 224 } 225 } 226 if(cmp == Comparison.TYPE_2_BETTER && !t2MoreSpecific) { 227 t2MoreSpecific = true; 228 if(t1MoreSpecific) { 229 return Comparison.INDETERMINATE; 230 } 231 } 232 } 233 } 234 if(t1MoreSpecific) { 235 return Comparison.TYPE_1_BETTER; 236 } else if(t2MoreSpecific) { 237 return Comparison.TYPE_2_BETTER; 238 } 239 return Comparison.INDETERMINATE; 240 } 241 242 private static Comparison compare(final Class<?> c1, final Class<?> c2, final Class<?>[] argTypes, final int i, final LinkerServices cmp) { 243 if(cmp != null) { 244 final Comparison c = cmp.compareConversion(argTypes[i], c1, c2); 245 if(c != Comparison.INDETERMINATE) { 246 return c; 247 } 248 } 249 if(TypeUtilities.isSubtype(c1, c2)) { 250 return Comparison.TYPE_1_BETTER; 251 } if(TypeUtilities.isSubtype(c2, c1)) { 252 return Comparison.TYPE_2_BETTER; 253 } 254 return Comparison.INDETERMINATE; 255 } 256 257 private static Class<?> getParameterClass(final MethodType t, final int l, final int i, final boolean varArgs) { 258 return varArgs && i >= l - 1 ? t.parameterType(l - 1).getComponentType() : t.parameterType(i); 259 } 260} 261