protobuf-net / protobuf-net.Grpc

GRPC bindings for protobuf-net and grpc-dotnet
Other
857 stars 109 forks source link

AOT support when using AddSubType #318

Open rodrigovaras opened 10 months ago

rodrigovaras commented 10 months ago

I understand there is an upcoming work to fully support AOT but in the meantime i was looking on a way to include the assemblies for the reflection support on the AOT bundle.

I'm not able to go further when my code hits this: static RpcContextExtensions() { RuntimeTypeModel.Default[typeof(ActionConfiguration)] .AddSubType(100, typeof(ActionBindingConfiguration)) .AddSubType(101, typeof(ActionExpressionConfiguration)); }

When running my AOT app it shows this: System.TypeInitializationException: A type initializer threw an exception. To determine which type, inspect the InnerException's StackTrace property. ---> System.ArgumentException: Value does not fall within the expected range. at System.Reflection.Runtime.PropertyInfos.RuntimePropertyInfo.GetValue(Object, BindingFlags, Binder, Object[], CultureInfo) + 0xbb at System.Reflection.PropertyInfo.GetValue(Object, Object[]) + 0x1d at ProtoBuf.Meta.AttributeMap.ReflectionAttributeMap.TryGet(String, Boolean, Object&) + 0xa8 at ProtoBuf.Meta.MetaType.GetFieldNumber(Int32&, AttributeMap, String) + 0x39 at ProtoBuf.Meta.MetaType.NormalizeProtoMember(MemberInfo, MetaType.AttributeFamily, Boolean, Boolean, List1, Int32, Boolean, EnumMember&, MemberInfo) + 0x59c at ProtoBuf.Meta.MetaType.ApplyDefaultBehaviour_AddMembers(MetaType.AttributeFamily, Boolean, List1, Int32, Boolean, ImplicitFields, List1, MemberInfo, Boolean&, Boolean, Boolean, Type&, List1, MemberInfo) + 0xd8 at ProtoBuf.Meta.MetaType.ApplyDefaultBehaviourImpl(CompatibilityLevel) + 0xea0 at ProtoBuf.Meta.MetaType.ApplyDefaultBehaviour(CompatibilityLevel) + 0x3c at ProtoBuf.Meta.RuntimeTypeModel.FindOrAddAuto(Type, Boolean, Boolean, Boolean, CompatibilityLevel) + 0x1ed at ProtoBuf.Meta.RuntimeTypeModel.get_Item(Type) + 0x28 at ProtoBuf.Meta.MetaType.AddSubType(Int32, Type, DataFormat) + 0xb4 at Teksoft.Node.Rpc.RpcContextExtensions..cctor() + 0x4e

Any way to avoid this? By using the [ProtoInclude] will this still happen ? BTW, i was trying to avoid using the [ProtoInclude] since i did not want my contract assembly to depend on protobuff-Core hence i did it by code when needed.

mgravell commented 10 months ago

We do not claim AOT support currently; I do not know, without checking, which hurdle this is hitting, so it is hard for me to comment on what would work and what wouldn't

rodrigovaras commented 10 months ago

I did investigate further this and get into the bottom of the problem. A basic HelloModel will fail under AOT. It seems that the failure i was pointing is the fact that the protobuf lib is attempting to fully reflect the DataMemeber or ProtoMember attributes and hence failing when AOT trimm all the reflection daata on those classes.

Adding this on the rd.xml in your AOT host app will make this erro go away:

    <Assembly Name ="protobuf-net.Core">
        <Type Name="ProtoBuf.ProtoContractAttribute" Dynamic="Required All" />
        <Type Name="ProtoBuf.ProtoMemberAttribute" Dynamic="Required All" />
        <Type Name="ProtoBuf.ProtoIncludeAttribute" Dynamic="Required All" />
    </Assembly>

    <Assembly Name ="System.Runtime.Serialization.Primitives">
        <Type Name="System.Runtime.Serialization.DataContractAttribute" Dynamic="Required All" />
        <Type Name="System.Runtime.Serialization.DataMemberAttribute" Dynamic="Required All" />
    </Assembly>

But there is another more challenge problem, when using for example: public class Foo { public Bar Bar {get;set;} }

The inner class make protobuf fail here: Unhandled Exception: System.InvalidOperationException: No serializer defined for type: Bar at ProtoBuf.Internal.ThrowHelper.NoSerializerDefined(Type) + 0xe1 at ProtoBuf.Meta.ValueMember.BuildSerializer() + 0x1c5 at ProtoBuf.Meta.ValueMember.get_Serializer() + 0x1a at ProtoBuf.Meta.MetaType.BuildSerializer() + 0x6a7

I suspect the problem could be the usage of MakeGenericType() inside protobuf lib.

mgravell commented 10 months ago

Yes, that's entirely likely. We make zero claims of AOT compat. I know what is involved in bridging this gap, and it isn't trivial. It is on the roadmap, but I cannot offer you that today.

rodrigovaras commented 10 months ago

I look further on what AOT was doing with the trimming and found the proper generic construction that was throwing is here: try { return (DynamicStub)Activator.CreateInstance(typeDef.MakeGenericType(args), nonPublic: true); <=== } catch (Exception e) { return NilStub.Instance; }

The silent error was hard to track and find the exact spot where AOT was failing. Good news is that there is a mitigation by properly configuring the rd.xml file when running the AOT tools. I did this and it worked:

    <Assembly Name ="protobuf-net.Core">
        <Type Name="ProtoBuf.Internal.DynamicStub+ConcreteStub`1[[ConsoleApp4.Error, ConsoleApp4]]" Dynamic="Required All" />

The 'Error' type was inside my 'Address' type class under a simple 'ConsoleApp4' assembly and namespace.

This workaround would work until the library change the names but at least i think it will unblock me from more testing AOT.

Thanks for your quick responses.