IL编织[ValidSystemPath]属性失效问题求助(基于Mono.Cecil)
使用Mono.Cecil编织[ValidSystemPath]属性Getter的问题修复
关键问题分析
你的编织器代码存在几个核心问题,导致逻辑未生效:
- 嵌套方法定义不完整:缺少参数声明、局部变量定义,且IL指令序列错误(无Ret指令、泛型方法未指定类型参数)
- 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
相关产品推荐
相关产品推荐

