I want to be able to generate il for the code
InjectBake.AddConstruction(typeof(Tests.TestConst), new Func<object[], IServiceProvider, object>[]
{
Test1,
Test2
});
I saw this code in decompilation , I dot`t know how to get its construction ,
newobj instance void class [mscorlib]System.Func`3<object[], class [mscorlib]System.IServiceProvider, object>::.ctor(object, native int)
Suposing you want to generate the folowing class:
using System;
class Test
{
static string M(int i) { return i.ToString(); }
void Foo()
{
Func conv = M;
}
}
You can use Cecilifier to generate the following code:
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using System;
using System.Linq;
using BindingFlags = System.Reflection.BindingFlags;
using Cecilifier.Runtime;
public class SnippetRunner
{
public static void Main(string[] args)
{
using(var assembly = AssemblyDefinition.CreateAssembly(new AssemblyNameDefinition("Test", Version.Parse("1.0.0.0")), "moduleName", ModuleKind.Dll))
{
var t1 = new TypeDefinition("", "Test", TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.NotPublic, assembly.MainModule.TypeSystem.Object);
assembly.MainModule.Types.Add(t1);
t1.BaseType = assembly.MainModule.TypeSystem.Object;
var Test_ctor_ = new MethodDefinition(".ctor", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName, assembly.MainModule.TypeSystem.Void);
t1.Methods.Add(Test_ctor_);
var il1 = Test_ctor_.Body.GetILProcessor();
var Ldarg_02 = il1.Create(OpCodes.Ldarg_0);
il1.Append(Ldarg_02);
var Call3 = il1.Create(OpCodes.Call, assembly.MainModule.ImportReference(TypeHelpers.DefaultCtorFor(t1.BaseType)));
il1.Append(Call3);
var Ret4 = il1.Create(OpCodes.Ret);
il1.Append(Ret4);
//Method : M
var Test_M_int32 = new MethodDefinition("M", MethodAttributes.Static | MethodAttributes.Private | MethodAttributes.HideBySig, assembly.MainModule.TypeSystem.String);
t1.Methods.Add(Test_M_int32);
var il_Test_M_int32 = Test_M_int32.Body.GetILProcessor();
//Parameters of 'static string M(int i) { return i.ToString(); }'
var i5 = new ParameterDefinition("i", ParameterAttributes.None, assembly.MainModule.TypeSystem.Int32);
Test_M_int32.Parameters.Add(i5);
//return i.ToString();
var Ldarga6 = il_Test_M_int32.Create(OpCodes.Ldarga, i5);
il_Test_M_int32.Append(Ldarga6);
var Call7 = il_Test_M_int32.Create(OpCodes.Call, assembly.MainModule.ImportReference(TypeHelpers.ResolveMethod("System.Private.CoreLib", "System.Int32", "ToString",System.Reflection.BindingFlags.Default|System.Reflection.BindingFlags.Instance|System.Reflection.BindingFlags.Public,"")));
il_Test_M_int32.Append(Call7);
var Ret8 = il_Test_M_int32.Create(OpCodes.Ret);
il_Test_M_int32.Append(Ret8);
//Method : Foo
var Test_Foo_ = new MethodDefinition("Foo", MethodAttributes.Private | MethodAttributes.HideBySig, assembly.MainModule.TypeSystem.Void);
t1.Methods.Add(Test_Foo_);
var il_Test_Foo_ = Test_Foo_.Body.GetILProcessor();
//Func<int, string> conv = M;
var lv_conv9 = new VariableDefinition(assembly.MainModule.ImportReference(typeof(System.Func<,>)).MakeGenericInstanceType(assembly.MainModule.TypeSystem.Int32,assembly.MainModule.TypeSystem.String));
Test_Foo_.Body.Variables.Add(lv_conv9);
var Ldnull10 = il_Test_Foo_.Create(OpCodes.Ldnull);
il_Test_Foo_.Append(Ldnull10);
var Ldftn11 = il_Test_Foo_.Create(OpCodes.Ldftn, Test_M_int32);
il_Test_Foo_.Append(Ldftn11);
var Newobj12 = il_Test_Foo_.Create(OpCodes.Newobj, assembly.MainModule.ImportReference(TypeHelpers.ResolveMethod("System.Private.CoreLib", "System.Func`2", ".ctor",System.Reflection.BindingFlags.Default|System.Reflection.BindingFlags.Instance|System.Reflection.BindingFlags.Public,"System.Int32,System.String", "System.Object", "System.IntPtr")));
il_Test_Foo_.Append(Newobj12);
var Stloc13 = il_Test_Foo_.Create(OpCodes.Stloc, lv_conv9);
il_Test_Foo_.Append(Stloc13);
var Ret14 = il_Test_Foo_.Create(OpCodes.Ret);
il_Test_Foo_.Append(Ret14);
PrivateCoreLibFixer.FixReferences(assembly.MainModule);
assembly.Write(args[0]);
}
}
}
In a simplified way, you'll need to:
Get the method definition for which you want to change / add the code to. (Line #48)
Get the ILProcessor for that method (Line #50)
Inject the NewObj IL (Line #59)
Step 3 is the most important :
var Newobj12 = il_Test_Foo_.Create(
OpCodes.Newobj,
assembly.MainModule.ImportReference(
TypeHelpers.ResolveMethod(
"System.Private.CoreLib",
"System.Func`2",
".ctor",
System.Reflection.BindingFlags.Default|System.Reflection.BindingFlags.Instance|System.Reflection.BindingFlags.Public,
"System.Int32,System.String",
"System.Object",
"System.IntPtr")));
Basically it is simply calling ILProcessor.Create() passing:
For the step #2 above Cecilifier is using a helper function TypeHelpers.ResolveMethod() which basically uses reflection to retrieve the method and passing it to ModuleDefinition.ImportReference()
.