Skip to content

Dynamically Generating UnsafeAccessor Stub via MethodBuilder Appends Random IL Body #116649

Closed
@HyperSphereStudio

Description

@HyperSphereStudio

Description

I am currently testing out generating functions on .net 10 preview 5 (windows) and wanted to test out the relatively new feature UnsafeAccessor.

To do this I do the following:

  • Create a type builder

  • Create a public static method that calls the accessor on some struct field that is private

  • Create a nested private extern function that has two custom attributes (CompilerGenerated & Unsafe Accessor) with no il emitted. (Implementation Flags set to IL | PreserveSig). Note: PreserveSig was intentionally placed there after reading through TypeBuilders skipping of method bodies on this flag.

  • Create the type

The function works fine at runtime (because the JIT will replace the IL body) but I notice when I call GetILAsByteArray() it is nonzero. This is a problem for the debugger because it crashes whenever it enters the method (I am assuming it attempts to read the body). Or when writing the function later into a persisted assembly (this isnt as big of a problem because I can make a filter for UnsafeAccessors).

The IL it seemed to copy from appears to be method 1.

Reproduction Steps

  • Create a type builder

  • Create a public static method that calls the accessor on some struct field that is private

  • Create a nested private extern function that has two custom attributes (CompilerGenerated & Unsafe Accessor) with no il emitted. (Implementation Flags set to IL | PreserveSig)

  • Create the type

Expected behavior


public struct Val6 : Base.Val<Base.Int>, IJulia
{
  [SpecialName]
  public static Base.Int get_Value()
  {
    Base.Int @int;
    Val6.field(ref @int) = new IntPtr(4);
    return @int;
  }

  [CompilerGenerated]
  [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "<Value>k__BackingField")]
  internal static extern ref IntPtr field(ref Base.Int _param0);
}

Actual behavior


public struct Val6 : Base.Val<Base.Int>, IJulia
{
  [SpecialName]
  public static Base.Int get_Value()
  {
    Base.Int @int;
    Val6.field(ref @int) = new IntPtr(4);
    return @int;
  }

  [CompilerGenerated]
  [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "<Value>k__BackingField")]
  [MethodImpl(MethodImplOptions.PreserveSig)]
  internal static ref IntPtr field(ref Base.Int _param0)
  {
    Base.Int @int;
    Val6.field(ref @int) = new IntPtr(4);       //Stack overflow here for debugger.
    // ISSUE: cast to a reference type
    return (IntPtr&) @int;
  }
}

Note in the above that nothing was emitted into 'fields' il ... it copied it over somehow from get_Value.

Regression?

No response

Known Workarounds

No response

Configuration

.net 10 preview 5 (windows) x86_64; Release; Debug; Tier0; Tier1

Other information

Stepping through System.Private.CoreLIB's TypeBuilder.CreateTypeNoLock() this function should be skipping setting any IL because of the PreserveSig flag (continue to the next method when ImplFlag & PreserveSig != 0)

Not sure where this extra IL is getting added.... I dont think it is before the type creation though.

Metadata

Metadata

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions