Skip to content

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

@Gatongone

Description

@Gatongone

Discussed in #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<!0>::list

Sample

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<T> 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<T> 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<!0>::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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions