Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support "generic" (dotnet runtime types only) AOT #1023

Open
rmannibucau opened this issue Dec 15, 2024 · 0 comments
Open

Support "generic" (dotnet runtime types only) AOT #1023

rmannibucau opened this issue Dec 15, 2024 · 0 comments

Comments

@rmannibucau
Copy link

rmannibucau commented Dec 15, 2024

Is your feature request related to a problem? Please describe.
I'm targetting AOT compilation and using YamlDotNet.
The restriction to use only dotnet runtime types is fine in this particular context and since the (de)serialized structure(s) is not known upfront, using only List, Dictionary<object,object> and primitives+string is ok.

Main issue is that out of the box the yamlcontext generators don't work for this use case and there is no default implementation.

Describe the solution you'd like
Ideally it should work by building a default (De)Serializer - instead of testing Dictionary<,> it can test with object too (same for lists) and it should be close to work.

Describe alternatives you've considered
Implement a custom static context (mainly a draft since it doesn't cover all cases):

using YamlDotNet.Serialization;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.TypeInspectors;

namespace Workaround.YamlDotNet;

// note: aot generator doesn't work with generic containers so let's do it outselves
// StaticDeserializerBuilder has fallbacks (Dictionary<object, object>) and List<object>)
// in its constructor so let's just use them and only support that
public class YamlContext : StaticContext
{
    public static readonly YamlContext Instance = new();

    public override bool IsKnownType(Type type)
    {
        return false;
    }

    public override ITypeResolver GetTypeResolver() => SimpleTypeResolver.Instance;

    public override StaticObjectFactory GetFactory() => SimpleStaticObjectFactory.Instance;

    public override ITypeInspector GetTypeInspector() => SimpleTypeInspector.Instance;
}

internal class SimpleTypeInspector : TypeInspectorSkeleton
{
    internal static readonly SimpleTypeInspector Instance = new();

    public override IEnumerable<IPropertyDescriptor> GetProperties(Type type, object? container) =>
        [];

    public override string GetEnumName(Type enumType, string name) => name;

    public override string GetEnumValue(object enumValue) => enumValue.ToString()!;
}

internal class SimpleStaticObjectFactory : StaticObjectFactory
{
    internal static readonly SimpleStaticObjectFactory Instance = new();

    public override object Create(Type type)
    {
        if (
            typeof(Dictionary<object, object>) == type
            || typeof(IDictionary<object, object>) == type
        )
        {
            return new OrderedDictionary<object, object>();
        }

        if (
            typeof(Dictionary<string, object>) == type
            || typeof(IDictionary<string, object>) == type
        )
        {
            return new OrderedDictionary<string, object>();
        }

        if (typeof(List<object>) == type || typeof(IList<object>) == type)
        {
            return new List<object>();
        }

        if (typeof(IList<IDictionary<string, object>>) == type)
        {
            return new List<IDictionary<string, object>>();
        }

        throw new InvalidOperationException($"Unknown type: '{type.FullName}'");
    }

    public override Array CreateArray(Type type, int count)
    {
        throw new NotImplementedException("shoudn't be called");
    }

    public override bool IsDictionary(Type type)
    {
        return type == typeof(Dictionary<object, object>)
            || type == typeof(IDictionary<object, object>)
            || type == typeof(IDictionary<string, object>)
            || type == typeof(IDictionary<string, string>)
            || type == typeof(Dictionary<string, string>)
            || type == typeof(IDictionary<string, int>)
            || type == typeof(Dictionary<string, int>)
            || type == typeof(IDictionary<string, bool>)
            || type == typeof(Dictionary<string, bool>)
            || type == typeof(IDictionary<string, double>)
            || type == typeof(Dictionary<string, double>)
            || type == typeof(IDictionary<object, string>)
            || type == typeof(Dictionary<object, string>)
            || type == typeof(IDictionary<object, int>)
            || type == typeof(Dictionary<object, int>)
            || type == typeof(IDictionary<object, bool>)
            || type == typeof(Dictionary<object, bool>)
            || type == typeof(IDictionary<object, double>)
            || type == typeof(Dictionary<object, double>);
    }

    public override bool IsArray(Type type)
    {
        return false;
    }

    public override bool IsList(Type type)
    {
        return type == typeof(List<object>)
            || type == typeof(IList<object>)
            || type == typeof(List<string>)
            || type == typeof(IList<string>)
            || type == typeof(List<bool>)
            || type == typeof(IList<bool>)
            || type == typeof(List<int>)
            || type == typeof(IList<int>);
    }

    public override Type GetKeyType(Type type)
    {
        if (
            type == typeof(IDictionary<object, object>)
            || type == typeof(IDictionary<object, int>)
            || type == typeof(IDictionary<object, double>)
            || type == typeof(IDictionary<object, bool>)
            || type == typeof(Dictionary<object, object>)
            || type == typeof(Dictionary<object, int>)
            || type == typeof(Dictionary<object, double>)
            || type == typeof(Dictionary<object, bool>)
        )
        {
            return typeof(object);
        }

        if (
            type == typeof(IDictionary<string, object>)
            || type == typeof(IDictionary<string, int>)
            || type == typeof(IDictionary<string, double>)
            || type == typeof(IDictionary<string, bool>)
            || type == typeof(Dictionary<string, object>)
            || type == typeof(Dictionary<string, int>)
            || type == typeof(Dictionary<string, double>)
            || type == typeof(Dictionary<string, bool>)
        )
        {
            return typeof(string);
        }

        return typeof(string); // normally it is always that so it is a safe default for us
    }

    public override Type GetValueType(Type type)
    {
        if (
            type == typeof(Dictionary<object, object>)
            || type == typeof(List<object>)
            || type == typeof(IDictionary<object, object>)
            || type == typeof(IList<object>)
        )
        {
            return typeof(object);
        }

        if (
            type == typeof(List<IDictionary<string, object>>)
            || type == typeof(IList<IDictionary<string, object>>)
        )
        {
            return typeof(IDictionary<string, object>);
        }

        if (
            type == typeof(IDictionary<string, string>)
            || type == typeof(IDictionary<string, string>)
            || type == typeof(List<string>)
            || type == typeof(IList<string>)
        )
        {
            return typeof(string);
        }

        if (
            type == typeof(IDictionary<string, int>)
            || type == typeof(IDictionary<string, int>)
            || type == typeof(List<int>)
            || type == typeof(IList<int>)
        )
        {
            return typeof(int);
        }

        if (
            type == typeof(IDictionary<string, double>)
            || type == typeof(IDictionary<string, double>)
            || type == typeof(List<double>)
            || type == typeof(IList<double>)
        )
        {
            return typeof(double);
        }

        if (
            type == typeof(IDictionary<string, bool>)
            || type == typeof(IDictionary<string, bool>)
            || type == typeof(List<bool>)
            || type == typeof(IList<bool>)
        )
        {
            return typeof(bool);
        }

        throw new InvalidOperationException($"Unknown type: '{type.FullName}'");
    }

    public override void ExecuteOnDeserializing(object value) { }

    public override void ExecuteOnDeserialized(object value) { }

    public override void ExecuteOnSerializing(object value) { }

    public override void ExecuteOnSerialized(object value) { }
}

internal class SimpleTypeResolver : ITypeResolver
{
    public static readonly SimpleTypeResolver Instance = new();

    public Type Resolve(Type staticType, object? value)
    {
        return staticType;
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant