OverloadedDynamicMethod.java revision 1551:f3b883bec2d0
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.security.AccessControlContext; 89import java.security.AccessController; 90import java.security.PrivilegedAction; 91import java.text.Collator; 92import java.util.ArrayList; 93import java.util.Collections; 94import java.util.IdentityHashMap; 95import java.util.Iterator; 96import java.util.LinkedList; 97import java.util.List; 98import java.util.Map; 99import java.util.Set; 100import jdk.dynalink.CallSiteDescriptor; 101import jdk.dynalink.beans.ApplicableOverloadedMethods.ApplicabilityTest; 102import jdk.dynalink.internal.AccessControlContextFactory; 103import jdk.dynalink.internal.InternalTypeUtilities; 104import jdk.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 */ 111class 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} 434