You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用IL.Emit调用Memory.Slice时出现未定义行为的问题求助

问题:DynamicMethod调用ReadOnlyMemory.Slice时参数损坏异常

尝试编写接收ReadOnlyMemory<byte>的DynamicMethod动态方法,但调用Slice切割内存时出现参数损坏的异常行为,复现代码如下:

using System.Reflection.Emit;

DynamicMethod m = new DynamicMethod("EMITD_arraydeser_" + typeof(int[]), null, new Type[1] { typeof(ReadOnlyMemory<byte>) }, true);
var il = m.GetILGenerator();
il.DeclareLocal(typeof(ReadOnlyMemory<byte>));


il.Emit(OpCodes.Ldarg, 0);
il.Emit(OpCodes.Ldc_I4, 0);
il.Emit(OpCodes.Ldc_I4, 4);
il.EmitWriteLine("checkSlice_1....");
il.Emit(OpCodes.Call, typeof(Helpers).GetMethod("checkSlice")); // 正常工作,mem=ReadOnlyMemory<byte>[16], start=0, end=4
il.Emit(OpCodes.Pop);

il.Emit(OpCodes.Ldarg, 0);
il.Emit(OpCodes.Ldc_I4, 0);
il.Emit(OpCodes.Ldc_I4, 4);
il.EmitWriteLine("checkSlice_2....");
il.Emit(OpCodes.Call, typeof(Helpers).GetMethod("checkSlice")); // 参数损坏,start=4, end=随机值,mem不可读

// 打印结果
il.Emit(OpCodes.Call, typeof(ReadOnlyMemory<byte>).GetMethod("ToArray"));
il.Emit(OpCodes.Call, typeof(BitConverter).GetMethod("ToString", new Type[] { typeof(byte[]) }));
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));

il.Emit(OpCodes.Ldarga_S, 0);
il.Emit(OpCodes.Ldc_I4, 0);
il.Emit(OpCodes.Ldc_I4, 4);
il.EmitWriteLine("slice_1....");

il.Emit(OpCodes.Call, typeof(ReadOnlyMemory<byte>).GetMethod("Slice", new Type[2] {typeof(int), typeof(int)})); // 抛出ArgumentOutOfRangeException

// 打印结果
il.Emit(OpCodes.Call, typeof(ReadOnlyMemory<byte>).GetMethod("ToArray"));
il.Emit(OpCodes.Call, typeof(BitConverter).GetMethod("ToString", new Type[] { typeof(byte[]) }));
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));

il.Emit(OpCodes.Ret);


var del = m.CreateDelegate<Action<ReadOnlyMemory<byte>>>();
var s = new byte[] {
    3,0,0,0,
    0xAA,0xBB,0,0,
    0xCC,0xDD,0,0,
    0xEE,0xFF,0,0,
};
ReadOnlyMemory<byte> buff = new(s);
del(buff);

class Helpers
{
    public static ReadOnlyMemory<byte> checkSlice(ReadOnlyMemory<byte> mem, int start, int end)
    {
        
        try
        {
            Console.WriteLine($"{start}:{end}, {mem}");
            return mem.Slice(start, end);
        }
        catch (Exception)
        {
            Console.WriteLine("Exception on slice...." + start + ":" + end);
            return new ReadOnlyMemory<byte>();
        }
        
    } 
}

实际输出:

checkSlice_1....
0:4, System.ReadOnlyMemory<Byte>[16]
checkSlice_2....
Exception on slice....4:1616289928
slice_1....
Unhandled exception. System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. (Parameter 'start')
at EMITD_arraydeser_System.Int32[](ReadOnlyMemory`1)
at Program.<Main>$(String[] args) in C:\PATH\Program.cs:line 49

问题成因与修正方案

1. 栈不平衡导致参数错乱

checkSlice方法返回的ReadOnlyMemory<byte>是值类型,调用方法后会将返回值压入IL栈中。第一个checkSlice调用后你用OpCodes.Pop清理了栈,但第二个调用后没有执行Pop操作,栈上残留的返回值会干扰后续指令的参数传递,导致checkSlice_2的参数被错误的栈数据覆盖,出现参数损坏的情况。

2. 错误调用ReadOnlyMemory.Slice实例方法

Slice是ReadOnlyMemory<T>的实例方法,你使用OpCodes.Ldarga_S加载参数0的地址是错误的。对于值类型的实例方法,this参数需要直接加载实例本身(用OpCodes.Ldarg 0),而非地址。Ldarga_S会将参数的地址压栈,导致方法接收到的this参数类型不匹配,进而引发ArgumentOutOfRangeException。

修正后的关键IL代码

// 修正checkSlice_2的栈平衡
il.Emit(OpCodes.Ldarg, 0);
il.Emit(OpCodes.Ldc_I4, 0);
il.Emit(OpCodes.Ldc_I4, 4);
il.EmitWriteLine("checkSlice_2....");
il.Emit(OpCodes.Call, typeof(Helpers).GetMethod("checkSlice"));
il.Emit(OpCodes.Pop); // 新增Pop清理返回值

// 修正Slice调用
il.Emit(OpCodes.Ldarg, 0); // 加载实例本身,而非地址
il.Emit(OpCodes.Ldc_I4, 0);
il.Emit(OpCodes.Ldc_I4, 4);
il.EmitWriteLine("slice_1....");
il.Emit(OpCodes.Call, typeof(ReadOnlyMemory<byte>).GetMethod("Slice", new Type[] { typeof(int), typeof(int) }));

修正后,IL栈会保持平衡,参数传递正确,方法可以正常执行。

内容的提问来源于stack exchange,提问作者user10619713

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 00:17:04