jbevain / cecil

Cecil is a library to inspect, modify and create .NET programs and libraries.
MIT License
2.71k stars 619 forks source link

stfld got a wrong operand in a generic type. #954

Open Gatongone opened 1 month ago

Gatongone commented 1 month ago

Discussed in https://github.com/jbevain/cecil/discussions/935

Originally posted by **Gatongone** March 10, 2024 # Background When I tried to append a `stfld` to a method body from a TypeDefinition with generic arguments, it got a different instruction with the expected code. Is there any wrong? See following ``` stfld !0 Foo`1::list ``` and ``` stfld !0 class Foo`1::list ``` # Sample ```CSharp using Mono.Cecil; using Mono.Cecil.Cil; public class SnippetRunner { public static void Run(string path) { var mp = new ModuleParameters { Architecture = TargetArchitecture.AMD64, Kind = ModuleKind.Dll, }; using (var assembly = AssemblyDefinition.CreateAssembly(new AssemblyNameDefinition("Foo", Version.Parse("1.0.0.0")), Path.GetFileName(path), mp)) { //Class : Foo var cls_Foo_0 = new TypeDefinition("", "Foo`1", TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.NotPublic, assembly.MainModule.TypeSystem.Object); assembly.MainModule.Types.Add(cls_Foo_0); var gp_T_1 = new Mono.Cecil.GenericParameter("T", cls_Foo_0); cls_Foo_0.GenericParameters.Add(gp_T_1); //Field: list var fld_list_2 = new FieldDefinition("list", FieldAttributes.Private, gp_T_1); cls_Foo_0.Fields.Add(fld_list_2); //Method : Bar var md_Bar_3 = new MethodDefinition("Bar", MethodAttributes.Private | MethodAttributes.HideBySig, assembly.MainModule.TypeSystem.Void); cls_Foo_0.Methods.Add(md_Bar_3); md_Bar_3.Body.InitLocals = true; var il_Bar_4 = md_Bar_3.Body.GetILProcessor(); //Parameters of 'void Bar(T a) => list = a;' var p_a_5 = new ParameterDefinition("a", ParameterAttributes.None, gp_T_1); md_Bar_3.Parameters.Add(p_a_5); il_Bar_4.Emit(OpCodes.Ldarg_0); il_Bar_4.Emit(OpCodes.Ldarg_1); il_Bar_4.Emit(OpCodes.Stfld, fld_list_2); il_Bar_4.Emit(OpCodes.Ret); //** Constructor: Foo() ** var ctor_Foo_6 = new MethodDefinition(".ctor", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName, assembly.MainModule.TypeSystem.Void); cls_Foo_0.Methods.Add(ctor_Foo_6); var il_ctor_Foo_7 = ctor_Foo_6.Body.GetILProcessor(); il_ctor_Foo_7.Emit(OpCodes.Ldarg_0); il_ctor_Foo_7.Emit(OpCodes.Call, assembly.MainModule.ImportReference(TypeHelpers.DefaultCtorFor(cls_Foo_0.BaseType))); il_ctor_Foo_7.Emit(OpCodes.Ret); assembly.Write(path); } } } public class TypeHelpers { public static MethodReference DefaultCtorFor(TypeReference type) { var resolved = type.Resolve(); if (resolved == null) return null; var ctor = resolved.Methods.SingleOrDefault(m => m.IsConstructor && m.Parameters.Count == 0 && !m.IsStatic); if (ctor == null) return DefaultCtorFor(resolved.BaseType); return new MethodReference(".ctor", type.Module.TypeSystem.Void, type) {HasThis = true}; } } ``` Output: ``` .class private auto ansi beforefieldinit Foo`1 extends [mscorlib]System.Object { // Fields .field private !T list // Methods .method private hidebysig instance void Bar (!T a) cil managed { // Method begins at RVA 0x2050 // Header size: 12 // Code size: 8 (0x8) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld !0 Foo`1::list IL_0007: ret } // end of method Foo`1::Bar .method public hidebysig specialname rtspecialname instance void .ctor () cil managed {...} } // end of class Foo`1 ``` Expected: ``` .class private auto ansi beforefieldinit Foo`1 extends [mscorlib]System.Object { // Fields .field private !T list // Methods .method private hidebysig instance void Bar (!T a) cil managed { // Method begins at RVA 0x2050 // Header size: 12 // Code size: 8 (0x8) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld !0 class Foo`1::list IL_0007: ret } // end of method Foo`1::Bar .method public hidebysig specialname rtspecialname instance void .ctor () cil managed {...} } // end of class Foo`1 ```