1/* 2 * Copyright (c) 2010, 2016, 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; 85 86import java.lang.invoke.MethodHandle; 87import java.lang.invoke.MethodHandles; 88import java.lang.invoke.MethodHandles.Lookup; 89import java.lang.invoke.MethodType; 90import java.lang.invoke.WrongMethodTypeException; 91import java.security.AccessControlContext; 92import java.security.AccessController; 93import java.security.PrivilegedAction; 94import java.util.LinkedList; 95import java.util.List; 96import java.util.function.Supplier; 97import jdk.dynalink.internal.AccessControlContextFactory; 98import jdk.dynalink.linker.ConversionComparator; 99import jdk.dynalink.linker.ConversionComparator.Comparison; 100import jdk.dynalink.linker.GuardedInvocation; 101import jdk.dynalink.linker.GuardingTypeConverterFactory; 102import jdk.dynalink.linker.LinkerServices; 103import jdk.dynalink.linker.MethodTypeConversionStrategy; 104import jdk.dynalink.linker.support.TypeUtilities; 105 106/** 107 * A factory for type converters. This class is the main implementation behind the 108 * {@link LinkerServices#asType(MethodHandle, MethodType)}. It manages the known {@link GuardingTypeConverterFactory} 109 * instances and creates appropriate converters for method handles. 110 */ 111final class TypeConverterFactory { 112 private static final AccessControlContext GET_CLASS_LOADER_CONTEXT = 113 AccessControlContextFactory.createAccessControlContext("getClassLoader"); 114 115 private final GuardingTypeConverterFactory[] factories; 116 private final ConversionComparator[] comparators; 117 private final MethodTypeConversionStrategy autoConversionStrategy; 118 119 private final ClassValue<ClassMap<MethodHandle>> converterMap = new ClassValue<ClassMap<MethodHandle>>() { 120 @Override 121 protected ClassMap<MethodHandle> computeValue(final Class<?> sourceType) { 122 return new ClassMap<MethodHandle>(getClassLoader(sourceType)) { 123 @Override 124 protected MethodHandle computeValue(final Class<?> targetType) { 125 try { 126 return createConverter(sourceType, targetType); 127 } catch (final RuntimeException e) { 128 throw e; 129 } catch (final Exception e) { 130 throw new RuntimeException(e); 131 } 132 } 133 }; 134 } 135 }; 136 137 private final ClassValue<ClassMap<MethodHandle>> converterIdentityMap = new ClassValue<ClassMap<MethodHandle>>() { 138 @Override 139 protected ClassMap<MethodHandle> computeValue(final Class<?> sourceType) { 140 return new ClassMap<MethodHandle>(getClassLoader(sourceType)) { 141 @Override 142 protected MethodHandle computeValue(final Class<?> targetType) { 143 if(!canAutoConvert(sourceType, targetType)) { 144 final MethodHandle converter = getCacheableTypeConverter(sourceType, targetType); 145 if(converter != IDENTITY_CONVERSION) { 146 return converter; 147 } 148 } 149 return IDENTITY_CONVERSION.asType(MethodType.methodType(targetType, sourceType)); 150 } 151 }; 152 } 153 }; 154 155 private final ClassValue<ClassMap<Boolean>> canConvert = new ClassValue<ClassMap<Boolean>>() { 156 @Override 157 protected ClassMap<Boolean> computeValue(final Class<?> sourceType) { 158 return new ClassMap<Boolean>(getClassLoader(sourceType)) { 159 @Override 160 protected Boolean computeValue(final Class<?> targetType) { 161 try { 162 return getTypeConverterNull(sourceType, targetType) != null; 163 } catch (final RuntimeException e) { 164 throw e; 165 } catch (final Exception e) { 166 throw new RuntimeException(e); 167 } 168 } 169 }; 170 } 171 }; 172 173 private static ClassLoader getClassLoader(final Class<?> clazz) { 174 return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { 175 @Override 176 public ClassLoader run() { 177 return clazz.getClassLoader(); 178 } 179 }, GET_CLASS_LOADER_CONTEXT); 180 } 181 182 /** 183 * Creates a new type converter factory from the available {@link GuardingTypeConverterFactory} instances. 184 * 185 * @param factories the {@link GuardingTypeConverterFactory} instances to compose. 186 * @param autoConversionStrategy conversion strategy for automatic type conversions. After 187 * {@link #asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)} has applied all custom 188 * conversions to a method handle, it still needs to effect 189 * {@link TypeUtilities#isMethodInvocationConvertible(Class, Class) method invocation conversions} that 190 * can usually be automatically applied as per 191 * {@link java.lang.invoke.MethodHandle#asType(java.lang.invoke.MethodType)}. 192 * However, sometimes language runtimes will want to customize even those conversions for their own call 193 * sites. A typical example is allowing unboxing of null return values, which is by default prohibited by 194 * ordinary {@code MethodHandles.asType}. In this case, a language runtime can install its own custom 195 * automatic conversion strategy, that can deal with null values. Note that when the strategy's 196 * {@link MethodTypeConversionStrategy#asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)} 197 * is invoked, the custom language conversions will already have been applied to the method handle, so by 198 * design the difference between the handle's current method type and the desired final type will always 199 * only be ones that can be subjected to method invocation conversions. Can be null, in which case no 200 * custom strategy is employed. 201 */ 202 TypeConverterFactory(final Iterable<? extends GuardingTypeConverterFactory> factories, 203 final MethodTypeConversionStrategy autoConversionStrategy) { 204 final List<GuardingTypeConverterFactory> l = new LinkedList<>(); 205 final List<ConversionComparator> c = new LinkedList<>(); 206 for(final GuardingTypeConverterFactory factory: factories) { 207 l.add(factory); 208 if(factory instanceof ConversionComparator) { 209 c.add((ConversionComparator)factory); 210 } 211 } 212 this.factories = l.toArray(new GuardingTypeConverterFactory[0]); 213 this.comparators = c.toArray(new ConversionComparator[0]); 214 this.autoConversionStrategy = autoConversionStrategy; 215 } 216 217 /** 218 * Similar to {@link MethodHandle#asType(MethodType)} except it also hooks in method handles produced by 219 * {@link GuardingTypeConverterFactory} implementations, providing for language-specific type coercing of 220 * parameters. For all conversions that are not a JLS method invocation conversion it'll insert 221 * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with composite filters 222 * provided by {@link GuardingTypeConverterFactory} implementations. For the remaining JLS method invocation 223 * conversions, it will invoke {@link MethodTypeConversionStrategy#asType(MethodHandle, MethodType)} first 224 * if an automatic conversion strategy was specified in the 225 * {@link #TypeConverterFactory(Iterable, MethodTypeConversionStrategy) constructor}, and finally apply 226 * {@link MethodHandle#asType(MethodType)} for any remaining conversions. 227 * 228 * @param handle target method handle 229 * @param fromType the types of source arguments 230 * @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)}, 231 * {@link MethodTypeConversionStrategy#asType(MethodHandle, MethodType)}, and 232 * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with 233 * {@link GuardingTypeConverterFactory} produced type converters as filters. 234 */ 235 MethodHandle asType(final MethodHandle handle, final MethodType fromType) { 236 MethodHandle newHandle = handle; 237 final MethodType toType = newHandle.type(); 238 final int l = toType.parameterCount(); 239 if(l != fromType.parameterCount()) { 240 throw new WrongMethodTypeException("Parameter counts differ: " + handle.type() + " vs. " + fromType); 241 } 242 int pos = 0; 243 final List<MethodHandle> converters = new LinkedList<>(); 244 for(int i = 0; i < l; ++i) { 245 final Class<?> fromParamType = fromType.parameterType(i); 246 final Class<?> toParamType = toType.parameterType(i); 247 if(canAutoConvert(fromParamType, toParamType)) { 248 newHandle = applyConverters(newHandle, pos, converters); 249 } else { 250 final MethodHandle converter = getTypeConverterNull(fromParamType, toParamType); 251 if(converter != null) { 252 if(converters.isEmpty()) { 253 pos = i; 254 } 255 converters.add(converter); 256 } else { 257 newHandle = applyConverters(newHandle, pos, converters); 258 } 259 } 260 } 261 newHandle = applyConverters(newHandle, pos, converters); 262 263 // Convert return type 264 final Class<?> fromRetType = fromType.returnType(); 265 final Class<?> toRetType = toType.returnType(); 266 if(fromRetType != Void.TYPE && toRetType != Void.TYPE) { 267 if(!canAutoConvert(toRetType, fromRetType)) { 268 final MethodHandle converter = getTypeConverterNull(toRetType, fromRetType); 269 if(converter != null) { 270 newHandle = MethodHandles.filterReturnValue(newHandle, converter); 271 } 272 } 273 } 274 275 // Give change to automatic conversion strategy, if one is present. 276 final MethodHandle autoConvertedHandle = 277 autoConversionStrategy != null ? autoConversionStrategy.asType(newHandle, fromType) : newHandle; 278 279 // Do a final asType for any conversions that remain. 280 return autoConvertedHandle.asType(fromType); 281 } 282 283 private static MethodHandle applyConverters(final MethodHandle handle, final int pos, final List<MethodHandle> converters) { 284 if(converters.isEmpty()) { 285 return handle; 286 } 287 final MethodHandle newHandle = 288 MethodHandles.filterArguments(handle, pos, converters.toArray(new MethodHandle[0])); 289 converters.clear(); 290 return newHandle; 291 } 292 293 /** 294 * Returns true if there might exist a conversion between the requested types (either an automatic JVM conversion, 295 * or one provided by any available {@link GuardingTypeConverterFactory}), or false if there definitely does not 296 * exist a conversion between the requested types. Note that returning true does not guarantee that the conversion 297 * will succeed at runtime (notably, if the "from" or "to" types are sufficiently generic), but returning false 298 * guarantees that it would fail. 299 * 300 * @param from the source type for the conversion 301 * @param to the target type for the conversion 302 * @return true if there can be a conversion, false if there can not. 303 */ 304 boolean canConvert(final Class<?> from, final Class<?> to) { 305 return canAutoConvert(from, to) || canConvert.get(from).get(to); 306 } 307 308 /** 309 * Determines which of the two type conversions from a source type to the two target types is preferred. This is 310 * used for dynamic overloaded method resolution. If the source type is convertible to exactly one target type with 311 * a method invocation conversion, it is chosen, otherwise available {@link ConversionComparator}s are consulted. 312 * @param sourceType the source type. 313 * @param targetType1 one potential target type 314 * @param targetType2 another potential target type. 315 * @return one of Comparison constants that establish which - if any - of the target types is preferable for the 316 * conversion. 317 */ 318 Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) { 319 for(final ConversionComparator comparator: comparators) { 320 final Comparison result = comparator.compareConversion(sourceType, targetType1, targetType2); 321 if(result != Comparison.INDETERMINATE) { 322 return result; 323 } 324 } 325 if(TypeUtilities.isMethodInvocationConvertible(sourceType, targetType1)) { 326 if(!TypeUtilities.isMethodInvocationConvertible(sourceType, targetType2)) { 327 return Comparison.TYPE_1_BETTER; 328 } 329 } else if(TypeUtilities.isMethodInvocationConvertible(sourceType, targetType2)) { 330 return Comparison.TYPE_2_BETTER; 331 } 332 return Comparison.INDETERMINATE; 333 } 334 335 /** 336 * Determines whether it's safe to perform an automatic conversion between the source and target class. 337 * 338 * @param fromType convert from this class 339 * @param toType convert to this class 340 * @return true if it's safe to let MethodHandles.convertArguments() to handle this conversion. 341 */ 342 /*private*/ static boolean canAutoConvert(final Class<?> fromType, final Class<?> toType) { 343 return TypeUtilities.isMethodInvocationConvertible(fromType, toType); 344 } 345 346 /*private*/ MethodHandle getCacheableTypeConverterNull(final Class<?> sourceType, final Class<?> targetType) { 347 final MethodHandle converter = getCacheableTypeConverter(sourceType, targetType); 348 return converter == IDENTITY_CONVERSION ? null : converter; 349 } 350 351 /*private*/ MethodHandle getTypeConverterNull(final Class<?> sourceType, final Class<?> targetType) { 352 try { 353 return getCacheableTypeConverterNull(sourceType, targetType); 354 } catch(final NotCacheableConverter e) { 355 return e.converter; 356 } 357 } 358 359 /*private*/ MethodHandle getCacheableTypeConverter(final Class<?> sourceType, final Class<?> targetType) { 360 return converterMap.get(sourceType).get(targetType); 361 } 362 363 /** 364 * Given a source and target type, returns a method handle that converts between them. Never returns null; in worst 365 * case it will return an identity conversion (that might fail for some values at runtime). You can use this method 366 * if you have a piece of your program that is written in Java, and you need to reuse existing type conversion 367 * machinery in a non-invokedynamic context. 368 * @param sourceType the type to convert from 369 * @param targetType the type to convert to 370 * @return a method handle performing the conversion. 371 */ 372 MethodHandle getTypeConverter(final Class<?> sourceType, final Class<?> targetType) { 373 try { 374 return converterIdentityMap.get(sourceType).get(targetType); 375 } catch(final NotCacheableConverter e) { 376 return e.converter; 377 } 378 } 379 380 private static class LookupSupplier implements Supplier<MethodHandles.Lookup> { 381 volatile boolean returnedLookup; 382 volatile boolean closed; 383 384 @Override 385 public Lookup get() { 386 if (closed) { 387 // Something held on to this supplier and tried to invoke it 388 // after we're done with it. 389 throw new IllegalStateException(); 390 } 391 final Lookup lookup = LinkerServicesImpl.getCurrentLookup(); 392 returnedLookup = true; 393 return lookup; 394 } 395 } 396 397 /*private*/ MethodHandle createConverter(final Class<?> sourceType, final Class<?> targetType) throws Exception { 398 final MethodType type = MethodType.methodType(targetType, sourceType); 399 final MethodHandle identity = IDENTITY_CONVERSION.asType(type); 400 MethodHandle last = identity; 401 402 final LookupSupplier lookupSupplier = new LookupSupplier(); 403 try { 404 for(int i = factories.length; i-- > 0;) { 405 final GuardedInvocation next = factories[i].convertToType(sourceType, targetType, lookupSupplier); 406 if(next != null) { 407 last = next.compose(last); 408 } 409 } 410 } finally { 411 lookupSupplier.closed = true; 412 } 413 414 if(last == identity) { 415 return IDENTITY_CONVERSION; 416 } 417 if(!lookupSupplier.returnedLookup) { 418 return last; 419 } 420 // At least one of the consulted converter factories obtained the 421 // lookup, so we must presume the created converter is sensitive to the 422 // lookup class and thus we will not cache it. 423 throw new NotCacheableConverter(last); 424 } 425 426 /*private*/ static final MethodHandle IDENTITY_CONVERSION = MethodHandles.identity(Object.class); 427 428 @SuppressWarnings("serial") 429 private static class NotCacheableConverter extends RuntimeException { 430 final MethodHandle converter; 431 432 NotCacheableConverter(final MethodHandle converter) { 433 super("", null, false, false); 434 this.converter = converter; 435 } 436 } 437} 438