dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
18.83k stars 4k forks source link

Hot Reload: Allow extern functions to be added #73421

Open lambdageek opened 3 months ago

lambdageek commented 3 months ago

The [UnsafeAccessorAttribute] (API Proposal) is used on static extern functions to instruct the .NET runtime to generate an accessor method to a type member that bypass the normal .NET visibility checks.

   public class MyClass
   {
      private int _hidden;
      ...
    }

    public static partial class AccessHelpers
    {
        [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_hidden")]
        public static extern ref int GetHiddenField(MyClass c);    // runtime generated implementation
    }

    ...

    public static MyClass DeserializeMyClass(string s)
    {
        MyClass x = new MyClass();
        ref int hiddenFieldRef = ref AccessHelpers.GetHiddenField(x);
        hiddenFieldRef = int.Parse(s);
        return x;
     }    
}

With [UnsafeAccessor] it becomes possible to use source-generated reflection-free serializers that can serialize and deserialize fields of user-defined classes. For example, a user may add a new field _hidden to MyClass and the source generator will add a new accessor method to AccessHelpers and generate a method DeserializeMyClass that uses the access method to populate the hidden field.

Currently if the user adds a second private field _hidden2 and the generator adds a new static extern method, EnC will report a rude edit:

error ENC0025: Adding an extern method requires restarting the application.

Note that both AccessHelpers or the static extern method could be generic:

public class MyContainer<T>
{
   private T[] _elements;
}

public static AccessHelpers<T>
{
   [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_elements")]
   public static extern ref T[] GetElements(MyContainer<T> container);
}

public static AccessHelpers2
{
   [UnsafeAccessor(UnsafeAccessorKind.Field, Name="_elements")]
   public static extern ref T[] GetElements<T>(MyContainer<T> container);
}

The runtime support is tracked in https://github.com/dotnet/runtime/issues/102080

lambdageek commented 3 months ago

/cc @tmat

lambdageek commented 3 months ago

Note these are not P/Invokes. extern functions dont' have any special flags set. Instead they just don't have a method body RVA (and thus no method header or IL)

Compare C.PInvoke vs C.StaticExtern vs C.NormalStatic: https://sharplab.io/#v2:EYLgtghglgdgNAFxFANgHwAICYCMBYAKAwAYACDHAOgCUBXGBKMAU0oEkHmAnAewAcAytwBuUAMbMAzgG5ChDAGZyOAGzkspAMKkA3oVIHSAbQAiKFGzB8eXBAAoARAA0AmgC0AMlGAOAlAF19Q0VlNWYADwRuGHIAFlIABQ5hHgBrZjtfWQJDUiCDEIowyOi40gEECEYxAFESrhhM7Nz88iUisoA5G0gUCqrxTN0AX0JhoA