aboutsummaryrefslogtreecommitdiff
path: root/ARMeilleure/Translation/DelegateHelper.cs
blob: f021d1160a24d40b701365caef7aaed108b3fc38 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;

namespace ARMeilleure.Translation
{
    static class DelegateHelper
    {
        private const string DelegateTypesAssemblyName = "JitDelegateTypes";

        private static readonly ModuleBuilder _modBuilder;

        private static readonly Dictionary<string, Type> _delegateTypesCache;

        static DelegateHelper()
        {
            AssemblyBuilder asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(DelegateTypesAssemblyName), AssemblyBuilderAccess.Run);

            _modBuilder = asmBuilder.DefineDynamicModule(DelegateTypesAssemblyName);

            _delegateTypesCache = new Dictionary<string, Type>();
        }

        public static Delegate GetDelegate(MethodInfo info)
        {
            if (info == null)
            {
                throw new ArgumentNullException(nameof(info));
            }

            Type[] parameters = info.GetParameters().Select(pI => pI.ParameterType).ToArray();
            Type   returnType = info.ReturnType;

            Type delegateType = GetDelegateType(parameters, returnType);

            return Delegate.CreateDelegate(delegateType, info);
        }

        private static Type GetDelegateType(Type[] parameters, Type returnType)
        {
            string key = GetFunctionSignatureKey(parameters, returnType);

            if (!_delegateTypesCache.TryGetValue(key, out Type delegateType))
            {
                delegateType = MakeDelegateType(parameters, returnType, key);

                _delegateTypesCache.TryAdd(key, delegateType);
            }

            return delegateType;
        }

        private static string GetFunctionSignatureKey(Type[] parameters, Type returnType)
        {
            string sig = GetTypeName(returnType);

            foreach (Type type in parameters)
            {
                sig += '_' + GetTypeName(type);
            }

            return sig;
        }

        private static string GetTypeName(Type type)
        {
            return type.FullName.Replace(".", string.Empty);
        }

        private const MethodAttributes CtorAttributes =
            MethodAttributes.RTSpecialName |
            MethodAttributes.HideBySig     |
            MethodAttributes.Public;

        private const TypeAttributes DelegateTypeAttributes =
            TypeAttributes.Class     |
            TypeAttributes.Public    |
            TypeAttributes.Sealed    |
            TypeAttributes.AnsiClass |
            TypeAttributes.AutoClass;

        private const MethodImplAttributes ImplAttributes =
            MethodImplAttributes.Runtime |
            MethodImplAttributes.Managed;

        private const MethodAttributes InvokeAttributes =
            MethodAttributes.Public    |
            MethodAttributes.HideBySig |
            MethodAttributes.NewSlot   |
            MethodAttributes.Virtual;

        private static readonly Type[] _delegateCtorSignature = { typeof(object), typeof(IntPtr) };

        private static Type MakeDelegateType(Type[] parameters, Type returnType, string name)
        {
            TypeBuilder builder = _modBuilder.DefineType(name, DelegateTypeAttributes, typeof(MulticastDelegate));

            builder.DefineConstructor(CtorAttributes, CallingConventions.Standard, _delegateCtorSignature).SetImplementationFlags(ImplAttributes);

            builder.DefineMethod("Invoke", InvokeAttributes, returnType, parameters).SetImplementationFlags(ImplAttributes);

            return builder.CreateTypeInfo();
        }
    }
}