1
1
using Grpc . Core ;
2
2
using ProtoBuf . Grpc . Configuration ;
3
3
using System ;
4
+ using System . Buffers ;
4
5
using System . Collections . Concurrent ;
6
+ using System . Linq . Expressions ;
5
7
using System . Reflection ;
6
8
using System . Runtime . CompilerServices ;
9
+ using System . ServiceModel . Channels ;
7
10
8
11
namespace ProtoBuf . Grpc . Internal
9
12
{
@@ -25,11 +28,11 @@ static bool SlowImpl(MarshallerCache obj, Type type)
25
28
26
29
private readonly ConcurrentDictionary < Type , object ? > _marshallers
27
30
= new ConcurrentDictionary < Type , object ? >
28
- {
31
+ {
29
32
#pragma warning disable CS0618 // Empty
30
- [ typeof ( Empty ) ] = Empty . Marshaller
33
+ [ typeof ( Empty ) ] = Empty . Marshaller
31
34
#pragma warning restore CS0618
32
- } ;
35
+ } ;
33
36
34
37
internal Marshaller < T > GetMarshaller < T > ( )
35
38
{
@@ -55,17 +58,108 @@ internal void SetMarshaller<T>(Marshaller<T>? marshaller)
55
58
private Marshaller < T > ? CreateAndAdd < T > ( )
56
59
{
57
60
object ? obj = CreateMarshaller < T > ( ) ;
58
- if ( ! _marshallers . TryAdd ( typeof ( T ) , obj ) ) obj = _marshallers [ typeof ( T ) ] ;
61
+ if ( ! _marshallers . TryAdd ( typeof ( T ) , obj ) ) obj = _marshallers [ typeof ( T ) ] ;
59
62
return obj as Marshaller < T > ;
60
63
}
61
64
private Marshaller < T > ? CreateMarshaller < T > ( )
62
65
{
63
66
foreach ( var factory in _factories )
64
67
{
65
68
if ( factory . CanSerialize ( typeof ( T ) ) )
66
- return factory . CreateMarshaller < T > ( ) ;
69
+ return factory . CreateMarshaller < T > ( ) ;
70
+ }
71
+ return AutoDetectProtobufMarshaller ( ) ;
72
+
73
+ static Type ? FindIMessage ( out Type ? iBufferMessage )
74
+ {
75
+ Type ? iMessage = null ;
76
+ iBufferMessage = null ;
77
+ foreach ( var it in typeof ( T ) . GetInterfaces ( ) )
78
+ {
79
+ if ( it . Name == "IBufferMessage" && it . Namespace == "Google.Protobuf" && ! it . IsGenericType )
80
+ {
81
+ iBufferMessage = it ;
82
+ }
83
+ else if ( it . Name == "IMessage" && it . Namespace == "Google.Protobuf" && ! it . IsGenericType )
84
+ {
85
+ iMessage = it ;
86
+ }
87
+ }
88
+ return iMessage ;
89
+ }
90
+
91
+ // attempt to auto-detect the patterns exposed by Google.Protobuf types;
92
+ // this is (by necessity) reflection-based and imperfect
93
+ static Marshaller < T > ? AutoDetectProtobufMarshaller ( )
94
+ {
95
+ try
96
+ {
97
+ if ( typeof ( T ) . GetProperty ( "Parser" , BindingFlags . Public | BindingFlags . Static ) is { } parser
98
+ && FindIMessage ( out var iBufferMessage ) is { } iMessage
99
+ && iMessage . Assembly . GetType ( "Google.Protobuf.MessageExtensions" ) is { } me )
100
+ {
101
+ Func < DeserializationContext , T > deserializer ;
102
+ Action < T , global ::Grpc . Core . SerializationContext > serializer ;
103
+
104
+ if ( iBufferMessage is not null )
105
+ {
106
+ /* we want to generate:
107
+ // write
108
+ context.SetPayloadLength(message.CalculateSize());
109
+ global::Google.Protobuf.MessageExtensions.WriteTo(message, context.GetBufferWriter());
110
+ context.Complete();
111
+
112
+ // read
113
+ parser.ParseFrom(context.PayloadAsReadOnlySequence()
114
+ */
115
+ var context = Expression . Parameter ( typeof ( global ::Grpc . Core . DeserializationContext ) , "context" ) ;
116
+ var parseFrom = parser . PropertyType . GetMethod ( "ParseFrom" , new Type [ ] { typeof ( ReadOnlySequence < byte > ) } ) ;
117
+ Expression body = Expression . Call ( Expression . Constant ( parser . GetValue ( null ) , parser . PropertyType ) ,
118
+ parseFrom , Expression . Call ( context , nameof ( DeserializationContext . PayloadAsReadOnlySequence ) , Type . EmptyTypes ) ) ;
119
+ deserializer = Expression . Lambda < Func < DeserializationContext , T > > ( body , context ) . Compile ( ) ;
120
+
121
+ var message = Expression . Parameter ( typeof ( T ) , "message" ) ;
122
+ context = Expression . Parameter ( typeof ( global ::Grpc . Core . SerializationContext ) , "context" ) ;
123
+ var setPayloadLength = typeof ( global ::Grpc . Core . SerializationContext ) . GetMethod ( nameof ( global ::Grpc . Core . SerializationContext . SetPayloadLength ) , new Type [ ] { typeof ( int ) } ) ;
124
+ var calculateSize = iMessage . GetMethod ( "CalculateSize" , Type . EmptyTypes ) ;
125
+ var writeTo = me . GetMethod ( "WriteTo" , new Type [ ] { iMessage , typeof ( IBufferWriter < byte > ) } ) ;
126
+ body = Expression . Block (
127
+ Expression . Call ( context , setPayloadLength , Expression . Call ( message , calculateSize ) ) ,
128
+ Expression . Call ( writeTo , message , Expression . Call ( context , "GetBufferWriter" , Type . EmptyTypes ) ) ,
129
+ Expression . Call ( context , "Complete" , Type . EmptyTypes )
130
+ ) ;
131
+ serializer = Expression . Lambda < Action < T , global ::Grpc . Core . SerializationContext > > ( body , message , context ) . Compile ( ) ;
132
+ }
133
+ else
134
+ {
135
+ /* we want to generate:
136
+ // write
137
+ context.Complete(global::Google.Protobuf.MessageExtensions.ToByteArray(message));
138
+
139
+ // read
140
+ parser.ParseFrom(context.PayloadAsNewBuffer());
141
+ */
142
+
143
+ var context = Expression . Parameter ( typeof ( global ::Grpc . Core . DeserializationContext ) , "context" ) ;
144
+ var parseFrom = parser . PropertyType . GetMethod ( "ParseFrom" , new Type [ ] { typeof ( byte [ ] ) } ) ;
145
+ Expression body = Expression . Call ( Expression . Constant ( parser . GetValue ( null ) , parser . PropertyType ) ,
146
+ parseFrom , Expression . Call ( context , nameof ( DeserializationContext . PayloadAsNewBuffer ) , Type . EmptyTypes ) ) ;
147
+ deserializer = Expression . Lambda < Func < DeserializationContext , T > > ( body , context ) . Compile ( ) ;
148
+
149
+ var message = Expression . Parameter ( typeof ( T ) , "message" ) ;
150
+ context = Expression . Parameter ( typeof ( global ::Grpc . Core . SerializationContext ) , "context" ) ;
151
+ var toByteArray = me . GetMethod ( "ToByteArray" , new Type [ ] { iMessage } ) ;
152
+ var complete = typeof ( global ::Grpc . Core . SerializationContext ) . GetMethod (
153
+ nameof ( global ::Grpc . Core . SerializationContext . Complete ) , new Type [ ] { typeof ( byte [ ] ) } ) ;
154
+ body = Expression . Call ( context , complete , Expression . Call ( toByteArray , message ) ) ;
155
+ serializer = Expression . Lambda < Action < T , global ::Grpc . Core . SerializationContext > > ( body , message , context ) . Compile ( ) ;
156
+ }
157
+ return new Marshaller < T > ( serializer , deserializer ) ;
158
+ }
159
+ }
160
+ catch { } // this is very much a best-efforts thing
161
+ return null ;
67
162
}
68
- return null ;
69
163
}
70
164
71
165
internal MarshallerFactory ? TryGetFactory ( Type type )
@@ -79,7 +173,7 @@ internal void SetMarshaller<T>(Marshaller<T>? marshaller)
79
173
}
80
174
81
175
internal TFactory ? TryGetFactory < TFactory > ( )
82
- where TFactory : MarshallerFactory
176
+ where TFactory : MarshallerFactory
83
177
{
84
178
foreach ( var factory in _factories )
85
179
{
0 commit comments