aboutsummaryrefslogtreecommitdiff
path: root/src/jdk/internal/dynalink/support/TypeConverterFactory.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jdk/internal/dynalink/support/TypeConverterFactory.java')
-rw-r--r--src/jdk/internal/dynalink/support/TypeConverterFactory.java83
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;