using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using Jint.Native; using Jint.Runtime; namespace Jint.Extensions; internal static class ReflectionExtensions { private static readonly Type nullableType = typeof(Nullable<>); internal static void SetValue(this MemberInfo memberInfo, object forObject, object? value) { if (memberInfo.MemberType == MemberTypes.Field) { var fieldInfo = (FieldInfo) memberInfo; if (value != null && fieldInfo.FieldType.IsInstanceOfType(value)) { fieldInfo.SetValue(forObject, value); } } else if (memberInfo.MemberType == MemberTypes.Property) { var propertyInfo = (PropertyInfo) memberInfo; if (value != null && propertyInfo.PropertyType.IsInstanceOfType(value)) { propertyInfo.SetValue(forObject, value); } } } internal static Type GetDefinedType(this MemberInfo memberInfo) { return memberInfo switch { PropertyInfo propertyInfo => propertyInfo.PropertyType, FieldInfo fieldInfo => fieldInfo.FieldType, _ => null! }; } internal static IEnumerable GetExtensionMethods([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type) { return type.GetMethods(BindingFlags.Public | BindingFlags.Static) .Where(static m => m.IsExtensionMethod()); } private static readonly ConcurrentDictionary> _operatorOverloadMethodCache = new(); internal static List GetOperatorOverloadMethods([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type) { return _operatorOverloadMethodCache.GetOrAdd(type, static t => { #pragma warning disable IL2070 return t.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy) .Where(static m => m.IsSpecialName) .ToList(); #pragma warning restore IL2070 }); } private static bool IsExtensionMethod(this MethodBase methodInfo) { return methodInfo.IsDefined(typeof(ExtensionAttribute), inherit: true); } public static bool IsNullable(this Type type) { return type is { IsGenericType: true } && type.GetGenericTypeDefinition() == nullableType; } public static bool IsNumeric(this Type type) { if (type == null || type.IsEnum) { return false; } switch (Type.GetTypeCode(type)) { case TypeCode.Byte: case TypeCode.Decimal: case TypeCode.Double: case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: case TypeCode.SByte: case TypeCode.Single: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: return true; default: return false; } } public static bool IsClrNumericCoercible(this Type type) { if (type == null || type.IsEnum) { return false; } switch (Type.GetTypeCode(type)) { case TypeCode.Decimal: case TypeCode.Double: case TypeCode.Int32: case TypeCode.Int64: return true; default: return false; } } public static object AsNumberOfType(this double d, TypeCode type) { switch (type) { case TypeCode.Decimal: return (decimal) d; case TypeCode.Double: return d; case TypeCode.Int32: return (int) d; case TypeCode.Int64: return (long) d; default: Throw.ArgumentException("Cannot convert " + type); return null; } } public static bool TryConvertViaTypeCoercion( Type? memberType, ValueCoercionType valueCoercionType, JsValue value, [NotNullWhen(true)] out object? converted) { if (value.IsInteger() && (memberType == typeof(int) || memberType == typeof(long))) { // safe and doesn't require configuration converted = value.AsInteger(); return true; } if (memberType == typeof(bool) && (valueCoercionType & ValueCoercionType.Boolean) != ValueCoercionType.None) { converted = TypeConverter.ToBoolean(value); return true; } if (memberType == typeof(string) && !value.IsNullOrUndefined() && (valueCoercionType & ValueCoercionType.String) != ValueCoercionType.None) { // we know how to print out correct string presentation for primitives // that are non-null and non-undefined converted = TypeConverter.ToString(value); return true; } if (memberType is not null && memberType.IsClrNumericCoercible() && (valueCoercionType & ValueCoercionType.Number) != ValueCoercionType.None) { // we know how to print out correct string presentation for primitives // that are non-null and non-undefined var number = TypeConverter.ToNumber(value); converted = number.AsNumberOfType(Type.GetTypeCode(memberType)); return true; } converted = null; return false; } }