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

IL编织[ValidSystemPath]属性失效问题求助(基于Mono.Cecil)

使用Mono.Cecil编织[ValidSystemPath]属性Getter的问题修复

关键问题分析

你的编织器代码存在几个核心问题,导致逻辑未生效:

  1. 嵌套方法定义不完整:缺少参数声明、局部变量定义,且IL指令序列错误(无Ret指令、泛型方法未指定类型参数)
  2. Getter注入逻辑错误:未将原Getter的返回值作为参数传递给嵌套方法,也未用处理后的结果覆盖原返回值

修复后的编织器代码

private static void ValidSystemPath(ModuleDefinition module, TypeDefinition type, PropertyDefinition property)
{
    MethodDefinition getter = property.GetMethod;
    var getterProcessor = getter.Body.GetILProcessor();
    var stringType = module.TypeSystem.String;
    var charType = module.TypeSystem.Char;
    var enumerableType = typeof(System.Linq.Enumerable);
    var pathType = typeof(System.IO.Path);

    // 1. 正确导入泛型方法(指定类型参数)
    var getInvalidFileNameChars = module.ImportReference(pathType.GetMethod("GetInvalidFileNameChars", Type.EmptyTypes));
    var getInvalidPathChars = module.ImportReference(pathType.GetMethod("GetInvalidPathChars", Type.EmptyTypes));
    
    // 导入Concat<char>
    var concatMethod = module.ImportReference(enumerableType.GetMethod("Concat", new[] { typeof(IEnumerable<char>), typeof(IEnumerable<char>) }));
    concatMethod = module.ImportReference(concatMethod.MakeGenericMethod(charType));
    
    // 导入ToArray<char>
    var toArrayMethod = module.ImportReference(enumerableType.GetMethod("ToArray", new[] { typeof(IEnumerable<char>) }));
    toArrayMethod = module.ImportReference(toArrayMethod.MakeGenericMethod(charType));
    
    var splitMethod = module.ImportReference(stringType.Resolve().GetMethod("Split", new[] { charType.MakeArrayType() }));
    var joinMethod = module.ImportReference(stringType.Resolve().GetMethod("Join", new[] { stringType, stringType.MakeArrayType() }));

    // 2. 正确定义嵌套方法:添加参数、局部变量,设置正确的属性
    var nestedMethod = new MethodDefinition(
        $"<{getter.Name}>g__ReplaceIllegal|0_0",
        MethodAttributes.Assembly | MethodAttributes.HideBySig | MethodAttributes.Static,
        stringType
    );
    // 添加参数p(string类型)
    nestedMethod.Parameters.Add(new ParameterDefinition(stringType));
    type.Methods.Add(nestedMethod);

    var nestedProcessor = nestedMethod.Body.GetILProcessor();
    // 声明局部变量:char[] invalid, string result
    var invalidVar = new VariableDefinition(charType.MakeArrayType());
    var resultVar = new VariableDefinition(stringType);
    nestedMethod.Body.Variables.Add(invalidVar);
    nestedMethod.Body.Variables.Add(resultVar);

    // 3. 正确生成嵌套方法的IL指令
    nestedProcessor.Emit(OpCodes.Nop);
    // 获取无效字符数组:Path.GetInvalidFileNameChars().Concat(Path.GetInvalidPathChars()).ToArray()
    nestedProcessor.Emit(OpCodes.Call, getInvalidFileNameChars);
    nestedProcessor.Emit(OpCodes.Call, getInvalidPathChars);
    nestedProcessor.Emit(OpCodes.Call, concatMethod);
    nestedProcessor.Emit(OpCodes.Call, toArrayMethod);
    nestedProcessor.Emit(OpCodes.Stloc, invalidVar); // 存入局部变量
    
    // 执行替换:string.Join("_", arg0.Split(invalid))
    nestedProcessor.Emit(OpCodes.Ldstr, "_");
    nestedProcessor.Emit(OpCodes.Ldarg_0); // 传入参数p
    nestedProcessor.Emit(OpCodes.Ldloc, invalidVar);
    nestedProcessor.Emit(OpCodes.Call, splitMethod); // string.Split是实例方法,但这里用Call(已知类型)
    nestedProcessor.Emit(OpCodes.Call, joinMethod);
    nestedProcessor.Emit(OpCodes.Stloc, resultVar);
    
    // 返回结果
    nestedProcessor.Emit(OpCodes.Ldloc, resultVar);
    nestedProcessor.Emit(OpCodes.Ret); // 必须添加Ret指令

    // 4. 修复Getter注入逻辑:替换原返回流程
    getter.Body.SimplifyMacros(); // 先简化IL,方便操作
    var returnInstructions = getter.Body.Instructions.Where(i => i.OpCode == OpCodes.Ret).ToList();
    
    foreach (var ret in returnInstructions)
    {
        // 找到Ret之前的指令(原返回值在栈顶)
        var prev = ret.Previous;
        if (prev == null) continue;
        
        // 在Ret前插入:调用嵌套方法,将栈顶的原返回值作为参数传入
        getterProcessor.InsertBefore(ret, Instruction.Create(OpCodes.Call, nestedMethod));
        // 此时栈顶是嵌套方法的返回值,直接Ret即可
    }
    
    getter.Body.OptimizeMacros(); // 优化IL
}

核心修复点解释

  • 泛型方法导入:Concat和ToArray是泛型方法,必须指定char类型参数才能正确引用
  • 嵌套方法完善:
    • 添加了输入参数string p,匹配预期的嵌套方法逻辑
    • 声明了局部变量用于存储无效字符数组和结果,避免无声明就使用Stloc_0
    • 补充了Ret指令,确保方法能正确返回
  • Getter注入逻辑:
    • 调用SimplifyMacros()和OptimizeMacros()处理IL的宏指令,避免指令结构混乱
    • 原Getter的返回值会在Ret前位于栈顶,直接调用嵌套方法会将该值作为参数传入,嵌套方法的返回值会替换栈顶值,最终Ret返回处理后的结果
  • 方法调用修正:string.Split使用Call而非Callvirt,因为我们明确知道调用对象是string类型,不需要虚调度

验证结果

编织后,原属性Getter的IL会被修改为你预期的逻辑:

public string Path {
    get {
        string ReplaceIllegal(string p)
        {
            char[] invalid = Path.GetInvalidFileNameChars().Concat(Path.GetInvalidPathChars()).ToArray();
            return string.Join("_", p.Split(invalid));
        }
        return ReplaceIllegal(path);
    }
}

此时获取Path属性会返回"test_.txt",符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 23:50:04