src/jdk/internal/dynalink/support/TypeConverterFactory.java

Thu, 31 Aug 2017 15:30:47 +0800

author
aoqi
date
Thu, 31 Aug 2017 15:30:47 +0800
changeset 952
6d5471a497fb
parent 719
11b83c913cca
parent 0
b1a7da25b547
child 1205
4112748288bb
permissions
-rw-r--r--

merge

aoqi@0 1 /*
aoqi@0 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
aoqi@0 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0 4 *
aoqi@0 5 * This code is free software; you can redistribute it and/or modify it
aoqi@0 6 * under the terms of the GNU General Public License version 2 only, as
aoqi@0 7 * published by the Free Software Foundation. Oracle designates this
aoqi@0 8 * particular file as subject to the "Classpath" exception as provided
aoqi@0 9 * by Oracle in the LICENSE file that accompanied this code.
aoqi@0 10 *
aoqi@0 11 * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0 14 * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0 15 * accompanied this code).
aoqi@0 16 *
aoqi@0 17 * You should have received a copy of the GNU General Public License version
aoqi@0 18 * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0 20 *
aoqi@0 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0 22 * or visit www.oracle.com if you need additional information or have any
aoqi@0 23 * questions.
aoqi@0 24 */
aoqi@0 25
aoqi@0 26 /*
aoqi@0 27 * This file is available under and governed by the GNU General Public
aoqi@0 28 * License version 2 only, as published by the Free Software Foundation.
aoqi@0 29 * However, the following notice accompanied the original version of this
aoqi@0 30 * file, and Oracle licenses the original version of this file under the BSD
aoqi@0 31 * license:
aoqi@0 32 */
aoqi@0 33 /*
aoqi@0 34 Copyright 2009-2013 Attila Szegedi
aoqi@0 35
aoqi@0 36 Licensed under both the Apache License, Version 2.0 (the "Apache License")
aoqi@0 37 and the BSD License (the "BSD License"), with licensee being free to
aoqi@0 38 choose either of the two at their discretion.
aoqi@0 39
aoqi@0 40 You may not use this file except in compliance with either the Apache
aoqi@0 41 License or the BSD License.
aoqi@0 42
aoqi@0 43 If you choose to use this file in compliance with the Apache License, the
aoqi@0 44 following notice applies to you:
aoqi@0 45
aoqi@0 46 You may obtain a copy of the Apache License at
aoqi@0 47
aoqi@0 48 http://www.apache.org/licenses/LICENSE-2.0
aoqi@0 49
aoqi@0 50 Unless required by applicable law or agreed to in writing, software
aoqi@0 51 distributed under the License is distributed on an "AS IS" BASIS,
aoqi@0 52 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
aoqi@0 53 implied. See the License for the specific language governing
aoqi@0 54 permissions and limitations under the License.
aoqi@0 55
aoqi@0 56 If you choose to use this file in compliance with the BSD License, the
aoqi@0 57 following notice applies to you:
aoqi@0 58
aoqi@0 59 Redistribution and use in source and binary forms, with or without
aoqi@0 60 modification, are permitted provided that the following conditions are
aoqi@0 61 met:
aoqi@0 62 * Redistributions of source code must retain the above copyright
aoqi@0 63 notice, this list of conditions and the following disclaimer.
aoqi@0 64 * Redistributions in binary form must reproduce the above copyright
aoqi@0 65 notice, this list of conditions and the following disclaimer in the
aoqi@0 66 documentation and/or other materials provided with the distribution.
aoqi@0 67 * Neither the name of the copyright holder nor the names of
aoqi@0 68 contributors may be used to endorse or promote products derived from
aoqi@0 69 this software without specific prior written permission.
aoqi@0 70
aoqi@0 71 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
aoqi@0 72 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
aoqi@0 73 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
aoqi@0 74 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
aoqi@0 75 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
aoqi@0 76 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
aoqi@0 77 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
aoqi@0 78 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
aoqi@0 79 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
aoqi@0 80 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
aoqi@0 81 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
aoqi@0 82 */
aoqi@0 83
aoqi@0 84 package jdk.internal.dynalink.support;
aoqi@0 85
aoqi@0 86 import java.lang.invoke.MethodHandle;
aoqi@0 87 import java.lang.invoke.MethodHandles;
aoqi@0 88 import java.lang.invoke.MethodType;
aoqi@0 89 import java.lang.invoke.WrongMethodTypeException;
aoqi@0 90 import java.security.AccessController;
aoqi@0 91 import java.security.PrivilegedAction;
aoqi@0 92 import java.util.LinkedList;
aoqi@0 93 import java.util.List;
aoqi@0 94 import jdk.internal.dynalink.linker.ConversionComparator;
aoqi@0 95 import jdk.internal.dynalink.linker.ConversionComparator.Comparison;
aoqi@0 96 import jdk.internal.dynalink.linker.GuardedInvocation;
aoqi@0 97 import jdk.internal.dynalink.linker.GuardedTypeConversion;
aoqi@0 98 import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
aoqi@0 99 import jdk.internal.dynalink.linker.LinkerServices;
aoqi@0 100
aoqi@0 101 /**
aoqi@0 102 * A factory for type converters. This class is the main implementation behind the
aoqi@0 103 * {@link LinkerServices#asType(MethodHandle, MethodType)}. It manages the known {@link GuardingTypeConverterFactory}
aoqi@0 104 * instances and creates appropriate converters for method handles.
aoqi@0 105 *
aoqi@0 106 * @author Attila Szegedi
aoqi@0 107 */
aoqi@0 108 public class TypeConverterFactory {
aoqi@0 109
aoqi@0 110 private final GuardingTypeConverterFactory[] factories;
aoqi@0 111 private final ConversionComparator[] comparators;
aoqi@0 112
aoqi@0 113 private final ClassValue<ClassMap<MethodHandle>> converterMap = new ClassValue<ClassMap<MethodHandle>>() {
aoqi@0 114 @Override
aoqi@0 115 protected ClassMap<MethodHandle> computeValue(final Class<?> sourceType) {
aoqi@0 116 return new ClassMap<MethodHandle>(getClassLoader(sourceType)) {
aoqi@0 117 @Override
aoqi@0 118 protected MethodHandle computeValue(Class<?> targetType) {
aoqi@0 119 try {
aoqi@0 120 return createConverter(sourceType, targetType);
aoqi@0 121 } catch (RuntimeException e) {
aoqi@0 122 throw e;
aoqi@0 123 } catch (Exception e) {
aoqi@0 124 throw new RuntimeException(e);
aoqi@0 125 }
aoqi@0 126 }
aoqi@0 127 };
aoqi@0 128 }
aoqi@0 129 };
aoqi@0 130
aoqi@0 131 private final ClassValue<ClassMap<MethodHandle>> converterIdentityMap = new ClassValue<ClassMap<MethodHandle>>() {
aoqi@0 132 @Override
aoqi@0 133 protected ClassMap<MethodHandle> computeValue(final Class<?> sourceType) {
aoqi@0 134 return new ClassMap<MethodHandle>(getClassLoader(sourceType)) {
aoqi@0 135 @Override
aoqi@0 136 protected MethodHandle computeValue(Class<?> targetType) {
aoqi@0 137 if(!canAutoConvert(sourceType, targetType)) {
aoqi@0 138 final MethodHandle converter = getCacheableTypeConverter(sourceType, targetType);
aoqi@0 139 if(converter != IDENTITY_CONVERSION) {
aoqi@0 140 return converter;
aoqi@0 141 }
aoqi@0 142 }
aoqi@0 143 return IDENTITY_CONVERSION.asType(MethodType.methodType(targetType, sourceType));
aoqi@0 144 }
aoqi@0 145 };
aoqi@0 146 }
aoqi@0 147 };
aoqi@0 148
aoqi@0 149 private final ClassValue<ClassMap<Boolean>> canConvert = new ClassValue<ClassMap<Boolean>>() {
aoqi@0 150 @Override
aoqi@0 151 protected ClassMap<Boolean> computeValue(final Class<?> sourceType) {
aoqi@0 152 return new ClassMap<Boolean>(getClassLoader(sourceType)) {
aoqi@0 153 @Override
aoqi@0 154 protected Boolean computeValue(Class<?> targetType) {
aoqi@0 155 try {
aoqi@0 156 return getTypeConverterNull(sourceType, targetType) != null;
aoqi@0 157 } catch (RuntimeException e) {
aoqi@0 158 throw e;
aoqi@0 159 } catch (Exception e) {
aoqi@0 160 throw new RuntimeException(e);
aoqi@0 161 }
aoqi@0 162 }
aoqi@0 163 };
aoqi@0 164 }
aoqi@0 165 };
aoqi@0 166
aoqi@0 167 private static final ClassLoader getClassLoader(final Class<?> clazz) {
aoqi@0 168 return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
aoqi@0 169 @Override
aoqi@0 170 public ClassLoader run() {
aoqi@0 171 return clazz.getClassLoader();
aoqi@0 172 }
aoqi@0 173 }, ClassLoaderGetterContextProvider.GET_CLASS_LOADER_CONTEXT);
aoqi@0 174 }
aoqi@0 175
aoqi@0 176 /**
aoqi@0 177 * Creates a new type converter factory from the available {@link GuardingTypeConverterFactory} instances.
aoqi@0 178 *
aoqi@0 179 * @param factories the {@link GuardingTypeConverterFactory} instances to compose.
aoqi@0 180 */
aoqi@0 181 public TypeConverterFactory(Iterable<? extends GuardingTypeConverterFactory> factories) {
aoqi@0 182 final List<GuardingTypeConverterFactory> l = new LinkedList<>();
aoqi@0 183 final List<ConversionComparator> c = new LinkedList<>();
aoqi@0 184 for(GuardingTypeConverterFactory factory: factories) {
aoqi@0 185 l.add(factory);
aoqi@0 186 if(factory instanceof ConversionComparator) {
aoqi@0 187 c.add((ConversionComparator)factory);
aoqi@0 188 }
aoqi@0 189 }
aoqi@0 190 this.factories = l.toArray(new GuardingTypeConverterFactory[l.size()]);
aoqi@0 191 this.comparators = c.toArray(new ConversionComparator[c.size()]);
aoqi@0 192
aoqi@0 193 }
aoqi@0 194
aoqi@0 195 /**
aoqi@0 196 * Similar to {@link MethodHandle#asType(MethodType)} except it also hooks in method handles produced by
aoqi@0 197 * {@link GuardingTypeConverterFactory} implementations, providing for language-specific type coercing of
aoqi@0 198 * parameters. It will apply {@link MethodHandle#asType(MethodType)} for all primitive-to-primitive,
aoqi@0 199 * wrapper-to-primitive, primitive-to-wrapper conversions as well as for all upcasts. For all other conversions,
aoqi@0 200 * it'll insert {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with composite filters
aoqi@0 201 * provided by {@link GuardingTypeConverterFactory} implementations.
aoqi@0 202 *
aoqi@0 203 * @param handle target method handle
aoqi@0 204 * @param fromType the types of source arguments
aoqi@0 205 * @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)} and
aoqi@0 206 * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with
aoqi@0 207 * {@link GuardingTypeConverterFactory} produced type converters as filters.
aoqi@0 208 */
aoqi@0 209 public MethodHandle asType(MethodHandle handle, final MethodType fromType) {
aoqi@0 210 MethodHandle newHandle = handle;
aoqi@0 211 final MethodType toType = newHandle.type();
aoqi@0 212 final int l = toType.parameterCount();
aoqi@0 213 if(l != fromType.parameterCount()) {
aoqi@0 214 throw new WrongMethodTypeException("Parameter counts differ: " + handle.type() + " vs. " + fromType);
aoqi@0 215 }
aoqi@0 216 int pos = 0;
aoqi@0 217 final List<MethodHandle> converters = new LinkedList<>();
aoqi@0 218 for(int i = 0; i < l; ++i) {
aoqi@0 219 final Class<?> fromParamType = fromType.parameterType(i);
aoqi@0 220 final Class<?> toParamType = toType.parameterType(i);
aoqi@0 221 if(canAutoConvert(fromParamType, toParamType)) {
aoqi@0 222 newHandle = applyConverters(newHandle, pos, converters);
aoqi@0 223 } else {
aoqi@0 224 final MethodHandle converter = getTypeConverterNull(fromParamType, toParamType);
aoqi@0 225 if(converter != null) {
aoqi@0 226 if(converters.isEmpty()) {
aoqi@0 227 pos = i;
aoqi@0 228 }
aoqi@0 229 converters.add(converter);
aoqi@0 230 } else {
aoqi@0 231 newHandle = applyConverters(newHandle, pos, converters);
aoqi@0 232 }
aoqi@0 233 }
aoqi@0 234 }
aoqi@0 235 newHandle = applyConverters(newHandle, pos, converters);
aoqi@0 236
aoqi@0 237 // Convert return type
aoqi@0 238 final Class<?> fromRetType = fromType.returnType();
aoqi@0 239 final Class<?> toRetType = toType.returnType();
aoqi@0 240 if(fromRetType != Void.TYPE && toRetType != Void.TYPE) {
aoqi@0 241 if(!canAutoConvert(toRetType, fromRetType)) {
aoqi@0 242 final MethodHandle converter = getTypeConverterNull(toRetType, fromRetType);
aoqi@0 243 if(converter != null) {
aoqi@0 244 newHandle = MethodHandles.filterReturnValue(newHandle, converter);
aoqi@0 245 }
aoqi@0 246 }
aoqi@0 247 }
aoqi@0 248
aoqi@0 249 // Take care of automatic conversions
aoqi@0 250 return newHandle.asType(fromType);
aoqi@0 251 }
aoqi@0 252
aoqi@0 253 private static MethodHandle applyConverters(MethodHandle handle, int pos, List<MethodHandle> converters) {
aoqi@0 254 if(converters.isEmpty()) {
aoqi@0 255 return handle;
aoqi@0 256 }
aoqi@0 257 final MethodHandle newHandle =
aoqi@0 258 MethodHandles.filterArguments(handle, pos, converters.toArray(new MethodHandle[converters.size()]));
aoqi@0 259 converters.clear();
aoqi@0 260 return newHandle;
aoqi@0 261 }
aoqi@0 262
aoqi@0 263 /**
aoqi@0 264 * Returns true if there might exist a conversion between the requested types (either an automatic JVM conversion,
aoqi@0 265 * or one provided by any available {@link GuardingTypeConverterFactory}), or false if there definitely does not
aoqi@0 266 * exist a conversion between the requested types. Note that returning true does not guarantee that the conversion
aoqi@0 267 * will succeed at runtime (notably, if the "from" or "to" types are sufficiently generic), but returning false
aoqi@0 268 * guarantees that it would fail.
aoqi@0 269 *
aoqi@0 270 * @param from the source type for the conversion
aoqi@0 271 * @param to the target type for the conversion
aoqi@0 272 * @return true if there can be a conversion, false if there can not.
aoqi@0 273 */
aoqi@0 274 public boolean canConvert(final Class<?> from, final Class<?> to) {
aoqi@0 275 return canAutoConvert(from, to) || canConvert.get(from).get(to).booleanValue();
aoqi@0 276 }
aoqi@0 277
aoqi@0 278 /**
aoqi@0 279 * Determines which of the two type conversions from a source type to the two target types is preferred. This is
aoqi@0 280 * used for dynamic overloaded method resolution. If the source type is convertible to exactly one target type with
aoqi@0 281 * a method invocation conversion, it is chosen, otherwise available {@link ConversionComparator}s are consulted.
aoqi@0 282 * @param sourceType the source type.
aoqi@0 283 * @param targetType1 one potential target type
aoqi@0 284 * @param targetType2 another potential target type.
aoqi@0 285 * @return one of Comparison constants that establish which - if any - of the target types is preferable for the
aoqi@0 286 * conversion.
aoqi@0 287 */
aoqi@0 288 public Comparison compareConversion(Class<?> sourceType, Class<?> targetType1, Class<?> targetType2) {
aoqi@0 289 for(ConversionComparator comparator: comparators) {
aoqi@0 290 final Comparison result = comparator.compareConversion(sourceType, targetType1, targetType2);
aoqi@0 291 if(result != Comparison.INDETERMINATE) {
aoqi@0 292 return result;
aoqi@0 293 }
aoqi@0 294 }
aoqi@0 295 if(TypeUtilities.isMethodInvocationConvertible(sourceType, targetType1)) {
aoqi@0 296 if(!TypeUtilities.isMethodInvocationConvertible(sourceType, targetType2)) {
aoqi@0 297 return Comparison.TYPE_1_BETTER;
aoqi@0 298 }
aoqi@0 299 } else if(TypeUtilities.isMethodInvocationConvertible(sourceType, targetType2)) {
aoqi@0 300 return Comparison.TYPE_2_BETTER;
aoqi@0 301 }
aoqi@0 302 return Comparison.INDETERMINATE;
aoqi@0 303 }
aoqi@0 304
aoqi@0 305 /**
aoqi@0 306 * Determines whether it's safe to perform an automatic conversion between the source and target class.
aoqi@0 307 *
aoqi@0 308 * @param fromType convert from this class
aoqi@0 309 * @param toType convert to this class
aoqi@0 310 * @return true if it's safe to let MethodHandles.convertArguments() to handle this conversion.
aoqi@0 311 */
aoqi@0 312 /*private*/ static boolean canAutoConvert(final Class<?> fromType, final Class<?> toType) {
aoqi@0 313 return TypeUtilities.isMethodInvocationConvertible(fromType, toType);
aoqi@0 314 }
aoqi@0 315
aoqi@0 316 /*private*/ MethodHandle getCacheableTypeConverterNull(Class<?> sourceType, Class<?> targetType) {
aoqi@0 317 final MethodHandle converter = getCacheableTypeConverter(sourceType, targetType);
aoqi@0 318 return converter == IDENTITY_CONVERSION ? null : converter;
aoqi@0 319 }
aoqi@0 320
aoqi@0 321 /*private*/ MethodHandle getTypeConverterNull(Class<?> sourceType, Class<?> targetType) {
aoqi@0 322 try {
aoqi@0 323 return getCacheableTypeConverterNull(sourceType, targetType);
aoqi@0 324 } catch(NotCacheableConverter e) {
aoqi@0 325 return e.converter;
aoqi@0 326 }
aoqi@0 327 }
aoqi@0 328
aoqi@0 329 /*private*/ MethodHandle getCacheableTypeConverter(Class<?> sourceType, Class<?> targetType) {
aoqi@0 330 return converterMap.get(sourceType).get(targetType);
aoqi@0 331 }
aoqi@0 332
aoqi@0 333 /**
aoqi@0 334 * Given a source and target type, returns a method handle that converts between them. Never returns null; in worst
aoqi@0 335 * case it will return an identity conversion (that might fail for some values at runtime). You can use this method
aoqi@0 336 * if you have a piece of your program that is written in Java, and you need to reuse existing type conversion
aoqi@0 337 * machinery in a non-invokedynamic context.
aoqi@0 338 * @param sourceType the type to convert from
aoqi@0 339 * @param targetType the type to convert to
aoqi@0 340 * @return a method handle performing the conversion.
aoqi@0 341 */
aoqi@0 342 public MethodHandle getTypeConverter(Class<?> sourceType, Class<?> targetType) {
aoqi@0 343 try {
aoqi@0 344 return converterIdentityMap.get(sourceType).get(targetType);
aoqi@0 345 } catch(NotCacheableConverter e) {
aoqi@0 346 return e.converter;
aoqi@0 347 }
aoqi@0 348 }
aoqi@0 349
aoqi@0 350 /*private*/ MethodHandle createConverter(Class<?> sourceType, Class<?> targetType) throws Exception {
aoqi@0 351 final MethodType type = MethodType.methodType(targetType, sourceType);
aoqi@0 352 final MethodHandle identity = IDENTITY_CONVERSION.asType(type);
aoqi@0 353 MethodHandle last = identity;
aoqi@0 354 boolean cacheable = true;
aoqi@0 355 for(int i = factories.length; i-- > 0;) {
aoqi@0 356 final GuardedTypeConversion next = factories[i].convertToType(sourceType, targetType);
aoqi@0 357 if(next != null) {
aoqi@0 358 cacheable = cacheable && next.isCacheable();
aoqi@0 359 final GuardedInvocation conversionInvocation = next.getConversionInvocation();
aoqi@0 360 conversionInvocation.assertType(type);
aoqi@0 361 last = conversionInvocation.compose(last);
aoqi@0 362 }
aoqi@0 363 }
aoqi@0 364 if(last == identity) {
aoqi@0 365 return IDENTITY_CONVERSION;
aoqi@0 366 }
aoqi@0 367 if(cacheable) {
aoqi@0 368 return last;
aoqi@0 369 }
aoqi@0 370 throw new NotCacheableConverter(last);
aoqi@0 371 }
aoqi@0 372
aoqi@0 373 /*private*/ static final MethodHandle IDENTITY_CONVERSION = MethodHandles.identity(Object.class);
aoqi@0 374
aoqi@0 375 private static class NotCacheableConverter extends RuntimeException {
aoqi@0 376 final MethodHandle converter;
aoqi@0 377
aoqi@0 378 NotCacheableConverter(final MethodHandle converter) {
aoqi@0 379 super("", null, false, false);
aoqi@0 380 this.converter = converter;
aoqi@0 381 }
aoqi@0 382 }
aoqi@0 383 }

mercurial