1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System.Reflection; 9 using System.Runtime.CompilerServices; 10 using System; 11 using System.Linq; 12 using Antmicro.Renode.Core; 13 using Antmicro.Renode.Peripherals; 14 using Microsoft.CSharp.RuntimeBinder; 15 using System.Collections.Generic; 16 using Dynamitey; 17 using Antmicro.Renode.Time; 18 using Antmicro.Renode.UserInterface; 19 using Antmicro.Renode.Utilities.Collections; 20 21 using Range = Antmicro.Renode.Core.Range; 22 23 namespace Antmicro.Renode.Utilities 24 { 25 public static class TypeExtensions 26 { IsCallable(this PropertyInfo info)27 public static bool IsCallable(this PropertyInfo info) 28 { 29 return cache.Get(info, InnerIsCallable); 30 } 31 IsCallableIndexer(this PropertyInfo info)32 public static bool IsCallableIndexer(this PropertyInfo info) 33 { 34 return cache.Get(info, InnerIsCallableIndexer); 35 } 36 IsCallable(this FieldInfo info)37 public static bool IsCallable(this FieldInfo info) 38 { 39 return cache.Get(info, InnerIsCallable); 40 } 41 IsCallable(this MethodInfo info)42 public static bool IsCallable(this MethodInfo info) 43 { 44 return cache.Get(info, InnerIsCallable); 45 } 46 IsExtensionCallable(this MethodInfo info)47 public static bool IsExtensionCallable(this MethodInfo info) 48 { 49 return cache.Get(info, InnerIsExtensionCallable); 50 } 51 IsStatic(this MemberInfo info)52 public static bool IsStatic(this MemberInfo info) 53 { 54 return cache.Get(info, InnerIsStatic); 55 } 56 IsCurrentlyGettable(this PropertyInfo info, BindingFlags flags)57 public static bool IsCurrentlyGettable(this PropertyInfo info, BindingFlags flags) 58 { 59 return cache.Get(info, flags, InnerIsCurrentlyGettable); 60 } 61 IsCurrentlySettable(this PropertyInfo info, BindingFlags flags)62 public static bool IsCurrentlySettable(this PropertyInfo info, BindingFlags flags) 63 { 64 return cache.Get(info, flags, InnerIsCurrentlySettable); 65 } 66 IsExtension(this MethodInfo info)67 public static bool IsExtension(this MethodInfo info) 68 { 69 return cache.Get(info, InnerIsExtension); 70 } 71 IsBaseCallable(this MemberInfo info)72 private static bool IsBaseCallable(this MemberInfo info) 73 { 74 return cache.Get(info, InnerIsBaseCallable); 75 } 76 GetEnumerableType(Type type)77 private static Type GetEnumerableType(Type type) 78 { 79 var ifaces = type.GetInterfaces(); 80 if(ifaces.Length == 0) 81 { 82 return null; 83 } 84 var iface = ifaces.FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>)); 85 86 if(iface == null) 87 { 88 return null; 89 } 90 return iface.GetGenericArguments()[0]; 91 } 92 93 private static Type[] convertibleTypes = { 94 typeof(FilePath), 95 typeof(IConnectable), 96 typeof(IConvertible), 97 typeof(IPeripheral), 98 typeof(IExternal), 99 typeof(IEmulationElement), 100 typeof(IClockSource), 101 typeof(Range), 102 typeof(TimeSpan), 103 typeof(TimeInterval), 104 typeof(TimeStamp), 105 typeof(TimerResult) 106 }; 107 IsTypeConvertible(Type type)108 private static bool IsTypeConvertible(Type type) 109 { 110 return cache.Get(type, InnerIsTypeConvertible); 111 } 112 InnerIsTypeConvertible(Type type)113 private static bool InnerIsTypeConvertible(Type type) 114 { 115 var underlyingType = GetEnumerableType(type); 116 if(underlyingType != null) 117 { 118 return IsTypeConvertible(underlyingType); 119 } 120 if(type.IsEnum || type.IsDefined(typeof(ConvertibleAttribute), true) 121 || convertibleTypes.Any(x => x.IsAssignableFrom(type))) 122 { 123 return true; 124 } 125 if(type.IsByRef || type == typeof(object)) //these are always wrong 126 { 127 return false; 128 } 129 try //try with a number 130 { 131 Dynamic.InvokeConvert(1, type, false); 132 return true; 133 } 134 //because every conversion operator may throw anything 135 //trying to convert Span<T> and ReadOnlySpan<T> always throws exception 136 catch(Exception ex) when (ex is TypeLoadException || 137 ex is BadImageFormatException || 138 ex is RuntimeBinderException || 139 ex is ArgumentException) 140 { 141 } 142 try //try with a string 143 { 144 Dynamic.InvokeConvert("a", type, false); 145 return true; 146 } 147 catch(Exception ex) when (ex is TypeLoadException || 148 ex is BadImageFormatException || 149 ex is RuntimeBinderException || 150 ex is ArgumentException) 151 { 152 } 153 try //try with a character 154 { 155 Dynamic.InvokeConvert('a', type, false); 156 return true; 157 } 158 catch(Exception ex) when (ex is TypeLoadException || 159 ex is BadImageFormatException || 160 ex is RuntimeBinderException || 161 ex is ArgumentException) 162 { 163 } 164 try //try with a bool 165 { 166 Dynamic.InvokeConvert(true, type, false); 167 return true; 168 } 169 catch(Exception ex) when (ex is TypeLoadException || 170 ex is BadImageFormatException || 171 ex is RuntimeBinderException || 172 ex is ArgumentException) 173 { 174 } 175 return false; 176 } 177 InnerIsCallable(PropertyInfo i)178 private static bool InnerIsCallable(PropertyInfo i) 179 { 180 return IsTypeConvertible(i.PropertyType) && i.GetIndexParameters().Length == 0 && i.IsBaseCallable(); //disallow indexers 181 } 182 InnerIsCallableIndexer(PropertyInfo i)183 private static bool InnerIsCallableIndexer(PropertyInfo i) 184 { 185 return IsTypeConvertible(i.PropertyType) && i.GetIndexParameters().Length != 0 && i.IsBaseCallable(); //only indexers 186 } 187 InnerIsCallable(FieldInfo i)188 private static bool InnerIsCallable(FieldInfo i) 189 { 190 return IsTypeConvertible(i.FieldType) && i.IsBaseCallable(); 191 } 192 InnerIsCallable(MethodInfo i)193 private static bool InnerIsCallable(MethodInfo i) 194 { 195 return i.GetParameters().All(y => !y.IsOut && IsTypeConvertible(y.ParameterType) || y.IsOptional) && i.IsBaseCallable(); 196 } 197 InnerIsExtensionCallable(MethodInfo i)198 private static bool InnerIsExtensionCallable(MethodInfo i) 199 { 200 return !i.IsGenericMethod && i.GetParameters().Skip(1).All(y => !y.IsOut && IsTypeConvertible(y.ParameterType) || y.IsOptional) && i.IsBaseCallable(); 201 } 202 InnerIsStatic(MemberInfo i)203 private static bool InnerIsStatic(MemberInfo i) 204 { 205 var eventInfo = i as EventInfo; 206 var fieldInfo = i as FieldInfo; 207 var methodInfo = i as MethodInfo; 208 var propertyInfo = i as PropertyInfo; 209 var type = i as Type; 210 211 if(eventInfo != null) 212 { 213 var addMethod = eventInfo.GetAddMethod(true); 214 if(addMethod != null) 215 { 216 return addMethod.IsStatic; 217 } 218 var rmMethod = eventInfo.GetRemoveMethod(true); 219 if(rmMethod != null) 220 { 221 return rmMethod.IsStatic; 222 } 223 throw new ArgumentException(String.Format("Unhandled type of event: {0} in {1}.", eventInfo.Name, eventInfo.DeclaringType)); 224 } 225 226 if(fieldInfo != null) 227 { 228 return (fieldInfo.Attributes & FieldAttributes.Static) != 0; 229 } 230 231 if(methodInfo != null) 232 { 233 return methodInfo.IsStatic; 234 } 235 236 if(propertyInfo != null) 237 { 238 var getMethod = propertyInfo.GetGetMethod(true); 239 if(getMethod != null) 240 { 241 return getMethod.IsStatic; 242 } 243 var setMethod = propertyInfo.GetSetMethod(true); 244 if(setMethod != null) 245 { 246 return setMethod.IsStatic; 247 } 248 throw new ArgumentException(String.Format("Unhandled type of property: {0} in {1}.", propertyInfo.Name, propertyInfo.DeclaringType)); 249 } 250 251 if(type != null) 252 { 253 return type.IsAbstract && type.IsSealed; 254 } 255 throw new ArgumentException(String.Format("Unhandled type of MemberInfo: {0} in {1}.", i.Name, i.DeclaringType)); 256 } 257 InnerIsCurrentlyGettable(PropertyInfo i, BindingFlags f)258 private static bool InnerIsCurrentlyGettable(PropertyInfo i, BindingFlags f) 259 { 260 return i.CanRead && i.GetGetMethod((f & BindingFlags.NonPublic) > 0) != null; 261 } 262 InnerIsCurrentlySettable(PropertyInfo i, BindingFlags f)263 private static bool InnerIsCurrentlySettable(PropertyInfo i, BindingFlags f) 264 { 265 return i.CanWrite && i.GetSetMethod((f & BindingFlags.NonPublic) > 0) != null; 266 } 267 InnerIsExtension(MethodInfo i)268 private static bool InnerIsExtension(MethodInfo i) 269 { 270 return i.IsDefined(typeof(ExtensionAttribute), true); 271 } 272 InnerIsBaseCallable(MemberInfo i)273 private static bool InnerIsBaseCallable(MemberInfo i) 274 { 275 return !i.IsDefined(typeof(HideInMonitorAttribute)); 276 } 277 278 private static readonly SimpleCache cache = new SimpleCache(); 279 } 280 } 281 282