|
| 1 | +using System; |
1 | 2 | using System.Collections.Concurrent; |
2 | 3 | using System.Collections.ObjectModel; |
3 | 4 | using System.Diagnostics.CodeAnalysis; |
@@ -25,12 +26,21 @@ public class DefaultTypeConverter : ITypeConverter |
25 | 26 | private readonly record struct TypeConversionKey(Type Source, Type Target); |
26 | 27 |
|
27 | 28 | private static readonly ConcurrentDictionary<TypeConversionKey, MethodInfo?> _knownCastOperators = new(); |
| 29 | + private static readonly ConcurrentDictionary<TypeConversionKey, MethodInfo?> _knownFromResultGenerics = new(); |
28 | 30 |
|
29 | 31 | private static readonly Type intType = typeof(int); |
30 | 32 | private static readonly Type iCallableType = typeof(JsCallDelegate); |
31 | 33 | private static readonly Type jsValueType = typeof(JsValue); |
32 | 34 | private static readonly Type objectType = typeof(object); |
33 | 35 | private static readonly Type engineType = typeof(Engine); |
| 36 | + private static readonly Type taskType = typeof(Task); |
| 37 | + private static readonly Type genTaskType = typeof(Task<>); |
| 38 | + private static readonly MethodInfo taskFromResultInfo = taskType.GetMethod("FromResult")!; |
| 39 | +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP |
| 40 | + private static readonly Type valueTaskType = typeof(ValueTask); |
| 41 | + private static readonly Type genValueTaskType = typeof(ValueTask<>); |
| 42 | + private static readonly MethodInfo valueTaskFromResultInfo = valueTaskType.GetMethod("FromResult")!; |
| 43 | +#endif |
34 | 44 |
|
35 | 45 | private static readonly MethodInfo changeTypeIfConvertible = typeof(DefaultTypeConverter).GetMethod( |
36 | 46 | nameof(ChangeTypeOnlyIfConvertible), BindingFlags.NonPublic | BindingFlags.Static)!; |
@@ -390,12 +400,58 @@ private LambdaExpression BuildDelegate( |
390 | 400 | [return: NotNullIfNotNull(nameof(value))] |
391 | 401 | private static object? ChangeTypeOnlyIfConvertible(object? value, Type conversionType, IFormatProvider? provider) |
392 | 402 | { |
| 403 | + if (conversionType == taskType) |
| 404 | + { |
| 405 | + return Task.CompletedTask; |
| 406 | + } |
| 407 | + |
| 408 | +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP |
| 409 | + if (conversionType == valueTaskType) |
| 410 | + { |
| 411 | + return default(ValueTask); |
| 412 | + } |
| 413 | +#endif |
| 414 | + |
| 415 | + if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition() == genTaskType) |
| 416 | + { |
| 417 | + var key = new TypeConversionKey(conversionType.GetGenericArguments()[0], genTaskType); |
| 418 | + var fromResultMethod = _knownFromResultGenerics.GetOrAdd(key, GetFromResultMethod); |
| 419 | + if (fromResultMethod != null) |
| 420 | + { |
| 421 | + return fromResultMethod.Invoke(null, [value]); |
| 422 | + } |
| 423 | + } |
| 424 | + |
| 425 | +#if NETCOREAPP |
| 426 | + if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition() == genValueTaskType) |
| 427 | + { |
| 428 | + var key = new TypeConversionKey(conversionType.GetGenericArguments()[0], genValueTaskType); |
| 429 | + var fromResultMethod = _knownFromResultGenerics.GetOrAdd(key, GetFromResultMethod); |
| 430 | + if (fromResultMethod != null) |
| 431 | + { |
| 432 | + return fromResultMethod.Invoke(null, [value]); |
| 433 | + } |
| 434 | + } |
| 435 | +#endif |
| 436 | + |
393 | 437 | if (value == null || value is IConvertible) |
394 | 438 | return System.Convert.ChangeType(value, conversionType, provider); |
395 | 439 |
|
396 | 440 | return value; |
397 | 441 | } |
398 | 442 |
|
| 443 | + private static MethodInfo? GetFromResultMethod(TypeConversionKey key) |
| 444 | + { |
| 445 | + var (target, taskType) = key; |
| 446 | +#if NETCOREAPP |
| 447 | + if (taskType == genValueTaskType) |
| 448 | + { |
| 449 | + return valueTaskFromResultInfo.MakeGenericMethod(target); |
| 450 | + } |
| 451 | +#endif |
| 452 | + return taskFromResultInfo.MakeGenericMethod(target); |
| 453 | + } |
| 454 | + |
399 | 455 | private static bool TryCastWithOperators(object value, Type type, Type valueType, [NotNullWhen(true)] out object? converted) |
400 | 456 | { |
401 | 457 | var key = new TypeConversionKey(valueType, type); |
|
0 commit comments