Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mcs / class / referencesource / System.Web / Util / ReflectionUtil.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="ReflectionUtil.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>                                                                
5 //------------------------------------------------------------------------------
6
7 namespace System.Web.Util {
8     using System;
9     using System.Reflection;
10     using System.Reflection.Emit;
11     using System.Security.Permissions;
12
13     // Provides helper methods for performing reflection over managed objects.
14
15     [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
16     internal static class ReflectionUtil {
17
18         // Resets an object to its "default" state, e.g. where each instance field is given the value default(TField).
19         public static void Reset<T>(T obj) where T : class {
20             ResetUtil<T>.ResetFn(obj);
21         }
22
23         private static class ResetUtil<T> where T : class {
24             internal readonly static Action<T> ResetFn = CreateResetFn();
25
26             private static Action<T> CreateResetFn() {
27                 Type targetType = typeof(T);
28                 DynamicMethod dynamicMethod = CreateDynamicMethodWithAssert();
29                 ILGenerator ilGen = dynamicMethod.GetILGenerator();
30
31                 // for each field in the target type, reset to default(TField)
32                 FieldInfo[] allFields = targetType.GetFields(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public);
33                 foreach (FieldInfo fieldInfo in allFields) {
34                     if (fieldInfo.IsInitOnly || fieldInfo.IsDefined(typeof(DoNotResetAttribute))) {
35                         // This field is not eligible to be reset because it is marked readonly or [DoNotReset].
36                         continue;
37                     }
38
39                     // obj.field = default(TField);
40                     // Opcodes.Initobj can be used for both value and reference types; see ECMA 335, Partition III, Sec. 4.5 "initobj"
41                     // (ref: http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf)
42                     ilGen.Emit(OpCodes.Ldarg_0);
43                     ilGen.Emit(OpCodes.Ldflda, fieldInfo);
44                     ilGen.Emit(OpCodes.Initobj, fieldInfo.FieldType);
45                 }
46
47                 ilGen.Emit(OpCodes.Ret);
48                 // dynamicMethod = obj => {
49                 //   obj.field1 = default(TField1);
50                 //   obj.field2 = default(TField2);
51                 //   ...
52                 // };
53                 return (Action<T>)dynamicMethod.CreateDelegate(typeof(Action<T>));
54             }
55
56             [ReflectionPermission(SecurityAction.Assert, MemberAccess = true)] // needed to create a DynamicMethod inside the target type
57             private static DynamicMethod CreateDynamicMethodWithAssert() {
58                 Type targetType = typeof(T);
59                 return new DynamicMethod(
60                     name: "Reset-" + targetType.Name,
61                     returnType: typeof(void),
62                     parameterTypes: new Type[] { targetType },
63                     owner: targetType,
64                     skipVisibility: true);
65             }
66         }
67
68     }
69 }