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