• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Sholtee / proxygen / 957

03 Apr 2025 03:49AM UTC coverage: 90.283% (+3.1%) from 87.222%
957

push

appveyor

Sholtee
fix method hash generation

4841 of 5362 relevant lines covered (90.28%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.67
/SRC/Private/Extensions/Metadata/TypeExtensions.cs
1
/********************************************************************************
2
* TypeExtensions.cs                                                             *
3
*                                                                               *
4
* Author: Denes Solti                                                           *
5
********************************************************************************/
6
using System;
7
using System.Collections.Generic;
8
using System.Diagnostics;
9
using System.Linq;
10
using System.Linq.Expressions;
11
using System.Reflection;
12
using System.Text.RegularExpressions;
13

14
namespace Solti.Utils.Proxy.Internals
15
{
16
    using Properties;
17

18
    internal static partial class TypeExtensions
19
    {
20
        //
21
        // https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/specifying-fully-qualified-type-names
22
        //
23
        // "&": by ref parameter
24
        // "*": pointer
25
        // "`d": generic type where "d" is an integer
26
        // "[T, TT]": generic parameter
27
        // "[<PropName_1>xXx, <PropName_2>xXx]": props belong to an anon object
28
        //
29

30
        private static readonly Regex TypeNameReplacer = new(@"\&|\*|`\d+(\[[\w,<>]+\])?", RegexOptions.Compiled);
1✔
31

32
        public static string GetFriendlyName(this Type src)
33
        {
1✔
34
            if (src.GetInnermostElementType()?.IsGenericType is true)
1✔
35
                src = src.GetGenericDefinition();
1✔
36

37
            return TypeNameReplacer.Replace
1✔
38
            (
1✔
39
                src.IsNested()
1✔
40
                    ? src.Name 
1✔
41
                    : src.ToString(), 
1✔
42
                string.Empty
1✔
43
            );
1✔
44
        }
1✔
45

46
        public static Type GetGenericDefinition(this Type src)  // works with GenericType<TConcrete>[] too
47
        {
1✔
48
            if (src.IsArray)
1✔
49
            {
1✔
50
                int rank = src.GetArrayRank();
1✔
51
                src = src.GetElementType().GetGenericDefinition();
1✔
52
                return src.MakeArrayType(rank);
1✔
53
            }
54

55
            if (src.IsByRef)
1✔
56
            {
×
57
                src = src.GetElementType().GetGenericDefinition();
×
58
                return src.MakeByRefType();
×
59
            }
60

61
            if (src.IsPointer)
1✔
62
            {
×
63
                src = src.GetElementType().GetGenericDefinition();
×
64
                return src.MakePointerType();
×
65
            }
66

67
            return src.GetGenericTypeDefinition();
1✔
68
        }
1✔
69

70
        public static string? GetQualifiedName(this Type src) 
71
        {
1✔
72
            src = src.GetInnermostElementType() ?? src;
1✔
73

74
            if (src.IsGenericType)
1✔
75
                src = src.GetGenericDefinition();
1✔
76

77
            return src.FullName;
1✔
78
        }
1✔
79

80
        public static Type? GetInnermostElementType(this Type src) 
81
        {
1✔
82
            Type? prev = null;
1✔
83

84
            for (Type? current = src; (current = current!.GetElementType()) is not null;)
1✔
85
            {
1✔
86
                prev = current;
1✔
87
            }
1✔
88

89
            return prev;
1✔
90
        }
1✔
91

92
        public static Type? GetEnclosingType(this Type src) 
93
        {
1✔
94
            src = src.GetInnermostElementType() ?? src;
1✔
95

96
            Type? enclosingType = src.DeclaringType;
1✔
97
            if (enclosingType is null)
1✔
98
                return null;
1✔
99

100
            if (src.IsGenericParameter)
1✔
101
                return enclosingType;
1✔
102

103
            //
104
            // "Cica<T>.Mica<TT>.Kutya" counts as generic, too: In open form it is returned as Cica<T>.Mica<TT>.Kutya<T, TT>
105
            // while in closed as "Cica<T>.Mica<TT>.Kutya<TConcrete1, TConcrete2>".
106
            //
107

108
            int gaCount = enclosingType.GetGenericArguments().Length;
1✔
109
            if (gaCount is 0)
1✔
110
                return enclosingType;
1✔
111

112
            Type[] gas = new Type[gaCount];
1✔
113
            Array.Copy(src.GetGenericArguments(), 0, gas, 0, gaCount);
1✔
114

115
            return enclosingType.MakeGenericType(gas);
1✔
116
        }
1✔
117

118
        public static bool IsNested(this Type src) =>
119
            //
120
            // Types (for instance arrays) derived from embedded types are not embedded anymore.
121
            //
122

123
            (src.GetInnermostElementType() ?? src).IsNested;
1✔
124

125
        public static bool IsClass(this Type src) => !src.IsGenericParameter && src.IsClass;
1✔
126

127
        public static bool IsAbstract(this Type src) => src.IsAbstract && !src.IsSealed; // IL representation of static classes are "sealed abstract"
1✔
128

129
        public static IEnumerable<MethodInfo> ListMethods(this Type src, bool includeStatic = false, bool skipSpecial = true)
130
        {
1✔
131
            IEnumerable<MethodInfo> methods = src.ListMembersInternal
1✔
132
            (
1✔
133
                static (t, f) => t.GetMethods(f),
1✔
134
                static m => m,
1✔
135
                static m => m.GetOverriddenMethod(),
1✔
136
                includeStatic
1✔
137
            );
1✔
138

139
            if (skipSpecial)
1✔
140
                methods = methods.Where(static m => !m.IsSpecialName);
1✔
141

142
            return methods;
1✔
143
        }
1✔
144

145
        public static IEnumerable<PropertyInfo> ListProperties(this Type src, bool includeStatic = false)
146
        {
1✔
147
            return src.ListMembersInternal
1✔
148
            (
1✔
149
                static (t, f) => t.GetProperties(f),
1✔
150
                GetUnderlyingMethod,
1✔
151
                static p => GetUnderlyingMethod(p).GetOverriddenMethod(),
1✔
152
                includeStatic
1✔
153
            );
1✔
154

155
            //
156
            // Higher visibility has the precedence
157
            //
158

159
            static MethodInfo GetUnderlyingMethod(PropertyInfo prop)
160
            {
1✔
161
                if (prop.GetMethod is null)
1✔
162
                    return prop.SetMethod;
1✔
163

164
                if (prop.SetMethod is null)
1✔
165
                    return prop.GetMethod;
1✔
166

167
                return prop.GetMethod.GetAccessModifiers() > prop.SetMethod.GetAccessModifiers()
1✔
168
                    ? prop.GetMethod
1✔
169
                    : prop.SetMethod;
1✔
170
            }
1✔
171
        }
1✔
172

173
        public static IEnumerable<EventInfo> ListEvents(this Type src, bool includeStatic = false)
174
        {
1✔
175
            return src.ListMembersInternal
1✔
176
            (
1✔
177
                static (t, f) => t.GetEvents(f),
1✔
178
                GetUnderlyingMethod,
1✔
179
                static e => GetUnderlyingMethod(e).GetOverriddenMethod(),
1✔
180
                includeStatic
1✔
181
            );
1✔
182

183
            //
184
            // Higher visibility has the precedence
185
            //
186

187
            static MethodInfo GetUnderlyingMethod(EventInfo evt)
188
            {
1✔
189
                if (evt.AddMethod is null)
1✔
190
                    return evt.RemoveMethod;
×
191

192
                if (evt.RemoveMethod is null)
1✔
193
                    return evt.AddMethod;
×
194

195
                return evt.AddMethod.GetAccessModifiers() > evt.RemoveMethod.GetAccessModifiers()
×
196
                    ? evt.AddMethod
×
197
                    : evt.RemoveMethod;
×
198
            }
1✔
199
        }
1✔
200

201
        private static IEnumerable<TMember> ListMembersInternal<TMember>(
202
            this Type src, 
203
            Func<Type, BindingFlags, TMember[]> getter, 
204
            Func<TMember, MethodInfo> getUnderlyingMethod, 
205
            Func<TMember, MethodInfo?> getOverriddenMethod, 
206
            bool includeStatic) where TMember: MemberInfo
207
        {
1✔
208
            if (src.IsGenericParameter)
1✔
209
                yield break;
1✔
210

211
            BindingFlags flags = 
1✔
212
                BindingFlags.Public |
1✔
213

1✔
214
                //
1✔
215
                // BindingFlags.FlattenHierarchy will return public and protected members only. Unfortunately
1✔
216
                // explicit implementations are private
1✔
217
                //
1✔
218

1✔
219
                //BindingFlags.FlattenHierarchy |
1✔
220

1✔
221
                BindingFlags.NonPublic |
1✔
222
                BindingFlags.Instance |
1✔
223
                BindingFlags.DeclaredOnly;
1✔
224

225
            //
226
            // As of NET6_0 we may declare static members on interfaces
227
            //
228

229
            if (includeStatic)
1✔
230
                flags |= BindingFlags.Static;
1✔
231

232
            if (src.IsInterface)
1✔
233
            {
1✔
234
                foreach (Type t in src.GetHierarchy())
1✔
235
                {
1✔
236
                    foreach (TMember member in getter(t, flags))
1✔
237
                    {
1✔
238
                        yield return member;
1✔
239
                    }
1✔
240
                }
1✔
241
            }
1✔
242
            else
243
            {
1✔
244
                HashSet<MethodInfo> overriddenMethods = new();
1✔
245

246
                //
247
                // Order matters: we're processing the hierarchy towards the ancestor
248
                //
249

250
                foreach (Type t in src.GetHierarchy())
1✔
251
                {
1✔
252
                    foreach (TMember member in getter(t, flags))
1✔
253
                    {
1✔
254
                        MethodInfo? 
1✔
255
                            overriddenMethod = getOverriddenMethod(member),
1✔
256
                            underlyingMethod = getUnderlyingMethod(member);
1✔
257

258
                        if (overriddenMethod is not null)
1✔
259
                            overriddenMethods.Add(overriddenMethod);
1✔
260

261
                        if (overriddenMethods.Contains(underlyingMethod))
1✔
262
                            continue;
1✔
263

264
                        //
265
                        // If it was not yielded before (due to "new" or "override") and not private then we are fine.
266
                        //
267

268
                        if (underlyingMethod.GetAccessModifiers() > AccessModifiers.Private)
1✔
269
                            yield return member;
1✔
270
                    }
1✔
271
                }
1✔
272
            }
1✔
273
        }
1✔
274

275
        public static IEnumerable<ConstructorInfo> GetDeclaredConstructors(this Type type)
276
        {
1✔
277
            //
278
            // Array type really sucks: All its constructors are generated by the compiler so from symbols
279
            // they cannot be retrieved.
280
            //
281

282
            if (type.IsArray)
1✔
283
                yield break;
1✔
284

285
            foreach (ConstructorInfo ctor in type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
1✔
286
            {
1✔
287
                if (ctor.GetAccessModifiers() > AccessModifiers.Private)
1✔
288
                    yield return ctor;
1✔
289
            }
1✔
290
        }
1✔
291

292
        public static IEnumerable<Type> GetBaseTypes(this Type type) 
293
        {
1✔
294
            for (Type? baseType = type.GetBaseType(); baseType is not null; baseType = baseType.GetBaseType())
1✔
295
                yield return baseType;
1✔
296
        }
1✔
297

298
        public static IEnumerable<Type> GetAllInterfaces(this Type type) => !type.IsGenericParameter
1✔
299
            ? type.GetInterfaces()
1✔
300
            : Array.Empty<Type>();
1✔
301

302
        public static Type? GetBaseType(this Type src) => !src.IsGenericParameter
1✔
303
            ? src.BaseType
1✔
304
            : null;
1✔
305

306
        public static IEnumerable<Type> GetHierarchy(this Type src)
307
        {
1✔
308
            yield return src;
1✔
309

310
            foreach (Type t in src.IsInterface ? src.GetAllInterfaces() : src.GetBaseTypes())
1✔
311
            {
1✔
312
                yield return t;
1✔
313
            }
1✔
314
        }
1✔
315

316
        public static IEnumerable<Type> GetOwnGenericArguments(this Type src)
317
        {
1✔
318
            if (!src.IsGenericType)
1✔
319
                yield break;
1✔
320

321
            //
322
            // "Cica<T>.Mica<TT>.Kutya" counts as generic, too: In open form it is returned as Cica<T>.Mica<TT>.Kutya<T, TT>
323
            // while in closed as "Cica<T>.Mica<TT>.Kutya<TConcrete1, TConcrete2>".
324
            //
325

326
            Type[] 
1✔
327
                closedArgs = src.GetGenericArguments(),
1✔
328
                openArgs = (src = src.GetGenericTypeDefinition()).GetGenericArguments();
1✔
329

330
            for(int i = 0; i < openArgs.Length; i++)
1✔
331
            {
1✔
332
                Type openArg = openArgs[i];
1✔
333

334
                bool own = true;
1✔
335
                for (Type? parent = src; (parent = parent!.DeclaringType) is not null;)
1✔
336
                {
1✔
337
                    //
338
                    // GetGenericArguments() will return empty array if "parent" is not generic
339
                    //
340

341
                    if (parent.GetGenericArguments().Any(arg => openArg.IsGenericParameter ? arg.IsGenericParameter && arg.Name == openArg.Name : arg == openArg))
1✔
342
                    {
1✔
343
                        own = false;
1✔
344
                        break;
1✔
345
                    }
346
                }
1✔
347
                if (own) 
1✔
348
                    yield return closedArgs[i];
1✔
349
            } 
1✔
350
        }
1✔
351

352
        public static AccessModifiers GetAccessModifiers(this Type src)
353
        {
1✔
354
            src = src.GetInnermostElementType() ?? src;
1✔
355

356
            AccessModifiers am = src switch
1✔
357
            {
1✔
358
                _ when (src.IsPublic && src.IsVisible) || src.IsNestedPublic => AccessModifiers.Public,
1✔
359
                _ when src.IsNestedFamily => AccessModifiers.Protected,
×
360
                _ when src.IsNestedFamORAssem => AccessModifiers.Protected | AccessModifiers.Internal,
1✔
361
                _ when src.IsNestedFamANDAssem => AccessModifiers.Protected | AccessModifiers.Private,
×
362
                _ when src.IsNestedAssembly || (!src.IsVisible && !src.IsNested) => AccessModifiers.Internal,
1✔
363
                _ when src.IsNestedPrivate => AccessModifiers.Private,
1✔
364
                _ => throw new InvalidOperationException(Resources.UNDETERMINED_ACCESS_MODIFIER)
×
365
            };
1✔
366

367
            if (src.IsGenericParameter)
1✔
368
                return am;
1✔
369

370
            //
371
            // Generic arguments may impact the visibility.
372
            //
373

374
            if (src.IsConstructedGenericType)
1✔
375
            {
1✔
376
                foreach (Type ga in src.GetGenericArguments())
1✔
377
                {
1✔
378
                    UpdateAm(ref am, ga);
1✔
379
                }
1✔
380
            }
1✔
381

382
            Type? enclosingType = src.GetEnclosingType();
1✔
383
            if (enclosingType is not null)
1✔
384
            {
1✔
385
                UpdateAm(ref am, enclosingType);
1✔
386
            }
1✔
387

388
            return am;
1✔
389

390
            static void UpdateAm(ref AccessModifiers am, Type t)
391
            {
1✔
392
                AccessModifiers @new = t.GetAccessModifiers();
1✔
393
                if (@new < am)
1✔
394
                    am = @new;
1✔
395
            }
1✔
396
        }
1✔
397

398
        public static RefType GetRefType(this Type src) => src switch
1✔
399
        {
1✔
400
            _ when
1✔
401
            #if NETSTANDARD2_1_OR_GREATER
1✔
402
                src.IsByRefLike ||
1✔
403
            #endif
1✔
404
                src.GetCustomAttributes().Any(static ca => ca.GetType().FullName?.Equals("System.Runtime.CompilerServices.IsByRefLikeAttribute", StringComparison.OrdinalIgnoreCase) is true) => RefType.Ref, // ref struct
×
405
            _ when src.IsPointer => RefType.Pointer,
1✔
406
            _ when src.IsArray => RefType.Array,
1✔
407
            _ => RefType.None
1✔
408
        };
1✔
409

410
        //
411
        // IsFunctionPointer is available in net8.0+ only
412
        //
413

414
        private static Func<Type, bool> GetIsFunctionPointerCore()
415
        {
1✔
416
            PropertyInfo? prop = typeof(Type).GetProperty("IsFunctionPointer");
1✔
417
            ParameterExpression type = Expression.Parameter(typeof(Type), nameof(type));
1✔
418
            return Expression.Lambda<Func<Type, bool>>
×
419
            (
×
420
                body: prop is null ? Expression.Constant(false) : Expression.Property(type, prop),
×
421
                type
×
422
            ).Compile();
×
423
        }
1✔
424

425
        private static readonly Func<Type, bool> FIsFunctionPointerCore = GetIsFunctionPointerCore();
1✔
426

427
        public static bool IsFunctionPointer(this Type src) => FIsFunctionPointerCore(src);
1✔
428

429
        public static IEnumerable<Type> GetGenericConstraints(this Type src, MemberInfo declaringMember)
430
        {
1✔
431
            foreach(Type gpc in src.GetGenericParameterConstraints())
1✔
432
            {      
1✔
433
                //
434
                // We don't want a
435
                //     "where TT : struct, global::System.ValueType"
436
                //
437

438
                if (gpc == typeof(ValueType) && src.GenericParameterAttributes.HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint))
1✔
439
                    continue;
1✔
440

441
                Type? declaringType = declaringMember switch
442
                {
443
                    MethodInfo method => method.DeclaringType,
1✔
444
                    Type type => type.DeclaringType,
×
445
                    _ => null
×
446
                };
447
                if (declaringType?.IsConstructedGenericType is true)
×
448
                {
1✔
449
                    //
450
                    // Get the specialized constraint from the declaring member
451
                    // (note that gpc.DeclaringXxX always returns the generic definition)
452
                    //
453

454
                    int position = declaringType.GetGenericTypeDefinition().GetGenericArguments().IndexOf(gpc);
1✔
455
                    if (position >= 0)
1✔
456
                    {
1✔
457
                        yield return declaringType.GetGenericArguments()[position];
1✔
458
                        continue;
1✔
459
                    }
460
                }
1✔
461

462
                yield return gpc;
1✔
463
            }
1✔
464
        }
1✔
465

466
        public static int GetGenericParameterIndex(this Type src)
467
        {
1✔
468
            if (!src.IsGenericParameter)
1✔
469
                return 0;
×
470

471
            src = src.GetInnermostElementType() ?? src;
1✔
472

473
            return src.DeclaringMethod is not null
1✔
474
                ? GetIndex(src.DeclaringMethod.GetGenericArguments(), src)
1✔
475
                : GetIndex(src.DeclaringType.GetGenericArguments(), src) * -1;
1✔
476

477
            static int GetIndex(IEnumerable<Type> gas, Type src)
478
            {
1✔
479
                int result = gas.Select(static t => t.Name).IndexOf(src.Name);
1✔
480
                Debug.Assert(result >= 0);
1✔
481

482
                return result + 1;
1✔
483
            }
1✔
484
        }
1✔
485
    }
486
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc