Mono.Cecil 中的固定变量
Pinned variables in Mono.Cecil
如何使用 Mono.Cecil 固定局部变量?
完整示例:
这个:
public unsafe static void Assign2(byte[] arr)
{
fixed (byte* ptr = arr)
*ptr = 255;
可以翻译成:
var assembly = AssemblyDefinition.CreateAssembly(new AssemblyNameDefinition("Test", new Version()), "Test", ModuleKind.Dll);
var module = assembly.MainModule;
var testClassType = new TypeDefinition(
"TestNamespace",
"TestClass",
TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Sealed,
module.ImportReference(typeof(object)));
module.Types.Add(testClassType);
var assignMethod = new MethodDefinition(
"Assign",
MethodAttributes.Public | MethodAttributes.Static,
module.ImportReference(typeof(void)));
assignMethod.Parameters.Add(new ParameterDefinition("arr", ParameterAttributes.None, module.ImportReference(typeof(byte[]))));
testClassType.Methods.Add(assignMethod);
// Get ILProcessor for the method body
var ilProcessor = assignMethod.Body.GetILProcessor();
ilProcessor.Body.Variables.Add(new VariableDefinition(module.ImportReference(typeof(byte).MakePointerType())));
// THIS IS WHAT YOU WANT!
// using Mono.Cecil.Rocks or new PinnedType(module.ImportReference(...))
ilProcessor.Body.Variables.Add(new VariableDefinition(module.ImportReference(typeof(byte[])).MakePinnedType()));
var instrLabelA = ilProcessor.Create(OpCodes.Ldc_I4_0);
var instrLabelB = ilProcessor.Create(OpCodes.Ldloc_1);
var instrLabelC = ilProcessor.Create(OpCodes.Ldloc_0);
ilProcessor.Emit(OpCodes.Ldarg_0);
ilProcessor.Emit(OpCodes.Dup);
ilProcessor.Emit(OpCodes.Stloc_1);
ilProcessor.Emit(OpCodes.Brfalse_S, instrLabelA);
ilProcessor.Emit(OpCodes.Ldloc_1);
ilProcessor.Emit(OpCodes.Ldlen);
ilProcessor.Emit(OpCodes.Conv_I4);
ilProcessor.Emit(OpCodes.Brtrue_S, instrLabelB);
ilProcessor.Append(instrLabelA);
ilProcessor.Emit(OpCodes.Conv_U);
ilProcessor.Emit(OpCodes.Stloc_0);
ilProcessor.Emit(OpCodes.Br_S, instrLabelC);
ilProcessor.Append(instrLabelB);
ilProcessor.Emit(OpCodes.Ldc_I4_0);
ilProcessor.Emit(OpCodes.Ldelema, module.ImportReference(typeof(byte)));
ilProcessor.Emit(OpCodes.Conv_U);
ilProcessor.Emit(OpCodes.Stloc_0);
ilProcessor.Append(instrLabelC);
ilProcessor.Emit(OpCodes.Ldc_I4, 255);
ilProcessor.Emit(OpCodes.Stind_I1);
ilProcessor.Emit(OpCodes.Ldnull);
ilProcessor.Emit(OpCodes.Stloc_1);
ilProcessor.Emit(OpCodes.Ret);
// Save the assembly to disk
assembly.Write(@"test.dll");
IL 代码取自 SharpLab。
你想要的是:
ilProcessor.Body.Variables.Add(new VariableDefinition(module.ImportReference(typeof(byte[])).MakePinnedType()));
记得用
using Mono.Cecil.Rocks;
不使用 Rocks 你可以直接:
ilProcessor.Body.Variables.Add(new VariableDefinition(new PinnedType(module.ImportReference(typeof(byte[])))));
注意这个:
public unsafe static void Assign(byte[] arr)
{
fixed (byte* ptr = &arr[0])
*ptr = 255;
应该生成相同的代码,但实际上并没有。
var ilProcessor = assignMethod.Body.GetILProcessor();
// THIS IS WHAT YOU WANT!
// using Mono.Cecil.Rocks or new PinnedType(module.ImportReference(...))
ilProcessor.Body.Variables.Add(new VariableDefinition(module.ImportReference(typeof(byte)).MakePinnedType()));
ilProcessor.Emit(OpCodes.Ldarg_0);
ilProcessor.Emit(OpCodes.Ldc_I4_0);
ilProcessor.Emit(OpCodes.Ldelema, module.ImportReference(typeof(byte)));
ilProcessor.Emit(OpCodes.Stloc_0);
ilProcessor.Emit(OpCodes.Ldloc_0);
ilProcessor.Emit(OpCodes.Conv_U);
ilProcessor.Emit(OpCodes.Ldc_I4, 255);
ilProcessor.Emit(OpCodes.Stind_I1);
ilProcessor.Emit(OpCodes.Ldc_I4_0);
ilProcessor.Emit(OpCodes.Conv_U);
ilProcessor.Emit(OpCodes.Stloc_0);
ilProcessor.Emit(OpCodes.Ret);
我会说发生这种情况是因为案例 arr == null || arr.Length == 0
在第一种情况下必须由代码显式处理,而在这种情况下,它由 .NET 处理(会抛出异常) &arr[0]
.
如何使用 Mono.Cecil 固定局部变量?
完整示例:
这个:
public unsafe static void Assign2(byte[] arr)
{
fixed (byte* ptr = arr)
*ptr = 255;
可以翻译成:
var assembly = AssemblyDefinition.CreateAssembly(new AssemblyNameDefinition("Test", new Version()), "Test", ModuleKind.Dll);
var module = assembly.MainModule;
var testClassType = new TypeDefinition(
"TestNamespace",
"TestClass",
TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Sealed,
module.ImportReference(typeof(object)));
module.Types.Add(testClassType);
var assignMethod = new MethodDefinition(
"Assign",
MethodAttributes.Public | MethodAttributes.Static,
module.ImportReference(typeof(void)));
assignMethod.Parameters.Add(new ParameterDefinition("arr", ParameterAttributes.None, module.ImportReference(typeof(byte[]))));
testClassType.Methods.Add(assignMethod);
// Get ILProcessor for the method body
var ilProcessor = assignMethod.Body.GetILProcessor();
ilProcessor.Body.Variables.Add(new VariableDefinition(module.ImportReference(typeof(byte).MakePointerType())));
// THIS IS WHAT YOU WANT!
// using Mono.Cecil.Rocks or new PinnedType(module.ImportReference(...))
ilProcessor.Body.Variables.Add(new VariableDefinition(module.ImportReference(typeof(byte[])).MakePinnedType()));
var instrLabelA = ilProcessor.Create(OpCodes.Ldc_I4_0);
var instrLabelB = ilProcessor.Create(OpCodes.Ldloc_1);
var instrLabelC = ilProcessor.Create(OpCodes.Ldloc_0);
ilProcessor.Emit(OpCodes.Ldarg_0);
ilProcessor.Emit(OpCodes.Dup);
ilProcessor.Emit(OpCodes.Stloc_1);
ilProcessor.Emit(OpCodes.Brfalse_S, instrLabelA);
ilProcessor.Emit(OpCodes.Ldloc_1);
ilProcessor.Emit(OpCodes.Ldlen);
ilProcessor.Emit(OpCodes.Conv_I4);
ilProcessor.Emit(OpCodes.Brtrue_S, instrLabelB);
ilProcessor.Append(instrLabelA);
ilProcessor.Emit(OpCodes.Conv_U);
ilProcessor.Emit(OpCodes.Stloc_0);
ilProcessor.Emit(OpCodes.Br_S, instrLabelC);
ilProcessor.Append(instrLabelB);
ilProcessor.Emit(OpCodes.Ldc_I4_0);
ilProcessor.Emit(OpCodes.Ldelema, module.ImportReference(typeof(byte)));
ilProcessor.Emit(OpCodes.Conv_U);
ilProcessor.Emit(OpCodes.Stloc_0);
ilProcessor.Append(instrLabelC);
ilProcessor.Emit(OpCodes.Ldc_I4, 255);
ilProcessor.Emit(OpCodes.Stind_I1);
ilProcessor.Emit(OpCodes.Ldnull);
ilProcessor.Emit(OpCodes.Stloc_1);
ilProcessor.Emit(OpCodes.Ret);
// Save the assembly to disk
assembly.Write(@"test.dll");
IL 代码取自 SharpLab。
你想要的是:
ilProcessor.Body.Variables.Add(new VariableDefinition(module.ImportReference(typeof(byte[])).MakePinnedType()));
记得用
using Mono.Cecil.Rocks;
不使用 Rocks 你可以直接:
ilProcessor.Body.Variables.Add(new VariableDefinition(new PinnedType(module.ImportReference(typeof(byte[])))));
注意这个:
public unsafe static void Assign(byte[] arr)
{
fixed (byte* ptr = &arr[0])
*ptr = 255;
应该生成相同的代码,但实际上并没有。
var ilProcessor = assignMethod.Body.GetILProcessor();
// THIS IS WHAT YOU WANT!
// using Mono.Cecil.Rocks or new PinnedType(module.ImportReference(...))
ilProcessor.Body.Variables.Add(new VariableDefinition(module.ImportReference(typeof(byte)).MakePinnedType()));
ilProcessor.Emit(OpCodes.Ldarg_0);
ilProcessor.Emit(OpCodes.Ldc_I4_0);
ilProcessor.Emit(OpCodes.Ldelema, module.ImportReference(typeof(byte)));
ilProcessor.Emit(OpCodes.Stloc_0);
ilProcessor.Emit(OpCodes.Ldloc_0);
ilProcessor.Emit(OpCodes.Conv_U);
ilProcessor.Emit(OpCodes.Ldc_I4, 255);
ilProcessor.Emit(OpCodes.Stind_I1);
ilProcessor.Emit(OpCodes.Ldc_I4_0);
ilProcessor.Emit(OpCodes.Conv_U);
ilProcessor.Emit(OpCodes.Stloc_0);
ilProcessor.Emit(OpCodes.Ret);
我会说发生这种情况是因为案例 arr == null || arr.Length == 0
在第一种情况下必须由代码显式处理,而在这种情况下,它由 .NET 处理(会抛出异常) &arr[0]
.