ReflectionExtensions.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. using System.Collections.Concurrent;
  2. using System.Diagnostics.CodeAnalysis;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Runtime.CompilerServices;
  6. using Jint.Native;
  7. using Jint.Runtime;
  8. namespace Jint.Extensions;
  9. internal static class ReflectionExtensions
  10. {
  11. private static readonly Type nullableType = typeof(Nullable<>);
  12. internal static void SetValue(this MemberInfo memberInfo, object forObject, object? value)
  13. {
  14. if (memberInfo.MemberType == MemberTypes.Field)
  15. {
  16. var fieldInfo = (FieldInfo) memberInfo;
  17. if (value != null && fieldInfo.FieldType.IsInstanceOfType(value))
  18. {
  19. fieldInfo.SetValue(forObject, value);
  20. }
  21. }
  22. else if (memberInfo.MemberType == MemberTypes.Property)
  23. {
  24. var propertyInfo = (PropertyInfo) memberInfo;
  25. if (value != null && propertyInfo.PropertyType.IsInstanceOfType(value))
  26. {
  27. propertyInfo.SetValue(forObject, value);
  28. }
  29. }
  30. }
  31. internal static Type GetDefinedType(this MemberInfo memberInfo)
  32. {
  33. return memberInfo switch
  34. {
  35. PropertyInfo propertyInfo => propertyInfo.PropertyType,
  36. FieldInfo fieldInfo => fieldInfo.FieldType,
  37. _ => null!
  38. };
  39. }
  40. internal static IEnumerable<MethodInfo> GetExtensionMethods([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type)
  41. {
  42. return type.GetMethods(BindingFlags.Public | BindingFlags.Static)
  43. .Where(static m => m.IsExtensionMethod());
  44. }
  45. private static readonly ConcurrentDictionary<Type, List<MethodInfo>> _operatorOverloadMethodCache = new();
  46. internal static List<MethodInfo> GetOperatorOverloadMethods([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type)
  47. {
  48. return _operatorOverloadMethodCache.GetOrAdd(type, static t =>
  49. {
  50. #pragma warning disable IL2070
  51. return t.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
  52. .Where(static m => m.IsSpecialName)
  53. .ToList();
  54. #pragma warning restore IL2070
  55. });
  56. }
  57. private static bool IsExtensionMethod(this MethodBase methodInfo)
  58. {
  59. return methodInfo.IsDefined(typeof(ExtensionAttribute), inherit: true);
  60. }
  61. public static bool IsNullable(this Type type)
  62. {
  63. return type is { IsGenericType: true } && type.GetGenericTypeDefinition() == nullableType;
  64. }
  65. public static bool IsNumeric(this Type type)
  66. {
  67. if (type == null || type.IsEnum)
  68. {
  69. return false;
  70. }
  71. switch (Type.GetTypeCode(type))
  72. {
  73. case TypeCode.Byte:
  74. case TypeCode.Decimal:
  75. case TypeCode.Double:
  76. case TypeCode.Int16:
  77. case TypeCode.Int32:
  78. case TypeCode.Int64:
  79. case TypeCode.SByte:
  80. case TypeCode.Single:
  81. case TypeCode.UInt16:
  82. case TypeCode.UInt32:
  83. case TypeCode.UInt64:
  84. return true;
  85. default:
  86. return false;
  87. }
  88. }
  89. public static bool IsClrNumericCoercible(this Type type)
  90. {
  91. if (type == null || type.IsEnum)
  92. {
  93. return false;
  94. }
  95. switch (Type.GetTypeCode(type))
  96. {
  97. case TypeCode.Decimal:
  98. case TypeCode.Double:
  99. case TypeCode.Int32:
  100. case TypeCode.Int64:
  101. return true;
  102. default:
  103. return false;
  104. }
  105. }
  106. public static object AsNumberOfType(this double d, TypeCode type)
  107. {
  108. switch (type)
  109. {
  110. case TypeCode.Decimal:
  111. return (decimal) d;
  112. case TypeCode.Double:
  113. return d;
  114. case TypeCode.Int32:
  115. return (int) d;
  116. case TypeCode.Int64:
  117. return (long) d;
  118. default:
  119. Throw.ArgumentException("Cannot convert " + type);
  120. return null;
  121. }
  122. }
  123. public static bool TryConvertViaTypeCoercion(
  124. Type? memberType,
  125. ValueCoercionType valueCoercionType,
  126. JsValue value,
  127. [NotNullWhen(true)] out object? converted)
  128. {
  129. if (value.IsInteger() && (memberType == typeof(int) || memberType == typeof(long)))
  130. {
  131. // safe and doesn't require configuration
  132. converted = value.AsInteger();
  133. return true;
  134. }
  135. if (memberType == typeof(bool) && (valueCoercionType & ValueCoercionType.Boolean) != ValueCoercionType.None)
  136. {
  137. converted = TypeConverter.ToBoolean(value);
  138. return true;
  139. }
  140. if (memberType == typeof(string)
  141. && !value.IsNullOrUndefined()
  142. && (valueCoercionType & ValueCoercionType.String) != ValueCoercionType.None)
  143. {
  144. // we know how to print out correct string presentation for primitives
  145. // that are non-null and non-undefined
  146. converted = TypeConverter.ToString(value);
  147. return true;
  148. }
  149. if (memberType is not null && memberType.IsClrNumericCoercible() && (valueCoercionType & ValueCoercionType.Number) != ValueCoercionType.None)
  150. {
  151. // we know how to print out correct string presentation for primitives
  152. // that are non-null and non-undefined
  153. var number = TypeConverter.ToNumber(value);
  154. converted = number.AsNumberOfType(Type.GetTypeCode(memberType));
  155. return true;
  156. }
  157. converted = null;
  158. return false;
  159. }
  160. }