using Mono.Cecil;
using Mono.Cecil.Cil;
/// <summary>Reject a null raw stack before the native ThreadContext writes to it.</summary>
internal static class ThreadStackRuntimePatch
{
public const string GuardName = "XfeRequireThreadStackV1";
public const string FatalName = "XfeFailThreadStackV1";
public const string AllocateName = "XfeAllocateThreadStackV1";
public const string LargestName = "XfeRawLargestFreeRunPagesV1";
public static bool Apply(AssemblyDefinition assembly)
{
var module = assembly.MainModule;
var thread = module.Types.Single(type => type.FullName == "Cosmos.Kernel.Core.Scheduler.SchedulerThread");
var initialize = thread.Methods.Single(method => method.Name == "InitializeStack");
var existing = thread.Methods.SingleOrDefault(method => method.Name == GuardName);
if (existing is not null)
{
if (existing.Body.Instructions.Count != 7 || initialize.Body.Instructions.Count(instruction => instruction.Operand is MethodReference method && method.FullName == existing.FullName) != 1 ||
thread.Methods.Count(method => method.Name is FatalName or AllocateName or LargestName or "XfeRawLargestFreeRunPagesExportV1" or "XfeRawFreePageCountV1" or "XfeRawTotalPageCountV1") != 6)
throw new InvalidOperationException("Unexpected raw thread stack patch shape.");
return true;
}
if (initialize.Body.CodeSize != 84 || initialize.Body.Variables.Count != 3 || initialize.Body.ExceptionHandlers.Count != 0 || initialize.Body.Instructions[0].OpCode != OpCodes.Ldarg_0 ||
initialize.Body.Instructions.Single(instruction => instruction.Offset == 0x0c) is not { OpCode.Code: Code.Call, Operand: MethodReference allocator } raw ||
allocator.Name != "Alloc" || allocator.DeclaringType.FullName != "Cosmos.Kernel.Core.Memory.MemoryOp" ||
raw.Next.Operand is not MethodReference store || store.Name != "set_StackBase")
throw new InvalidOperationException("Pinned raw thread stack allocation changed; refusing to guess.");
var serial = module.Types.Single(type => type.FullName == "Cosmos.Kernel.Core.IO.Serial");
var pageAllocator = module.Types.Single(type => type.FullName == "Cosmos.Kernel.Core.Memory.PageAllocator");
var write = serial.Methods.Single(method => method.Name == "WriteString" && method.Parameters.Count == 1 && method.Parameters[0].ParameterType.MetadataType == MetadataType.String);
var number = serial.Methods.Single(method => method.Name == "WriteNumber" && method.Parameters.Count == 2 && method.Parameters[0].ParameterType.MetadataType == MetadataType.UInt64);
var free = pageAllocator.Methods.Single(method => method.Name == "get_FreePageCount");
var total = pageAllocator.Fields.Single(field => field.Name == "TotalPageCount");
var largest = CreateLargestRun(module, thread, pageAllocator, total);
var fatal = new MethodDefinition(FatalName, MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.HideBySig, module.TypeSystem.Void)
{ ImplAttributes = MethodImplAttributes.NoInlining };
fatal.Parameters.Add(new("requestedBytes", ParameterAttributes.None, module.TypeSystem.UInt64));
thread.Methods.Add(fatal);
var fi = fatal.Body.GetILProcessor();
fi.Append(fi.Create(OpCodes.Call, module.Types.Single(type => type.FullName == "Cosmos.Kernel.Core.CPU.InternalCpu").Methods.Single(method => method.Name == "DisableInterruptsScope" && method.Parameters.Count == 0)));
fi.Append(fi.Create(OpCodes.Pop));
Text("\nXFE_RUNTIME_STACK_OOM|RequestedBytes|"); fi.Append(fi.Create(OpCodes.Ldarg_0)); Number();
Text("|RawFreePages|"); fi.Append(fi.Create(OpCodes.Call, free)); Number();
Text("|RawTotalPages|"); fi.Append(fi.Create(OpCodes.Ldsfld, total)); Number();
Text("|RawLargestFreeRunPages|"); fi.Append(fi.Create(OpCodes.Call, largest)); Number();
Text("|Action|GuestHalt\n[FAILFAST] Raw thread stack allocation failed; guest halted.\n");
var stop = fi.Create(OpCodes.Br, fi.Create(OpCodes.Nop)); stop.Operand = stop; fi.Append(stop); fatal.Body.MaxStackSize = 2;
var guard = new MethodDefinition(GuardName, MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig, allocator.ReturnType)
{ ImplAttributes = MethodImplAttributes.AggressiveInlining };
guard.Parameters.Add(new("result", ParameterAttributes.None, allocator.ReturnType));
guard.Parameters.Add(new("requestedBytes", ParameterAttributes.None, module.TypeSystem.UInt64));
thread.Methods.Add(guard);
var gi = guard.Body.GetILProcessor(); var valid = gi.Create(OpCodes.Ldarg_0); var halted = gi.Create(OpCodes.Br, valid); halted.Operand = halted;
gi.Append(gi.Create(OpCodes.Ldarg_0)); gi.Append(gi.Create(OpCodes.Brtrue, valid));
gi.Append(gi.Create(OpCodes.Ldarg_1)); gi.Append(gi.Create(OpCodes.Call, fatal)); gi.Append(halted); gi.Append(valid); gi.Append(gi.Create(OpCodes.Ret)); guard.Body.MaxStackSize = 1;
var ii = initialize.Body.GetILProcessor(); var size = ii.Create(OpCodes.Ldarg, initialize.Parameters[3]); var convert = ii.Create(OpCodes.Conv_U8); var check = ii.Create(OpCodes.Call, guard);
ii.InsertAfter(raw, size); ii.InsertAfter(size, convert); ii.InsertAfter(convert, check);
initialize.Body.MaxStackSize = Math.Max(3, initialize.Body.MaxStackSize);
var allocate = new MethodDefinition(AllocateName, MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.HideBySig, allocator.ReturnType);
allocate.Parameters.Add(new("size", ParameterAttributes.None, module.TypeSystem.UInt32));
allocate.Body.InitLocals = true;
var result = new VariableDefinition(allocator.ReturnType); allocate.Body.Variables.Add(result); thread.Methods.Add(allocate);
var ai = allocate.Body.GetILProcessor(); var success = ai.Create(OpCodes.Ldloc, result);
ai.Append(ai.Create(OpCodes.Ldarg_0)); ai.Append(ai.Create(OpCodes.Call, allocator)); ai.Append(ai.Create(OpCodes.Stloc, result));
ai.Append(ai.Create(OpCodes.Ldloc, result)); ai.Append(ai.Create(OpCodes.Brtrue, success));
var gc = module.Types.Single(type => type.FullName == "Cosmos.Kernel.Core.Memory.GarbageCollector.GarbageCollector");
ai.Append(ai.Create(OpCodes.Call, gc.Methods.Single(method => method.Name == "Collect" && method.Parameters.Count == 0))); ai.Append(ai.Create(OpCodes.Pop));
ai.Append(ai.Create(OpCodes.Ldarg_0)); ai.Append(ai.Create(OpCodes.Call, allocator)); ai.Append(ai.Create(OpCodes.Stloc, result)); ai.Append(success); ai.Append(ai.Create(OpCodes.Ret)); allocate.Body.MaxStackSize = 1;
raw.Operand = allocate;
Export("XfeRawFreePageCountV1", "XfeRuntimeRawFreePageCount", OpCodes.Call, free);
Export("XfeRawTotalPageCountV1", "XfeRuntimeRawTotalPageCount", OpCodes.Ldsfld, total);
Export("XfeRawLargestFreeRunPagesExportV1", "XfeRuntimeRawLargestFreeRunPages", OpCodes.Call, largest);
return false;
void Text(string text) { fi.Append(fi.Create(OpCodes.Ldstr, text)); fi.Append(fi.Create(OpCodes.Call, write)); }
void Number() { fi.Append(fi.Create(OpCodes.Ldc_I4_0)); fi.Append(fi.Create(OpCodes.Call, number)); }
void Export(string name, string entryPoint, OpCode load, MemberReference source)
{
var method = new MethodDefinition(name, MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig, module.TypeSystem.UInt64);
var attribute = new CustomAttribute(module.ImportReference(typeof(System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute).GetConstructor(Type.EmptyTypes)!));
attribute.Fields.Add(new("EntryPoint", new CustomAttributeArgument(module.TypeSystem.String, entryPoint)));
method.CustomAttributes.Add(attribute); thread.Methods.Add(method);
var il = method.Body.GetILProcessor();
il.Append(source is MethodReference call ? il.Create(load, call) : il.Create(load, (FieldReference)source)); il.Append(il.Create(OpCodes.Ret)); method.Body.MaxStackSize = 1;
}
}
private static MethodDefinition CreateLargestRun(ModuleDefinition module, TypeDefinition owner, TypeDefinition allocator, FieldDefinition total)
{
var method = new MethodDefinition(LargestName, MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig, module.TypeSystem.UInt64);
owner.Methods.Add(method);
var cpu = module.Types.Single(type => type.FullName == "Cosmos.Kernel.Core.CPU.InternalCpu");
var disable = cpu.Methods.Single(candidate => candidate.Name == "DisableInterruptsScope" && candidate.Parameters.Count == 0);
var scopeType = disable.ReturnType.Resolve(); var dispose = scopeType.Methods.Single(candidate => candidate.Name == "Dispose" && candidate.Parameters.Count == 0);
var ratField = allocator.Fields.Single(field => field.Name == "s_mRAT");
var scope = new VariableDefinition(disable.ReturnType); var rat = new VariableDefinition(ratField.FieldType);
var count = new VariableDefinition(module.TypeSystem.UInt64); var index = new VariableDefinition(module.TypeSystem.UInt64);
var run = new VariableDefinition(module.TypeSystem.UInt64); var best = new VariableDefinition(module.TypeSystem.UInt64);
foreach (var local in new[] { scope, rat, count, index, run, best }) method.Body.Variables.Add(local);
method.Body.InitLocals = true;
var il = method.Body.GetILProcessor(); var finish = il.Create(OpCodes.Ldloca, scope); var test = il.Create(OpCodes.Ldloc, index);
var body = il.Create(OpCodes.Ldloc, rat); var used = il.Create(OpCodes.Ldc_I4_0); var advance = il.Create(OpCodes.Ldloc, index);
il.Append(il.Create(OpCodes.Call, disable)); il.Append(il.Create(OpCodes.Stloc, scope));
il.Append(il.Create(OpCodes.Ldsfld, ratField)); il.Append(il.Create(OpCodes.Stloc, rat));
il.Append(il.Create(OpCodes.Ldsfld, total)); il.Append(il.Create(OpCodes.Stloc, count));
il.Append(il.Create(OpCodes.Ldloc, rat)); il.Append(il.Create(OpCodes.Brfalse, finish)); il.Append(il.Create(OpCodes.Br, test));
il.Append(body); il.Append(il.Create(OpCodes.Ldloc, index)); il.Append(il.Create(OpCodes.Conv_U)); il.Append(il.Create(OpCodes.Add)); il.Append(il.Create(OpCodes.Ldind_U1)); il.Append(il.Create(OpCodes.Brtrue, used));
il.Append(il.Create(OpCodes.Ldloc, run)); il.Append(il.Create(OpCodes.Ldc_I4_1)); il.Append(il.Create(OpCodes.Conv_U8)); il.Append(il.Create(OpCodes.Add)); il.Append(il.Create(OpCodes.Stloc, run));
il.Append(il.Create(OpCodes.Ldloc, run)); il.Append(il.Create(OpCodes.Ldloc, best)); il.Append(il.Create(OpCodes.Ble_Un, advance));
il.Append(il.Create(OpCodes.Ldloc, run)); il.Append(il.Create(OpCodes.Stloc, best)); il.Append(il.Create(OpCodes.Br, advance));
il.Append(used); il.Append(il.Create(OpCodes.Conv_U8)); il.Append(il.Create(OpCodes.Stloc, run));
il.Append(advance); il.Append(il.Create(OpCodes.Ldc_I4_1)); il.Append(il.Create(OpCodes.Conv_U8)); il.Append(il.Create(OpCodes.Add)); il.Append(il.Create(OpCodes.Stloc, index));
il.Append(test); il.Append(il.Create(OpCodes.Ldloc, count)); il.Append(il.Create(OpCodes.Blt_Un, body));
il.Append(finish); il.Append(il.Create(OpCodes.Call, dispose)); il.Append(il.Create(OpCodes.Ldloc, best)); il.Append(il.Create(OpCodes.Ret)); method.Body.MaxStackSize = 2;
return method;
}
}
using Mono.Cecil;
using Mono.Cecil.Cil;
/// <summary>Reject a null raw stack before the native ThreadContext writes to it.</summary>
internal static class ThreadStackRuntimePatch
{
public const string GuardName = "XfeRequireThreadStackV1";
public const string FatalName = "XfeFailThreadStackV1";
public const string AllocateName = "XfeAllocateThreadStackV1";
public const string LargestName = "XfeRawLargestFreeRunPagesV1";
public static bool Apply(AssemblyDefinition assembly)
{
var module = assembly.MainModule;
var thread = module.Types.Single(type => type.FullName == "Cosmos.Kernel.Core.Scheduler.SchedulerThread");
var initialize = thread.Methods.Single(method => method.Name == "InitializeStack");
var existing = thread.Methods.SingleOrDefault(method => method.Name == GuardName);
if (existing is not null)
{
if (existing.Body.Instructions.Count != 7 || initialize.Body.Instructions.Count(instruction => instruction.Operand is MethodReference method && method.FullName == existing.FullName) != 1 ||
thread.Methods.Count(method => method.Name is FatalName or AllocateName or LargestName or "XfeRawLargestFreeRunPagesExportV1" or "XfeRawFreePageCountV1" or "XfeRawTotalPageCountV1") != 6)
throw new InvalidOperationException("Unexpected raw thread stack patch shape.");
return true;
}
if (initialize.Body.CodeSize != 84 || initialize.Body.Variables.Count != 3 || initialize.Body.ExceptionHandlers.Count != 0 || initialize.Body.Instructions[0].OpCode != OpCodes.Ldarg_0 ||
initialize.Body.Instructions.Single(instruction => instruction.Offset == 0x0c) is not { OpCode.Code: Code.Call, Operand: MethodReference allocator } raw ||
allocator.Name != "Alloc" || allocator.DeclaringType.FullName != "Cosmos.Kernel.Core.Memory.MemoryOp" ||
raw.Next.Operand is not MethodReference store || store.Name != "set_StackBase")
throw new InvalidOperationException("Pinned raw thread stack allocation changed; refusing to guess.");
var serial = module.Types.Single(type => type.FullName == "Cosmos.Kernel.Core.IO.Serial");
var pageAllocator = module.Types.Single(type => type.FullName == "Cosmos.Kernel.Core.Memory.PageAllocator");
var write = serial.Methods.Single(method => method.Name == "WriteString" && method.Parameters.Count == 1 && method.Parameters[0].ParameterType.MetadataType == MetadataType.String);
var number = serial.Methods.Single(method => method.Name == "WriteNumber" && method.Parameters.Count == 2 && method.Parameters[0].ParameterType.MetadataType == MetadataType.UInt64);
var free = pageAllocator.Methods.Single(method => method.Name == "get_FreePageCount");
var total = pageAllocator.Fields.Single(field => field.Name == "TotalPageCount");
var largest = CreateLargestRun(module, thread, pageAllocator, total);
var fatal = new MethodDefinition(FatalName, MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.HideBySig, module.TypeSystem.Void)
{ ImplAttributes = MethodImplAttributes.NoInlining };
fatal.Parameters.Add(new("requestedBytes", ParameterAttributes.None, module.TypeSystem.UInt64));
thread.Methods.Add(fatal);
var fi = fatal.Body.GetILProcessor();
fi.Append(fi.Create(OpCodes.Call, module.Types.Single(type => type.FullName == "Cosmos.Kernel.Core.CPU.InternalCpu").Methods.Single(method => method.Name == "DisableInterruptsScope" && method.Parameters.Count == 0)));
fi.Append(fi.Create(OpCodes.Pop));
Text("\nXFE_RUNTIME_STACK_OOM|RequestedBytes|"); fi.Append(fi.Create(OpCodes.Ldarg_0)); Number();
Text("|RawFreePages|"); fi.Append(fi.Create(OpCodes.Call, free)); Number();
Text("|RawTotalPages|"); fi.Append(fi.Create(OpCodes.Ldsfld, total)); Number();
Text("|RawLargestFreeRunPages|"); fi.Append(fi.Create(OpCodes.Call, largest)); Number();
Text("|Action|GuestHalt\n[FAILFAST] Raw thread stack allocation failed; guest halted.\n");
var stop = fi.Create(OpCodes.Br, fi.Create(OpCodes.Nop)); stop.Operand = stop; fi.Append(stop); fatal.Body.MaxStackSize = 2;
var guard = new MethodDefinition(GuardName, MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig, allocator.ReturnType)
{ ImplAttributes = MethodImplAttributes.AggressiveInlining };
guard.Parameters.Add(new("result", ParameterAttributes.None, allocator.ReturnType));
guard.Parameters.Add(new("requestedBytes", ParameterAttributes.None, module.TypeSystem.UInt64));
thread.Methods.Add(guard);
var gi = guard.Body.GetILProcessor(); var valid = gi.Create(OpCodes.Ldarg_0); var halted = gi.Create(OpCodes.Br, valid); halted.Operand = halted;
gi.Append(gi.Create(OpCodes.Ldarg_0)); gi.Append(gi.Create(OpCodes.Brtrue, valid));
gi.Append(gi.Create(OpCodes.Ldarg_1)); gi.Append(gi.Create(OpCodes.Call, fatal)); gi.Append(halted); gi.Append(valid); gi.Append(gi.Create(OpCodes.Ret)); guard.Body.MaxStackSize = 1;
var ii = initialize.Body.GetILProcessor(); var size = ii.Create(OpCodes.Ldarg, initialize.Parameters[3]); var convert = ii.Create(OpCodes.Conv_U8); var check = ii.Create(OpCodes.Call, guard);
ii.InsertAfter(raw, size); ii.InsertAfter(size, convert); ii.InsertAfter(convert, check);
initialize.Body.MaxStackSize = Math.Max(3, initialize.Body.MaxStackSize);
var allocate = new MethodDefinition(AllocateName, MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.HideBySig, allocator.ReturnType);
allocate.Parameters.Add(new("size", ParameterAttributes.None, module.TypeSystem.UInt32));
allocate.Body.InitLocals = true;
var result = new VariableDefinition(allocator.ReturnType); allocate.Body.Variables.Add(result); thread.Methods.Add(allocate);
var ai = allocate.Body.GetILProcessor(); var success = ai.Create(OpCodes.Ldloc, result);
ai.Append(ai.Create(OpCodes.Ldarg_0)); ai.Append(ai.Create(OpCodes.Call, allocator)); ai.Append(ai.Create(OpCodes.Stloc, result));
ai.Append(ai.Create(OpCodes.Ldloc, result)); ai.Append(ai.Create(OpCodes.Brtrue, success));
var gc = module.Types.Single(type => type.FullName == "Cosmos.Kernel.Core.Memory.GarbageCollector.GarbageCollector");
ai.Append(ai.Create(OpCodes.Call, gc.Methods.Single(method => method.Name == "Collect" && method.Parameters.Count == 0))); ai.Append(ai.Create(OpCodes.Pop));
ai.Append(ai.Create(OpCodes.Ldarg_0)); ai.Append(ai.Create(OpCodes.Call, allocator)); ai.Append(ai.Create(OpCodes.Stloc, result)); ai.Append(success); ai.Append(ai.Create(OpCodes.Ret)); allocate.Body.MaxStackSize = 1;
raw.Operand = allocate;
Export("XfeRawFreePageCountV1", "XfeRuntimeRawFreePageCount", OpCodes.Call, free);
Export("XfeRawTotalPageCountV1", "XfeRuntimeRawTotalPageCount", OpCodes.Ldsfld, total);
Export("XfeRawLargestFreeRunPagesExportV1", "XfeRuntimeRawLargestFreeRunPages", OpCodes.Call, largest);
return false;
void Text(string text) { fi.Append(fi.Create(OpCodes.Ldstr, text)); fi.Append(fi.Create(OpCodes.Call, write)); }
void Number() { fi.Append(fi.Create(OpCodes.Ldc_I4_0)); fi.Append(fi.Create(OpCodes.Call, number)); }
void Export(string name, string entryPoint, OpCode load, MemberReference source)
{
var method = new MethodDefinition(name, MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig, module.TypeSystem.UInt64);
var attribute = new CustomAttribute(module.ImportReference(typeof(System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute).GetConstructor(Type.EmptyTypes)!));
attribute.Fields.Add(new("EntryPoint", new CustomAttributeArgument(module.TypeSystem.String, entryPoint)));
method.CustomAttributes.Add(attribute); thread.Methods.Add(method);
var il = method.Body.GetILProcessor();
il.Append(source is MethodReference call ? il.Create(load, call) : il.Create(load, (FieldReference)source)); il.Append(il.Create(OpCodes.Ret)); method.Body.MaxStackSize = 1;
}
}
private static MethodDefinition CreateLargestRun(ModuleDefinition module, TypeDefinition owner, TypeDefinition allocator, FieldDefinition total)
{
var method = new MethodDefinition(LargestName, MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig, module.TypeSystem.UInt64);
owner.Methods.Add(method);
var cpu = module.Types.Single(type => type.FullName == "Cosmos.Kernel.Core.CPU.InternalCpu");
var disable = cpu.Methods.Single(candidate => candidate.Name == "DisableInterruptsScope" && candidate.Parameters.Count == 0);
var scopeType = disable.ReturnType.Resolve(); var dispose = scopeType.Methods.Single(candidate => candidate.Name == "Dispose" && candidate.Parameters.Count == 0);
var ratField = allocator.Fields.Single(field => field.Name == "s_mRAT");
var scope = new VariableDefinition(disable.ReturnType); var rat = new VariableDefinition(ratField.FieldType);
var count = new VariableDefinition(module.TypeSystem.UInt64); var index = new VariableDefinition(module.TypeSystem.UInt64);
var run = new VariableDefinition(module.TypeSystem.UInt64); var best = new VariableDefinition(module.TypeSystem.UInt64);
foreach (var local in new[] { scope, rat, count, index, run, best }) method.Body.Variables.Add(local);
method.Body.InitLocals = true;
var il = method.Body.GetILProcessor(); var finish = il.Create(OpCodes.Ldloca, scope); var test = il.Create(OpCodes.Ldloc, index);
var body = il.Create(OpCodes.Ldloc, rat); var used = il.Create(OpCodes.Ldc_I4_0); var advance = il.Create(OpCodes.Ldloc, index);
il.Append(il.Create(OpCodes.Call, disable)); il.Append(il.Create(OpCodes.Stloc, scope));
il.Append(il.Create(OpCodes.Ldsfld, ratField)); il.Append(il.Create(OpCodes.Stloc, rat));
il.Append(il.Create(OpCodes.Ldsfld, total)); il.Append(il.Create(OpCodes.Stloc, count));
il.Append(il.Create(OpCodes.Ldloc, rat)); il.Append(il.Create(OpCodes.Brfalse, finish)); il.Append(il.Create(OpCodes.Br, test));
il.Append(body); il.Append(il.Create(OpCodes.Ldloc, index)); il.Append(il.Create(OpCodes.Conv_U)); il.Append(il.Create(OpCodes.Add)); il.Append(il.Create(OpCodes.Ldind_U1)); il.Append(il.Create(OpCodes.Brtrue, used));
il.Append(il.Create(OpCodes.Ldloc, run)); il.Append(il.Create(OpCodes.Ldc_I4_1)); il.Append(il.Create(OpCodes.Conv_U8)); il.Append(il.Create(OpCodes.Add)); il.Append(il.Create(OpCodes.Stloc, run));
il.Append(il.Create(OpCodes.Ldloc, run)); il.Append(il.Create(OpCodes.Ldloc, best)); il.Append(il.Create(OpCodes.Ble_Un, advance));
il.Append(il.Create(OpCodes.Ldloc, run)); il.Append(il.Create(OpCodes.Stloc, best)); il.Append(il.Create(OpCodes.Br, advance));
il.Append(used); il.Append(il.Create(OpCodes.Conv_U8)); il.Append(il.Create(OpCodes.Stloc, run));
il.Append(advance); il.Append(il.Create(OpCodes.Ldc_I4_1)); il.Append(il.Create(OpCodes.Conv_U8)); il.Append(il.Create(OpCodes.Add)); il.Append(il.Create(OpCodes.Stloc, index));
il.Append(test); il.Append(il.Create(OpCodes.Ldloc, count)); il.Append(il.Create(OpCodes.Blt_Un, body));
il.Append(finish); il.Append(il.Create(OpCodes.Call, dispose)); il.Append(il.Create(OpCodes.Ldloc, best)); il.Append(il.Create(OpCodes.Ret)); method.Body.MaxStackSize = 2;
return method;
}
}