ReflectionExtensions.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. using System.Diagnostics.CodeAnalysis;
  2. using System.Linq;
  3. using System.Reflection;
  4. using System.Runtime.CompilerServices;
  5. using Jint.Native;
  6. using Jint.Runtime;
  7. namespace Jint.Extensions
  8. {
  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. internal static IEnumerable<MethodInfo> GetOperatorOverloadMethods([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type)
  46. {
  47. return type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
  48. .Where(static m => m.IsSpecialName);
  49. }
  50. private static bool IsExtensionMethod(this MethodBase methodInfo)
  51. {
  52. return methodInfo.IsDefined(typeof(ExtensionAttribute), inherit: true);
  53. }
  54. public static bool IsNullable(this Type type)
  55. {
  56. return type is { IsGenericType: true } && type.GetGenericTypeDefinition() == nullableType;
  57. }
  58. public static bool IsNumeric(this Type type)
  59. {
  60. if (type == null || type.IsEnum)
  61. {
  62. return false;
  63. }
  64. switch (Type.GetTypeCode(type))
  65. {
  66. case TypeCode.Byte:
  67. case TypeCode.Decimal:
  68. case TypeCode.Double:
  69. case TypeCode.Int16:
  70. case TypeCode.Int32:
  71. case TypeCode.Int64:
  72. case TypeCode.SByte:
  73. case TypeCode.Single:
  74. case TypeCode.UInt16:
  75. case TypeCode.UInt32:
  76. case TypeCode.UInt64:
  77. return true;
  78. default:
  79. return false;
  80. }
  81. }
  82. public static bool IsClrNumericCoercible(this Type type)
  83. {
  84. if (type == null || type.IsEnum)
  85. {
  86. return false;
  87. }
  88. switch (Type.GetTypeCode(type))
  89. {
  90. case TypeCode.Decimal:
  91. case TypeCode.Double:
  92. case TypeCode.Int32:
  93. case TypeCode.Int64:
  94. return true;
  95. default:
  96. return false;
  97. }
  98. }
  99. public static object AsNumberOfType(this double d, TypeCode type)
  100. {
  101. switch (type)
  102. {
  103. case TypeCode.Decimal:
  104. return (decimal) d;
  105. case TypeCode.Double:
  106. return d;
  107. case TypeCode.Int32:
  108. return (int) d;
  109. case TypeCode.Int64:
  110. return (long) d;
  111. default:
  112. ExceptionHelper.ThrowArgumentException("Cannot convert " + type);
  113. return null;
  114. }
  115. }
  116. public static bool TryConvertViaTypeCoercion(
  117. Type? memberType,
  118. ValueCoercionType valueCoercionType,
  119. JsValue value,
  120. [NotNullWhen(true)] out object? converted)
  121. {
  122. if (value.IsInteger() && (memberType == typeof(int) || memberType == typeof(long)))
  123. {
  124. // safe and doesn't require configuration
  125. converted = value.AsInteger();
  126. return true;
  127. }
  128. if (memberType == typeof(bool) && (valueCoercionType & ValueCoercionType.Boolean) != ValueCoercionType.None)
  129. {
  130. converted = TypeConverter.ToBoolean(value);
  131. return true;
  132. }
  133. if (memberType == typeof(string)
  134. && !value.IsNullOrUndefined()
  135. && (valueCoercionType & ValueCoercionType.String) != ValueCoercionType.None)
  136. {
  137. // we know how to print out correct string presentation for primitives
  138. // that are non-null and non-undefined
  139. converted = TypeConverter.ToString(value);
  140. return true;
  141. }
  142. if (memberType is not null && memberType.IsClrNumericCoercible() && (valueCoercionType & ValueCoercionType.Number) != ValueCoercionType.None)
  143. {
  144. // we know how to print out correct string presentation for primitives
  145. // that are non-null and non-undefined
  146. var number = TypeConverter.ToNumber(value);
  147. converted = number.AsNumberOfType(Type.GetTypeCode(memberType));
  148. return true;
  149. }
  150. converted = null;
  151. return false;
  152. }
  153. }
  154. }