diff options
Diffstat (limited to 'src/jdk/internal/dynalink/support/TypeConverterFactory.java')
-rw-r--r-- | src/jdk/internal/dynalink/support/TypeConverterFactory.java | 83 |
1 files changed, 55 insertions, 28 deletions
diff --git a/src/jdk/internal/dynalink/support/TypeConverterFactory.java b/src/jdk/internal/dynalink/support/TypeConverterFactory.java index 436acad7..736b787c 100644 --- a/src/jdk/internal/dynalink/support/TypeConverterFactory.java +++ b/src/jdk/internal/dynalink/support/TypeConverterFactory.java @@ -97,6 +97,7 @@ import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.GuardedTypeConversion; import jdk.internal.dynalink.linker.GuardingTypeConverterFactory; import jdk.internal.dynalink.linker.LinkerServices; +import jdk.internal.dynalink.linker.MethodTypeConversionStrategy; /** * A factory for type converters. This class is the main implementation behind the @@ -109,18 +110,19 @@ public class TypeConverterFactory { private final GuardingTypeConverterFactory[] factories; private final ConversionComparator[] comparators; + private final MethodTypeConversionStrategy autoConversionStrategy; private final ClassValue<ClassMap<MethodHandle>> converterMap = new ClassValue<ClassMap<MethodHandle>>() { @Override protected ClassMap<MethodHandle> computeValue(final Class<?> sourceType) { return new ClassMap<MethodHandle>(getClassLoader(sourceType)) { @Override - protected MethodHandle computeValue(Class<?> targetType) { + protected MethodHandle computeValue(final Class<?> targetType) { try { return createConverter(sourceType, targetType); - } catch (RuntimeException e) { + } catch (final RuntimeException e) { throw e; - } catch (Exception e) { + } catch (final Exception e) { throw new RuntimeException(e); } } @@ -133,7 +135,7 @@ public class TypeConverterFactory { protected ClassMap<MethodHandle> computeValue(final Class<?> sourceType) { return new ClassMap<MethodHandle>(getClassLoader(sourceType)) { @Override - protected MethodHandle computeValue(Class<?> targetType) { + protected MethodHandle computeValue(final Class<?> targetType) { if(!canAutoConvert(sourceType, targetType)) { final MethodHandle converter = getCacheableTypeConverter(sourceType, targetType); if(converter != IDENTITY_CONVERSION) { @@ -151,12 +153,12 @@ public class TypeConverterFactory { protected ClassMap<Boolean> computeValue(final Class<?> sourceType) { return new ClassMap<Boolean>(getClassLoader(sourceType)) { @Override - protected Boolean computeValue(Class<?> targetType) { + protected Boolean computeValue(final Class<?> targetType) { try { return getTypeConverterNull(sourceType, targetType) != null; - } catch (RuntimeException e) { + } catch (final RuntimeException e) { throw e; - } catch (Exception e) { + } catch (final Exception e) { throw new RuntimeException(e); } } @@ -177,11 +179,27 @@ public class TypeConverterFactory { * Creates a new type converter factory from the available {@link GuardingTypeConverterFactory} instances. * * @param factories the {@link GuardingTypeConverterFactory} instances to compose. + * @param autoConversionStrategy conversion strategy for automatic type conversions. After + * {@link #asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)} has applied all custom + * conversions to a method handle, it still needs to effect + * {@link TypeUtilities#isMethodInvocationConvertible(Class, Class) method invocation conversions} that + * can usually be automatically applied as per + * {@link java.lang.invoke.MethodHandle#asType(java.lang.invoke.MethodType)}. + * However, sometimes language runtimes will want to customize even those conversions for their own call + * sites. A typical example is allowing unboxing of null return values, which is by default prohibited by + * ordinary {@code MethodHandles.asType}. In this case, a language runtime can install its own custom + * automatic conversion strategy, that can deal with null values. Note that when the strategy's + * {@link MethodTypeConversionStrategy#asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)} + * is invoked, the custom language conversions will already have been applied to the method handle, so by + * design the difference between the handle's current method type and the desired final type will always + * only be ones that can be subjected to method invocation conversions. Can be null, in which case no + * custom strategy is employed. */ - public TypeConverterFactory(Iterable<? extends GuardingTypeConverterFactory> factories) { + public TypeConverterFactory(final Iterable<? extends GuardingTypeConverterFactory> factories, + final MethodTypeConversionStrategy autoConversionStrategy) { final List<GuardingTypeConverterFactory> l = new LinkedList<>(); final List<ConversionComparator> c = new LinkedList<>(); - for(GuardingTypeConverterFactory factory: factories) { + for(final GuardingTypeConverterFactory factory: factories) { l.add(factory); if(factory instanceof ConversionComparator) { c.add((ConversionComparator)factory); @@ -189,24 +207,28 @@ public class TypeConverterFactory { } this.factories = l.toArray(new GuardingTypeConverterFactory[l.size()]); this.comparators = c.toArray(new ConversionComparator[c.size()]); - + this.autoConversionStrategy = autoConversionStrategy; } /** * Similar to {@link MethodHandle#asType(MethodType)} except it also hooks in method handles produced by * {@link GuardingTypeConverterFactory} implementations, providing for language-specific type coercing of - * parameters. It will apply {@link MethodHandle#asType(MethodType)} for all primitive-to-primitive, - * wrapper-to-primitive, primitive-to-wrapper conversions as well as for all upcasts. For all other conversions, - * it'll insert {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with composite filters - * provided by {@link GuardingTypeConverterFactory} implementations. + * parameters. For all conversions that are not a JLS method invocation conversion it'll insert + * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with composite filters + * provided by {@link GuardingTypeConverterFactory} implementations. For the remaining JLS method invocation + * conversions, it will invoke {@link MethodTypeConversionStrategy#asType(MethodHandle, MethodType)} first + * if an automatic conversion strategy was specified in the + * {@link #TypeConverterFactory(Iterable, MethodTypeConversionStrategy) constructor}, and finally apply + * {@link MethodHandle#asType(MethodType)} for any remaining conversions. * * @param handle target method handle * @param fromType the types of source arguments - * @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)} and + * @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)}, + * {@link MethodTypeConversionStrategy#asType(MethodHandle, MethodType)}, and * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with * {@link GuardingTypeConverterFactory} produced type converters as filters. */ - public MethodHandle asType(MethodHandle handle, final MethodType fromType) { + public MethodHandle asType(final MethodHandle handle, final MethodType fromType) { MethodHandle newHandle = handle; final MethodType toType = newHandle.type(); final int l = toType.parameterCount(); @@ -246,11 +268,15 @@ public class TypeConverterFactory { } } - // Take care of automatic conversions - return newHandle.asType(fromType); + // Give change to automatic conversion strategy, if one is present. + final MethodHandle autoConvertedHandle = + autoConversionStrategy != null ? autoConversionStrategy.asType(newHandle, fromType) : newHandle; + + // Do a final asType for any conversions that remain. + return autoConvertedHandle.asType(fromType); } - private static MethodHandle applyConverters(MethodHandle handle, int pos, List<MethodHandle> converters) { + private static MethodHandle applyConverters(final MethodHandle handle, final int pos, final List<MethodHandle> converters) { if(converters.isEmpty()) { return handle; } @@ -285,8 +311,8 @@ public class TypeConverterFactory { * @return one of Comparison constants that establish which - if any - of the target types is preferable for the * conversion. */ - public Comparison compareConversion(Class<?> sourceType, Class<?> targetType1, Class<?> targetType2) { - for(ConversionComparator comparator: comparators) { + public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) { + for(final ConversionComparator comparator: comparators) { final Comparison result = comparator.compareConversion(sourceType, targetType1, targetType2); if(result != Comparison.INDETERMINATE) { return result; @@ -313,20 +339,20 @@ public class TypeConverterFactory { return TypeUtilities.isMethodInvocationConvertible(fromType, toType); } - /*private*/ MethodHandle getCacheableTypeConverterNull(Class<?> sourceType, Class<?> targetType) { + /*private*/ MethodHandle getCacheableTypeConverterNull(final Class<?> sourceType, final Class<?> targetType) { final MethodHandle converter = getCacheableTypeConverter(sourceType, targetType); return converter == IDENTITY_CONVERSION ? null : converter; } - /*private*/ MethodHandle getTypeConverterNull(Class<?> sourceType, Class<?> targetType) { + /*private*/ MethodHandle getTypeConverterNull(final Class<?> sourceType, final Class<?> targetType) { try { return getCacheableTypeConverterNull(sourceType, targetType); - } catch(NotCacheableConverter e) { + } catch(final NotCacheableConverter e) { return e.converter; } } - /*private*/ MethodHandle getCacheableTypeConverter(Class<?> sourceType, Class<?> targetType) { + /*private*/ MethodHandle getCacheableTypeConverter(final Class<?> sourceType, final Class<?> targetType) { return converterMap.get(sourceType).get(targetType); } @@ -339,15 +365,15 @@ public class TypeConverterFactory { * @param targetType the type to convert to * @return a method handle performing the conversion. */ - public MethodHandle getTypeConverter(Class<?> sourceType, Class<?> targetType) { + public MethodHandle getTypeConverter(final Class<?> sourceType, final Class<?> targetType) { try { return converterIdentityMap.get(sourceType).get(targetType); - } catch(NotCacheableConverter e) { + } catch(final NotCacheableConverter e) { return e.converter; } } - /*private*/ MethodHandle createConverter(Class<?> sourceType, Class<?> targetType) throws Exception { + /*private*/ MethodHandle createConverter(final Class<?> sourceType, final Class<?> targetType) throws Exception { final MethodType type = MethodType.methodType(targetType, sourceType); final MethodHandle identity = IDENTITY_CONVERSION.asType(type); MethodHandle last = identity; @@ -372,6 +398,7 @@ public class TypeConverterFactory { /*private*/ static final MethodHandle IDENTITY_CONVERSION = MethodHandles.identity(Object.class); + @SuppressWarnings("serial") private static class NotCacheableConverter extends RuntimeException { final MethodHandle converter; |