Skip to content

Serialization of F#-defined exception variants doesn't serialize fields #878

@eiriktsarpalis

Description

@eiriktsarpalis

This is an old issue with F# codegen which has left serialization in F#-defined exceptions using exception X = .... effectively broken. Here's a simple reproduction:

let clone (x : 'T) =
    let bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter()
    let m = new System.IO.MemoryStream()
    bf.Serialize(m, x)
    m.Position <- 0L
    bf.Deserialize(m) :?> 'T

exception Foo of x:string * y:int

let foo = Foo ("value", 42) 
clone foo // val it : exn = Foo (null,0)

The reason why this happens is that exceptions are necessarily serialized through ISerializable. Every type that inherits System.Exception must provide a SerializationInfo * StreamingContext constructor and an accompanying override of the ISerializable.GetObjectData() method, which are responsible for calling the corresponding base methods and serializing any additional data included in the subtype. In F# exceptions, generated code looks as follows (decompiled to C#):

    [CompilationMapping(SourceConstructFlags.Exception)]
    [Serializable]
    public class Foo : Exception, IStructuralEquatable
    {
        internal string x@;
        internal int y@;

        protected Foo(SerializationInfo info, StreamingContext context) : base(info, context)
        {
        }

        ...
    }

My proposal is to update codegen such that the generated class looks like so:

    [CompilationMapping(SourceConstructFlags.Exception)]
    [Serializable]
    public class Foo : Exception, IStructuralEquatable
    {
        internal string x@;
        internal int y@;

        protected Foo(SerializationInfo info, StreamingContext context) : base(info, context)
        {
            x@ = (string) info.GetValue("x", typeof(string));
            y@ = (int) info.GetValue("y", typeof(int));
        }

        public override GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
            info.AddValue("x", x@);
            info.AddValue("y", y@);
        }

        ...
    }

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    In Progress

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions