/
Emit.CallVirtual.cs
89 lines (75 loc) · 3.08 KB
/
Emit.CallVirtual.cs
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
using Sigil.Impl;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
namespace Sigil
{
public partial class Emit<DelegateType>
{
/// <summary>
/// Calls the given method virtually. Pops its arguments in reverse order (left-most deepest in the stack), and pushes the return value if it is non-void.
///
/// The `this` reference should appear before any arguments (deepest in the stack).
///
/// The method invoked at runtime is determined by the type of the `this` reference.
///
/// If the method invoked shouldn't vary (or if the method is static), use Call instead.
/// </summary>
public Emit<DelegateType> CallVirtual(MethodInfo method, Type constrained = null, Type[] arglist = null)
{
if (method == null)
{
throw new ArgumentNullException("method");
}
if (method.IsStatic)
{
throw new ArgumentException("Only non-static methods can be called using CallVirtual, found " + method);
}
if (HasFlag(method.CallingConvention, CallingConventions.VarArgs) && !HasFlag(method.CallingConvention, CallingConventions.Standard))
{
if (arglist == null)
{
throw new InvalidOperationException("When calling a VarArgs method, arglist must be set");
}
}
var expectedParams = ((LinqArray<ParameterInfo>)method.GetParameters()).Select(s => TypeOnStack.Get(s.ParameterType)).ToList();
var declaring = method.DeclaringType;
if (declaring.IsValueType)
{
declaring = declaring.MakePointerType();
}
// "this" parameter
expectedParams.Insert(0, TypeOnStack.Get(declaring));
if (arglist != null)
{
expectedParams.AddRange(LinqAlternative.Select(arglist, t => TypeOnStack.Get(t)));
}
var resultType = method.ReturnType == typeof(void) ? null : TypeOnStack.Get(method.ReturnType);
// Shove the constrained prefix in if it's supplied
if (constrained != null)
{
UpdateState(OpCodes.Constrained, constrained, Wrap(StackTransition.None(), "CallVirtual"));
}
IEnumerable<StackTransition> transitions;
if (resultType != null)
{
transitions =
new[]
{
new StackTransition(expectedParams.Reverse().AsEnumerable(), new [] { resultType })
};
}
else
{
transitions =
new[]
{
new StackTransition(expectedParams.Reverse().AsEnumerable(), new TypeOnStack[0])
};
}
UpdateState(OpCodes.Callvirt, method, Wrap(transitions, "CallVirtual"), arglist: arglist);
return this;
}
}
}