Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# reflection: If ... else?

I'm currently facing new problem with operators. Using following code, I want to make output that would be same as when using if ... else pair in C#.

var method = new DynamicMethod("dummy", null, Type.EmptyTypes);
var g = method.GetILGenerator();

g.Emit(OpCodes.Ldstr, "string");
g.Emit(OpCodes.Ldstr, "string");
g.Emit(OpCodes.Call, typeof(String).GetMethod("op_Equality", new Type[]{typeof(string), typeof(string)}));
g.Emit(OpCodes.Ldc_I4, 0);
g.Emit(OpCodes.Ceq);
g.Emit(OpCodes.Brtrue_S, );

var action = (Action)method.CreateDelegate(typeof(Action));
action();

Console.Read();

My questions are:

  1. How can I get the address of an instruction to pass it as a parameter for branch opcodes?
  2. Is there any difference between BR and BR_S, Brtrue and Brtrue_S, Brfalse and Brfalse_S and similar instructions?

Thanks.

like image 243
user35443 Avatar asked Jun 21 '12 13:06

user35443


2 Answers

  1. You can use a combination of the DefineLabel and MarkLabel methods to determine the target location of the branch. To do so, declare the labels you'll need - something like equal and notequal. Then you can mark the spots in your IL where the labels should be present. Once this is done, you can set the target of your branch instruction to this label.

    // Define labels
    Label equal = g.DefineLabel();
    Label notEqual = g.DefineLabel();
    Label endOfMethod = g.DefineLabel();
    // your logic here
    g.Emit(OpCodes.Brtrue_S, equal);
    g.MarkLabel(equal);
    // some code if they are equal
    g.MarkLabel(notEqual);
    // some code if they are not equal
    g.MarkLabel(endOfMethod); // this marks the return point
    g.Emit(OpCodes.Ret);
    
  2. The difference between Br, Brtrue, and Brfalse and their _S counterparts is the length of the jump. The _S denotes short form; the target instruction is a 1-byte signed offset from the next instruction. In the standard (non-short) form, the target is represented by a 4-byte offset.

like image 118
goric Avatar answered Sep 20 '22 15:09

goric


ILGenerator.ILOffset gives you the current offset in the IL stream, if that's what you want. You can use DefineLabel and MarkLabel, too, as goric suggested.

The only difference between brtrue.s and brtrue is that brtrue.s is the short version of brtrue. brtrue uses a 4-byte offset and brtrue.s uses a 1-byte offset. The same applies for brfalse and brfalse.s (and br/br.s).

Those are not the only short versions of an IL instruction, there are also other short instructions, like ldc.i4.0 - ldc.i4.8 for loading integers. Those are mainly useful for generating smaller binaries, but i don't think there is a big difference otherwise.

like image 37
Botz3000 Avatar answered Sep 20 '22 15:09

Botz3000