Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Emitting function with optional parameter

I have the following C# code:

public static double f(double x1, double x2 = 1)
{
    return x1 * x2;
}

And here it's IL code (ILSpy):

.method public hidebysig static 
    float64 f (
        float64 x1,
        [opt] float64 x2
    ) cil managed 
{
    .param [2] = float64(1)
    // Method begins at RVA 0x20c6
    // Code size 4 (0x4)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: ldarg.1
    IL_0002: mul
    IL_0003: ret
} // end of method A::f

How can I get it with System.Reflection.Emit or better with the Mono.Cecil?

like image 755
Ivan Kochurkin Avatar asked Oct 06 '22 08:10

Ivan Kochurkin


1 Answers

If I want to do stuff with Mono.Cecil I usually create a class / method in C# with the expected code. I then inspect it (Make sure you're running it in Release mode) with Mono.Cecil and re-create it.

So you'd need a MethodDefinition with a name, attributes and returnType parameter. The name: "f"

The attributes for your method would be : Mono.Cecil.MethodAttributes.FamANDAssem | Mono.Cecil.MethodAttributes.Family | Mono.Cecil.MethodAttributes.Static | Mono.Cecil.MethodAttributes.HideBySig

And the return type (of type Mono.Cecil.TypeReference as System.Double )

As for the parameters, there are two ParameterDefinition you can add with target.Parameters.Add()

One of your parameters has a default value so its attributes must be Mono.Cecil.ParameterAttributes.Optional | Mono.Cecil.ParameterAttributes.HasDefault and its Constant set to 1.0 (In your case)

Now for the method body:

target.Body.GetILProcessor();  // target is your `MethodDefinition` object.

After inspecting the instructions from target.Body.Instructions, we see the following codes :

IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: mul
IL_0003: stloc.0
IL_0004: br.s IL_0007
IL_0005: ldloc.0
IL_0007: ret

So just add the codes in the right sequence

 processor.Append(OpCodes.Ldarg_0);

After that, inject / save your MethodDefinition to the respective assembly.

My assembly inspector code looks something like this:

 private static void EnumerateAssembly(AssemblyDefinition assembly)
        {
            foreach (var module in assembly.Modules)
            {
                foreach (var type in module.GetAllTypes())
                {
                    foreach (var field in type.Fields)
                    {
                        Debug.Print(field.ToString());
                    }
                    foreach (var method in type.Methods)
                    {
                        Debug.Print(method.ToString());
                        foreach (var instruction in method.Body.Instructions)
                        {
                            Debug.Print(instruction.ToString());
                        }
                    }
                }
            }
        }
like image 90
Alex Avatar answered Oct 10 '22 02:10

Alex