ILGenerator
ILGeneratorRequest requests an ILGenerator from ConstructorBuilder or MethodBuilder.
// Request to create method builder for "MyFunction"
IRequestFor<MethodBuilder> myFunc = new MethodBuilderRequest("MyFunction", MethodAttributes.Public | MethodAttributes.Virtual, null, typeof(int), typeof(int));
// Request to create il generator
IRequestFor<ILGenerator> il = ILGeneratorRequest.Default;
When all opcode requests have been appended to il request, then il request is added to method request.
// Add il to "MyFunction"
myFunc = myFunc.Append(il);
Emit
Emit is a request to emit an Opcode to ILGenerator. Opcodes are listed on MSDN.
// Load first argument
il = il.Append(new Emit(OpCodes.Ldarg_1));
Extension method IRequestFor<ILGenerator>.Add() and ILGenerator.Add() has shorter notation.
// Load constant 5
il = il.Append(OpCodes.Ldc_I4_5);
Label
LabelRequest is a request to create a label.
// Define labels
IRequestFor<Label> trueLabel = new LabelRequest(), falseLabel = new LabelRequest();
MarkLabelRequest adds Label to il stream.
il = il.Append(new MarkLabelRequest(trueLabel));
LocalBuilder
LocalBuilderRequest is a request to create a local variable.
// Create local variable
IRequestFor<LocalBuilder> local = new LocalBuilderRequest(typeof(int));
// Add to il request
il = il.Append(local);
The local request can be used with OpCodes.Stloc.
// Write '5' to local
il = il.Append(OpCodes.Ldc_I4_5)
.Append(OpCodes.Stloc, local);
And with OpCodes.Ldloc.
// Read from local
il = il.Append(OpCodes.Ldloc, local);
EmitCast
EmitCast is a request to emit an opcode that casts one type to another. Reference types can be up and down-casted, value types can be boxed and unboxed.
// Load first argument (object)
il = il.Append(OpCodes.Ldarg_1);
// Add request to emit opcode to box int to object
il = il.Append(new EmitCast(typeof(int), typeof(object)));
// Return
il = il.Append(OpCodes.Ret);
Indirect load and store
In this example, we get a pointer to insides of a box object.
// Load first argument (object)
il = il.Append(OpCodes.Ldarg_1);
// Get Unbox -method
MethodInfo unbox = typeof(System.Runtime.CompilerServices.Unsafe).GetMethod("Unbox")!.MakeGenericMethod(typeof(int));
// Load pointer on stack
il = il.Append(OpCodes.Call, unbox);
EmitLdInd is a request to emit an opcode to read a value from pointer into stack.
// Load indirect (read from pointer)
il = il.Append(new EmitLdInd(typeof(int)));
EmitStInd is a request to emit an opcode to write the value on stack to memory pointer.
// Double value
il = il.Append(OpCodes.Dup).Append(OpCodes.Add);
// Store indirect (write to pointer)
il = il.Append(new EmitStInd(typeof(int)));
Full Example
Full example
using System;
using System.Reflection;
using System.Reflection.Emit;
using Avalanche.Emit;
using Avalanche.Service;
public class emit
{
public static void Run()
{
// LabelRequest & MarkLabelRequest
{
// Service that handles assembly builder, module builder and type builder
IService service = Services.Create(Avalanche.Emit.Module.Instance);
// Request to create type builder
IRequestFor<TypeBuilder> typeBuilder = new TypeBuilderRequest("MyClassOp1", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass);
// <00>
// Request to create method builder for "MyFunction"
IRequestFor<MethodBuilder> myFunc = new MethodBuilderRequest("MyFunction", MethodAttributes.Public | MethodAttributes.Virtual, null, typeof(int), typeof(int));
// Request to create il generator
IRequestFor<ILGenerator> il = ILGeneratorRequest.Default;
// </00>
// <03>
// Define labels
IRequestFor<Label> trueLabel = new LabelRequest(), falseLabel = new LabelRequest();
// </03>
// "return x < 5 ? 0 : 1"
// <01>
// Load first argument
il = il.Append(new Emit(OpCodes.Ldarg_1));
// </01>
// <02>
// Load constant 5
il = il.Append(OpCodes.Ldc_I4_5);
// </02>
il = il.Append(OpCodes.Clt).Append(OpCodes.Brtrue_S, falseLabel);
// <04>
il = il.Append(new MarkLabelRequest(trueLabel));
// </04>
// true: return 1;
il = il.Append(OpCodes.Ldc_I4_1).Append(OpCodes.Ret);
// true: return 1;
il = il.Append(new MarkLabelRequest(trueLabel)).Append(OpCodes.Ldc_I4_1).Append(OpCodes.Ret);
// false: return 0;
il = il.Append(new MarkLabelRequest(falseLabel)).Append(OpCodes.Ldc_I4_0).Append(OpCodes.Ret);
// <0B>
// Add il to "MyFunction"
myFunc = myFunc.Append(il);
// </0B>
// Add "MyFunction"
typeBuilder = typeBuilder.Append(myFunc);
// Request to build type
IRequestFor<Type> type = new TypeRequest(typeBuilder);
// Request to create new instance
IRequestFor<Object> newRequest = type.NewRequest();
// Print request
newRequest.PrintTreeTo(Console.Out);
// Get instance
object instance = service.GetRequired<IRequestFor<object>, object>(newRequest);
// Evaluate: value = 4 < 5 ? 0 : 1;
int value = (int)instance.GetType().GetMethod("MyFunction", new Type[] { typeof(int) })!.Invoke(instance, new object[] { 4 })!;
// Print
Console.WriteLine(value);
}
// LocalBuilder
{
// Service that handles assembly builder, module builder and type builder
IService service = Services.Create(Avalanche.Emit.Module.Instance);
// Request to create type builder
IRequestFor<TypeBuilder> typeBuilder = new TypeBuilderRequest("MyClassOp2", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass);
// Request to create method
IRequestFor<MethodBuilder> myFunc = new MethodBuilderRequest("MyFunction", MethodAttributes.Public | MethodAttributes.Virtual, null, typeof(int));
// Request to create il generator
IRequestFor<ILGenerator> il = ILGeneratorRequest.Default;
// <05>
// Create local variable
IRequestFor<LocalBuilder> local = new LocalBuilderRequest(typeof(int));
// Add to il request
il = il.Append(local);
// </05>
// <06>
// Write '5' to local
il = il.Append(OpCodes.Ldc_I4_5)
.Append(OpCodes.Stloc, local);
// </06>
// <07>
// Read from local
il = il.Append(OpCodes.Ldloc, local);
// </07>
// Read from local again
il = il.Append(OpCodes.Ldloc, local);
// Sum up
il = il.Append(OpCodes.Add);
// Return
il = il.Append(OpCodes.Ret);
// Add il to "MyFunction"
myFunc = myFunc.Append(il);
// Add "MyFunction"
typeBuilder = typeBuilder.Append(myFunc);
// Request to build type
IRequestFor<Type> type = new TypeRequest(typeBuilder);
// Request to create new instance
IRequestFor<Object> newRequest = type.NewRequest();
// Print request
newRequest.PrintTreeTo(Console.Out);
// Get instance
object instance = service.GetRequired<IRequestFor<object>, object>(newRequest);
// Call function
int value = (int)instance.GetType().GetMethod("MyFunction", Type.EmptyTypes)!.Invoke(instance, null)!;
// Print
Console.WriteLine(value);
}
// EmitCast
{
// Service that handles assembly builder, module builder and type builder
IService service = Services.Create(Avalanche.Emit.Module.Instance);
// Request to create type builder
IRequestFor<TypeBuilder> typeBuilder = new TypeBuilderRequest("MyClassOp3", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass);
// Request to create method
IRequestFor<MethodBuilder> myFunc = new MethodBuilderRequest(
name: "MyFunction",
methodAttributes: MethodAttributes.Public | MethodAttributes.Virtual,
attributes: null,
returnType: typeof(object),
parameterTypes: typeof(int));
// Request to create il generator
IRequestFor<ILGenerator> il = ILGeneratorRequest.Default;
// <10>
// Load first argument (object)
il = il.Append(OpCodes.Ldarg_1);
// Add request to emit opcode to box int to object
il = il.Append(new EmitCast(typeof(int), typeof(object)));
// Return
il = il.Append(OpCodes.Ret);
// </10>
// Add il to "MyFunction"
myFunc = myFunc.Append(il);
// Add "MyFunction"
typeBuilder = typeBuilder.Append(myFunc);
// Request to build type
IRequestFor<Type> type = new TypeRequest(typeBuilder);
// Request to create new instance
IRequestFor<Object> newRequest = type.NewRequest();
// Print request
newRequest.PrintTreeTo(Console.Out);
// Get instance
object instance = service.GetRequired<IRequestFor<object>, object>(newRequest);
// Box
object box = 9;
// Call function
int value = (int)instance.GetType().GetMethod("MyFunction", new Type[] { typeof(int) })!.Invoke(instance, new object[] { box })!;
// Print
Console.WriteLine(value);
}
// LdIndEmit & StIndEmit
{
// Service that handles assembly builder, module builder and type builder
IService service = Services.Create(Avalanche.Emit.Module.Instance);
// Request to create type builder
IRequestFor<TypeBuilder> typeBuilder = new TypeBuilderRequest("DoublerClass", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass);
// Request to create method
IRequestFor<MethodBuilder> myFunc = new MethodBuilderRequest(
name: "Double",
methodAttributes: MethodAttributes.Public | MethodAttributes.Virtual,
attributes: null,
returnType: null,
parameterTypes: typeof(object));
// Request to create il generator
IRequestFor<ILGenerator> il = ILGeneratorRequest.Default;
// <20>
// Load first argument (object)
il = il.Append(OpCodes.Ldarg_1);
// Get Unbox -method
MethodInfo unbox = typeof(System.Runtime.CompilerServices.Unsafe).GetMethod("Unbox")!.MakeGenericMethod(typeof(int));
// Load pointer on stack
il = il.Append(OpCodes.Call, unbox);
// </20>
// Copy the pointer to stack again, for load and store
il = il.Append(OpCodes.Dup);
// <21>
// Load indirect (read from pointer)
il = il.Append(new EmitLdInd(typeof(int)));
// </21>
// <22>
// Double value
il = il.Append(OpCodes.Dup).Append(OpCodes.Add);
// Store indirect (write to pointer)
il = il.Append(new EmitStInd(typeof(int)));
// </22>
// Return
il = il.Append(OpCodes.Ret);
// Add il to "Double"
myFunc = myFunc.Append(il);
// Add "Double"
typeBuilder = typeBuilder.Append(myFunc);
// Request to build type
IRequestFor<Type> type = new TypeRequest(typeBuilder);
// Request to create new instance
IRequestFor<Object> newRequest = type.NewRequest();
// Print request
newRequest.PrintTreeTo(Console.Out);
// Get instance
object instance = service.GetRequired<IRequestFor<object>, object>(newRequest);
// Box
object box = 4;
// Call "Double"
instance.GetType().GetMethod("Double", new Type[] { typeof(object) })!.Invoke(instance, new object[] { box });
// Print box -> "8"
Console.WriteLine(box);
}
}
}