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 Redistribution and use in source and binary forms, with or without 37 modification, are permitted provided that the following conditions are 38 met: 39 * Redistributions of source code must retain the above copyright 40 notice, this list of conditions and the following disclaimer. 41 * Redistributions in binary form must reproduce the above copyright 42 notice, this list of conditions and the following disclaimer in the 43 documentation and/or other materials provided with the distribution. 44 * Neither the name of the copyright holder nor the names of 45 contributors may be used to endorse or promote products derived from 46 this software without specific prior written permission. 47 48 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 49 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 50 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 51 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER 52 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 53 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 54 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 55 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 56 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 57 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 58 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 59 */ 60 61 package jdk.dynalink.beans; 62 63 import java.lang.invoke.MethodHandle; 64 import java.lang.invoke.MethodHandles; 65 import java.lang.invoke.MethodType; 66 import java.util.ArrayList; 67 import java.util.Iterator; 68 import java.util.List; 69 import java.util.Map; 70 import java.util.concurrent.ConcurrentHashMap; 71 import jdk.dynalink.SecureLookupSupplier; 72 import jdk.dynalink.internal.InternalTypeUtilities; 73 import jdk.dynalink.linker.LinkerServices; 74 import jdk.dynalink.linker.support.Lookup; 75 76 /** 77 * Represents a subset of overloaded methods for a certain method name on a certain class. It can be either a fixarg or 78 * a vararg subset depending on the subclass. The method is for a fixed number of arguments though (as it is generated 79 * for a concrete call site). As such, all methods in the subset can be invoked with the specified number of arguments 80 * (exactly matching for fixargs, or having less than or equal fixed arguments, for varargs). 81 */ 82 class OverloadedMethod { 83 private final Map<ClassString, MethodHandle> argTypesToMethods = new ConcurrentHashMap<>(); 84 private final OverloadedDynamicMethod parent; 85 private final ClassLoader callSiteClassLoader; 86 private final MethodType callSiteType; 87 private final MethodHandle invoker; 88 private final LinkerServices linkerServices; 89 private final SecureLookupSupplier lookupSupplier; 90 private final ArrayList<MethodHandle> fixArgMethods; 91 private final ArrayList<MethodHandle> varArgMethods; 92 OverloadedMethod(final List<MethodHandle> methodHandles, final OverloadedDynamicMethod parent, final ClassLoader callSiteClassLoader, final MethodType callSiteType, final LinkerServices linkerServices, final SecureLookupSupplier lookupSupplier)93 OverloadedMethod(final List<MethodHandle> methodHandles, 94 final OverloadedDynamicMethod parent, 95 final ClassLoader callSiteClassLoader, 96 final MethodType callSiteType, 97 final LinkerServices linkerServices, 98 final SecureLookupSupplier lookupSupplier) { 99 this.parent = parent; 100 this.callSiteClassLoader = callSiteClassLoader; 101 final Class<?> commonRetType = getCommonReturnType(methodHandles); 102 this.callSiteType = callSiteType.changeReturnType(commonRetType); 103 this.linkerServices = linkerServices; 104 this.lookupSupplier = lookupSupplier; 105 106 fixArgMethods = new ArrayList<>(methodHandles.size()); 107 varArgMethods = new ArrayList<>(methodHandles.size()); 108 final int argNum = callSiteType.parameterCount(); 109 for(final MethodHandle mh: methodHandles) { 110 if(mh.isVarargsCollector()) { 111 final MethodHandle asFixed = mh.asFixedArity(); 112 if(argNum == asFixed.type().parameterCount()) { 113 fixArgMethods.add(asFixed); 114 } 115 varArgMethods.add(mh); 116 } else { 117 fixArgMethods.add(mh); 118 } 119 } 120 fixArgMethods.trimToSize(); 121 varArgMethods.trimToSize(); 122 123 final MethodHandle bound = SELECT_METHOD.bindTo(this); 124 final MethodHandle collecting = SingleDynamicMethod.collectArguments(bound, argNum).asType( 125 callSiteType.changeReturnType(MethodHandle.class)); 126 invoker = linkerServices.asTypeLosslessReturn(MethodHandles.foldArguments( 127 MethodHandles.exactInvoker(this.callSiteType), collecting), callSiteType); 128 } 129 getInvoker()130 MethodHandle getInvoker() { 131 return invoker; 132 } 133 134 private static final MethodHandle SELECT_METHOD = Lookup.findOwnSpecial(MethodHandles.lookup(), "selectMethod", 135 MethodHandle.class, Object[].class); 136 137 @SuppressWarnings("unused") selectMethod(final Object[] args)138 private MethodHandle selectMethod(final Object[] args) throws NoSuchMethodException { 139 final Class<?>[] argTypes = new Class<?>[args.length]; 140 for(int i = 0; i < argTypes.length; ++i) { 141 final Object arg = args[i]; 142 argTypes[i] = arg == null ? ClassString.NULL_CLASS : arg.getClass(); 143 } 144 final ClassString classString = new ClassString(argTypes); 145 MethodHandle method = argTypesToMethods.get(classString); 146 if(method == null) { 147 List<MethodHandle> methods = classString.getMaximallySpecifics(fixArgMethods, linkerServices, false); 148 if(methods.isEmpty()) { 149 methods = classString.getMaximallySpecifics(varArgMethods, linkerServices, true); 150 } 151 switch(methods.size()) { 152 case 0: { 153 method = getNoSuchMethodThrower(argTypes); 154 break; 155 } 156 case 1: { 157 final List<MethodHandle> fmethods = methods; 158 method = linkerServices.getWithLookup( 159 ()->SingleDynamicMethod.getInvocation(fmethods.get(0), callSiteType, linkerServices), 160 lookupSupplier); 161 break; 162 } 163 default: { 164 // This is unfortunate - invocation time ambiguity. 165 method = getAmbiguousMethodThrower(argTypes, methods); 166 break; 167 } 168 } 169 // Avoid keeping references to unrelated classes; this ruins the 170 // performance a bit, but avoids class loader memory leaks. 171 if(classString.isVisibleFrom(callSiteClassLoader)) { 172 argTypesToMethods.put(classString, method); 173 } 174 } 175 return method; 176 } 177 getNoSuchMethodThrower(final Class<?>[] argTypes)178 private MethodHandle getNoSuchMethodThrower(final Class<?>[] argTypes) { 179 return adaptThrower(MethodHandles.insertArguments(THROW_NO_SUCH_METHOD, 0, this, argTypes)); 180 } 181 182 private static final MethodHandle THROW_NO_SUCH_METHOD = Lookup.findOwnSpecial(MethodHandles.lookup(), 183 "throwNoSuchMethod", void.class, Class[].class); 184 185 @SuppressWarnings("unused") throwNoSuchMethod(final Class<?>[] argTypes)186 private void throwNoSuchMethod(final Class<?>[] argTypes) throws NoSuchMethodException { 187 if(varArgMethods.isEmpty()) { 188 throw new NoSuchMethodException("None of the fixed arity signatures " + getSignatureList(fixArgMethods) + 189 " of method " + parent.getName() + " match the argument types " + argTypesString(argTypes)); 190 } 191 throw new NoSuchMethodException("None of the fixed arity signatures " + getSignatureList(fixArgMethods) + 192 " or the variable arity signatures " + getSignatureList(varArgMethods) + " of the method " + 193 parent.getName() + " match the argument types " + argTypesString(argTypes)); 194 } 195 getAmbiguousMethodThrower(final Class<?>[] argTypes, final List<MethodHandle> methods)196 private MethodHandle getAmbiguousMethodThrower(final Class<?>[] argTypes, final List<MethodHandle> methods) { 197 return adaptThrower(MethodHandles.insertArguments(THROW_AMBIGUOUS_METHOD, 0, this, argTypes, methods)); 198 } 199 adaptThrower(final MethodHandle rawThrower)200 private MethodHandle adaptThrower(final MethodHandle rawThrower) { 201 return MethodHandles.dropArguments(rawThrower, 0, callSiteType.parameterList()).asType(callSiteType); 202 } 203 204 private static final MethodHandle THROW_AMBIGUOUS_METHOD = Lookup.findOwnSpecial(MethodHandles.lookup(), 205 "throwAmbiguousMethod", void.class, Class[].class, List.class); 206 207 @SuppressWarnings("unused") throwAmbiguousMethod(final Class<?>[] argTypes, final List<MethodHandle> methods)208 private void throwAmbiguousMethod(final Class<?>[] argTypes, final List<MethodHandle> methods) throws NoSuchMethodException { 209 final String arity = methods.get(0).isVarargsCollector() ? "variable" : "fixed"; 210 throw new NoSuchMethodException("Can't unambiguously select between " + arity + " arity signatures " + 211 getSignatureList(methods) + " of the method " + parent.getName() + " for argument types " + 212 argTypesString(argTypes)); 213 } 214 argTypesString(final Class<?>[] classes)215 private static String argTypesString(final Class<?>[] classes) { 216 final StringBuilder b = new StringBuilder().append('['); 217 appendTypes(b, classes, false); 218 return b.append(']').toString(); 219 } 220 getSignatureList(final List<MethodHandle> methods)221 private static String getSignatureList(final List<MethodHandle> methods) { 222 final StringBuilder b = new StringBuilder().append('['); 223 final Iterator<MethodHandle> it = methods.iterator(); 224 if(it.hasNext()) { 225 appendSig(b, it.next()); 226 while(it.hasNext()) { 227 appendSig(b.append(", "), it.next()); 228 } 229 } 230 return b.append(']').toString(); 231 } 232 appendSig(final StringBuilder b, final MethodHandle m)233 private static void appendSig(final StringBuilder b, final MethodHandle m) { 234 b.append('('); 235 appendTypes(b, m.type().parameterArray(), m.isVarargsCollector()); 236 b.append(')'); 237 } 238 appendTypes(final StringBuilder b, final Class<?>[] classes, final boolean varArg)239 private static void appendTypes(final StringBuilder b, final Class<?>[] classes, final boolean varArg) { 240 final int l = classes.length; 241 if(!varArg) { 242 if(l > 1) { 243 b.append(classes[1].getCanonicalName()); 244 for(int i = 2; i < l; ++i) { 245 b.append(", ").append(classes[i].getCanonicalName()); 246 } 247 } 248 } else { 249 for(int i = 1; i < l - 1; ++i) { 250 b.append(classes[i].getCanonicalName()).append(", "); 251 } 252 b.append(classes[l - 1].getComponentType().getCanonicalName()).append("..."); 253 } 254 } 255 getCommonReturnType(final List<MethodHandle> methodHandles)256 private static Class<?> getCommonReturnType(final List<MethodHandle> methodHandles) { 257 final Iterator<MethodHandle> it = methodHandles.iterator(); 258 Class<?> retType = it.next().type().returnType(); 259 while(it.hasNext()) { 260 retType = InternalTypeUtilities.getCommonLosslessConversionType(retType, it.next().type().returnType()); 261 } 262 return retType; 263 } 264 } 265