aboutsummaryrefslogtreecommitdiff
path: root/PureData/DynamicMethod.cs
blob: 749bb1a826362816313a210e174c1670087c776c (plain)
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
using System;
using System.Reflection;
using System.Reflection.Emit;

namespace PureData
{
    public delegate void DynamicMethodBang(External target);
    public delegate void DynamicMethodFloat(External target,float f);
    public delegate void DynamicMethodSymbol(External target,Symbol s);
    public delegate void DynamicMethodPointer(External target,Pointer p);
    public delegate void DynamicMethodList(External target,Atom[] l);
    public delegate void DynamicMethodAnything(External target,int i,Symbol s,Atom[] l);
    public delegate void DynamicMethodObject(External target,int i,object o);

    class DynamicMethods
    {
        private static Delegate CreateIntern(Delegate del, Type dyntype)
        {
            MethodInfo dynmethod = dyntype.GetMethod("Invoke");
            ParameterInfo[] dynparms = dynmethod.GetParameters();
            MethodInfo method = del.Method;

#if DEBUG
            if (dynmethod.ReturnType != typeof(void)) throw new ArgumentException("Return type must be void");

            ParameterInfo[] parms = method.GetParameters();
            int numparms = parms.Length;

            if (dynparms.Length != numparms + 1) throw new ArgumentException("Number of parameters don't match");

            for (int i = 0; i < numparms; ++i)
                if (dynparms[i + 1].ParameterType != parms[i].ParameterType) throw new ArgumentException("Parameter types don't match");
#endif

            Type[] argtypes = new Type[dynparms.Length];
            for (int i = 0; i < dynparms.Length; ++i)
                argtypes[i] = dynparms[i].ParameterType;

            // Create dynamic method and obtain its IL generator to inject code.
            DynamicMethod dynam = new DynamicMethod("dummy", typeof(void), argtypes, typeof(DynamicMethods));
            ILGenerator il = dynam.GetILGenerator();

            #region IL Code generation
            // If method isn't static push target instance on top of stack.
            if (!method.IsStatic)
                // Argument 0 of dynamic method is target instance.
                il.Emit(OpCodes.Ldarg_0);

            // Push parameters onto the stack
            for (int i = 0; i < dynparms.Length - 1; ++i)
                il.Emit(OpCodes.Ldarg, i + 1);

            // Perform actual call.
            // If method is not final a callvirt is required otherwise a normal call will be emitted.
            il.Emit(method.IsFinal ? OpCodes.Call : OpCodes.Callvirt, method);

            // Emit return opcode.
            il.Emit(OpCodes.Ret);
            #endregion

            return dynam.CreateDelegate(dyntype);
        }

        #region Specialized DynamicMethod makers
        public static DynamicMethodBang Create(Public.MethodBang method)
        {
            return (DynamicMethodBang)CreateIntern(method, typeof(DynamicMethodBang));
        }

        public static DynamicMethodFloat Create(Public.MethodFloat method)
        {
            return (DynamicMethodFloat)CreateIntern(method,typeof(DynamicMethodFloat));
        }

        public static DynamicMethodSymbol Create(Public.MethodSymbol method)
        {
            return (DynamicMethodSymbol)CreateIntern(method,typeof(DynamicMethodSymbol));
        }

        public static DynamicMethodPointer Create(Public.MethodPointer method)
        {
            return (DynamicMethodPointer)CreateIntern(method,typeof(DynamicMethodPointer));
        }

        public static DynamicMethodList Create(Public.MethodList method)
        {
            return (DynamicMethodList)CreateIntern(method,typeof(DynamicMethodList));
        }

        public static DynamicMethodAnything Create(Public.MethodAnything method)
        {
            return (DynamicMethodAnything)CreateIntern(method,typeof(DynamicMethodAnything));
        }

        public static DynamicMethodObject Create(Public.MethodObject method)
        {
            return (DynamicMethodObject)CreateIntern(method, typeof(DynamicMethodObject));
        }
        #endregion
    }
}