@@ -20,7 +20,8 @@ public void Emit(SourceProductionContext context)
20
20
var classDeclaration = SF . ClassDeclaration ( parser . Identifier )
21
21
. AddModifiers ( SF . Token ( SK . PartialKeyword ) )
22
22
. AddAttributeLists ( SF . AttributeList ( ) . AddAttributes ( EmitGeneratedCodeAttribute ( ) ) )
23
- . AddMembers ( EmitSerializeHandlerMethod ( ) ) ;
23
+ . AddMembers ( EmitSerializeHandlerMethod ( ) , EmitMeasureHandlerMethod ( ) )
24
+ . AddBaseListTypes ( SF . SimpleBaseType ( SF . ParseName ( $ "global::Lagrange.Proto.IProtoSerializer<{ parser . Identifier } >") ) ) ;
24
25
25
26
var namespaceDeclaration = SF . FileScopedNamespaceDeclaration ( SF . ParseName ( parser . Namespace ?? string . Empty ) )
26
27
. AddMembers ( classDeclaration ) ;
@@ -41,15 +42,11 @@ public void Emit(SourceProductionContext context)
41
42
string code = compilationUnit . NormalizeWhitespace ( ) . ToFullString ( ) ;
42
43
context . AddSource ( $ "{ parser . Identifier } .g.cs", code ) ;
43
44
}
44
-
45
+
46
+ #region SerializeHandler
47
+
45
48
private MethodDeclarationSyntax EmitSerializeHandlerMethod ( )
46
49
{
47
- string methodName = $ "{ parser . Identifier } SerializeHandler";
48
- string classFullName = $ "global::{ parser . Namespace } .{ parser . Identifier } ?";
49
- var parameters = SF . ParameterList ( )
50
- . AddParameters ( SF . Parameter ( SF . Identifier ( "obj" ) ) . WithType ( SF . ParseTypeName ( classFullName ) ) )
51
- . AddParameters ( SF . Parameter ( SF . Identifier ( "writer" ) ) . WithType ( SF . ParseTypeName ( WriterFullName ) ) ) ;
52
-
53
50
var syntax = new List < StatementSyntax > { EmitNullableCheckStatement ( true , "obj" , SF . ReturnStatement ( ) ) } ;
54
51
55
52
foreach ( var t in parser . Fields )
@@ -82,33 +79,40 @@ private MethodDeclarationSyntax EmitSerializeHandlerMethod()
82
79
if ( parser . Model . GetTypeSymbol ( type ) . IsValueType && ! type . IsNullableType ( ) )
83
80
{
84
81
syntax . Add ( tag ) ;
85
- syntax . Add ( field . AddBlankLine ( ) ) ;
82
+ syntax . AddRange ( field ) ;
83
+ syntax [ syntax . Count - 1 ] = syntax [ syntax . Count - 1 ] . WithTrailingTrivia ( SF . Comment ( "\n " ) ) ;
86
84
}
87
85
else
88
86
{
89
- var block = SF . Block ( SF . List < StatementSyntax > ( [ tag , field ] ) ) ;
87
+ var block = SF . Block ( SF . List < StatementSyntax > ( [ tag , .. field ] ) ) ;
90
88
syntax . Add ( EmitNullableCheckStatement ( false , $ "obj.{ name } ", block , false ) ) ;
91
89
}
92
90
}
93
91
94
- return SF . MethodDeclaration ( SF . PredefinedType ( SF . Token ( SK . VoidKeyword ) ) , methodName )
95
- . AddModifiers ( SF . Token ( SK . PrivateKeyword ) , SF . Token ( SK . StaticKeyword ) )
92
+ string classFullName = $ "global::{ parser . Namespace } .{ parser . Identifier } ?";
93
+ var parameters = SF . ParameterList ( )
94
+ . AddParameters ( SF . Parameter ( SF . Identifier ( "obj" ) ) . WithType ( SF . ParseTypeName ( classFullName ) ) )
95
+ . AddParameters ( SF . Parameter ( SF . Identifier ( "writer" ) ) . WithType ( SF . ParseTypeName ( WriterFullName ) ) ) ;
96
+
97
+ return SF . MethodDeclaration ( SF . PredefinedType ( SF . Token ( SK . VoidKeyword ) ) , "SerializeHandler" )
98
+ . AddModifiers ( SF . Token ( SK . PublicKeyword ) , SF . Token ( SK . StaticKeyword ) )
96
99
. WithParameterList ( parameters )
97
100
. WithBody ( SF . Block ( SF . List ( syntax ) ) ) ;
98
101
}
99
-
100
- private StatementSyntax EmitMemberStatement ( WireType wireType , string identifier , TypeSyntax type , bool isSigned )
102
+
103
+ private StatementSyntax [ ] EmitMemberStatement ( WireType wireType , string identifier , TypeSyntax type , bool isSigned )
101
104
{
102
- bool isValueType = parser . Model . GetTypeSymbol ( type ) . IsValueType ;
103
- if ( type . IsNullableType ( ) && isValueType ) identifier += ".Value" ;
105
+ var symbol = parser . Model . GetTypeSymbol ( type ) ;
106
+ if ( type . IsNullableType ( ) && symbol . IsValueType ) identifier += ".Value" ;
104
107
105
108
return wireType switch
106
109
{
107
- WireType . VarInt => EmitVarIntSerializeStatement ( identifier , isSigned ) ,
108
- WireType . Fixed32 => EmitFixed32SerializeStatement ( identifier , isSigned ) ,
109
- WireType . Fixed64 => EmitFixed64SerializeStatement ( identifier , isSigned ) ,
110
- WireType . LengthDelimited when type . IsStringType ( ) => EmitStringSerializeStatement ( identifier ) ,
111
- WireType . LengthDelimited when type . IsByteArrayType ( ) => EmitBytesSerializeStatement ( identifier ) ,
110
+ WireType . VarInt => [ EmitVarIntSerializeStatement ( identifier , isSigned ) ] ,
111
+ WireType . Fixed32 => [ EmitFixed32SerializeStatement ( identifier , isSigned ) ] ,
112
+ WireType . Fixed64 => [ EmitFixed64SerializeStatement ( identifier , isSigned ) ] ,
113
+ WireType . LengthDelimited when type . IsStringType ( ) => [ EmitStringSerializeStatement ( identifier ) ] ,
114
+ WireType . LengthDelimited when type . IsByteArrayType ( ) => [ EmitBytesSerializeStatement ( identifier ) ] ,
115
+ WireType . LengthDelimited when symbol . IsUserDefinedType ( ) => EmitProtoPackableSerializeStatement ( type . ToString ( ) , identifier ) ,
112
116
_ => throw new Exception ( $ "Unsupported wire type: { wireType } for { identifier } ")
113
117
} ;
114
118
}
@@ -171,6 +175,19 @@ private static StatementSyntax EmitFixed64SerializeStatement(string name, bool i
171
175
return SF . ExpressionStatement ( SF . InvocationExpression ( access ) . AddArgumentListArguments ( SF . Argument ( arg ) ) ) ;
172
176
}
173
177
178
+ private static StatementSyntax [ ] EmitProtoPackableSerializeStatement ( string typeName , string name )
179
+ {
180
+ var measure = SF . MemberAccessExpression ( SK . SimpleMemberAccessExpression , SF . IdentifierName ( typeName ) , SF . IdentifierName ( $ "MeasureHandler") ) ;
181
+ var invocation = SF . InvocationExpression ( measure ) . AddArgumentListArguments ( SF . Argument ( SF . MemberAccessExpression ( SK . SimpleMemberAccessExpression , SF . IdentifierName ( "obj" ) , SF . IdentifierName ( name ) ) ) ) ;
182
+ var access = SF . MemberAccessExpression ( SK . SimpleMemberAccessExpression , SF . IdentifierName ( "writer" ) , SF . IdentifierName ( "EncodeVarInt" ) ) ;
183
+ var serialize = SF . MemberAccessExpression ( SK . SimpleMemberAccessExpression , SF . IdentifierName ( typeName ) , SF . IdentifierName ( "SerializeHandler" ) ) ;
184
+ return
185
+ [
186
+ SF . ExpressionStatement ( SF . InvocationExpression ( access ) . AddArgumentListArguments ( SF . Argument ( invocation ) ) ) ,
187
+ SF . ExpressionStatement ( SF . InvocationExpression ( serialize ) . AddArgumentListArguments ( SF . Argument ( SF . MemberAccessExpression ( SK . SimpleMemberAccessExpression , SF . IdentifierName ( "obj" ) , SF . IdentifierName ( name ) ) ) , SF . Argument ( SF . IdentifierName ( "writer" ) ) ) )
188
+ ] ;
189
+ }
190
+
174
191
private static StatementSyntax EmitBytesSerializeStatement ( string name )
175
192
{
176
193
var arg = SF . MemberAccessExpression ( SK . SimpleMemberAccessExpression , SF . IdentifierName ( "obj" ) , SF . IdentifierName ( name ) ) ;
@@ -195,5 +212,123 @@ private static AttributeSyntax EmitGeneratedCodeAttribute()
195
212
SF . AttributeArgument ( SF . LiteralExpression ( SK . StringLiteralExpression , SF . Literal ( "Lagrange.Proto.Generator" ) ) ) ,
196
213
SF . AttributeArgument ( SF . LiteralExpression ( SK . StringLiteralExpression , SF . Literal ( "1.0.0" ) ) ) ) ;
197
214
}
215
+
216
+ #endregion
217
+
218
+ #region MeasureHandler
219
+
220
+ private MethodDeclarationSyntax EmitMeasureHandlerMethod ( )
221
+ {
222
+ ExpressionSyntax syntax ;
223
+ if ( parser . Fields . Count == 0 )
224
+ {
225
+ syntax = SF . LiteralExpression ( SK . NumericLiteralExpression , SF . Literal ( 0 ) ) ;
226
+ }
227
+ else
228
+ {
229
+ int constant = 0 ;
230
+ var expressions = new List < ExpressionSyntax > ( ) ;
231
+
232
+ foreach ( var kv in parser . Fields )
233
+ {
234
+ TypeSyntax type ;
235
+ string name ;
236
+ switch ( kv . Value . Syntax )
237
+ {
238
+ case FieldDeclarationSyntax fieldDeclaration :
239
+ {
240
+ type = fieldDeclaration . Declaration . Type ;
241
+ name = fieldDeclaration . Declaration . Variables [ 0 ] . Identifier . ToString ( ) ;
242
+ break ;
243
+ }
244
+ case PropertyDeclarationSyntax propertyDeclaration :
245
+ {
246
+ type = propertyDeclaration . Type ;
247
+ name = propertyDeclaration . Identifier . ToString ( ) ;
248
+ break ;
249
+ }
250
+ default :
251
+ {
252
+ throw new Exception ( $ "Unsupported member type: { kv . Value . GetType ( ) } ") ;
253
+ }
254
+ }
255
+ var symbol = parser . Model . GetTypeSymbol ( type ) ;
256
+ string identifier = symbol . IsValueType && type . IsNullableType ( ) ? name + ".Value" : name ;
257
+
258
+ var expr = kv . Value . WireType switch
259
+ {
260
+ WireType . VarInt => EmitVarIntLengthExpression ( identifier ) ,
261
+ WireType . Fixed32 => SF . LiteralExpression ( SK . NumericLiteralExpression , SF . Literal ( 4 ) ) ,
262
+ WireType . Fixed64 => SF . LiteralExpression ( SK . NumericLiteralExpression , SF . Literal ( 8 ) ) ,
263
+ WireType . LengthDelimited when type . IsStringType ( ) => EmitStringLengthExpression ( identifier ) ,
264
+ WireType . LengthDelimited when type . IsByteArrayType ( ) => EmitBytesLengthExpression ( identifier ) ,
265
+ WireType . LengthDelimited when symbol . IsUserDefinedType ( ) => EmitProtoPackableLengthExpression ( identifier ) ,
266
+ _ => throw new Exception ( $ "Unsupported wire type: { kv . Value . WireType } for { type . ToString ( ) } ")
267
+ } ;
268
+
269
+ if ( symbol . IsValueType && ! type . IsNullableType ( ) )
270
+ {
271
+ constant += ProtoHelper . EncodeVarInt ( ( kv . Key << 3 ) | ( byte ) kv . Value . WireType ) . Length ;
272
+ }
273
+ else // null check with obj.{identifier}
274
+ {
275
+ var tag = ProtoHelper . EncodeVarInt ( ( kv . Key << 3 ) | ( byte ) kv . Value . WireType ) ;
276
+ var right = SF . BinaryExpression ( SK . AddExpression , SF . LiteralExpression ( SK . NumericLiteralExpression , SF . Literal ( tag . Length ) ) , expr ) ;
277
+ var left = SF . LiteralExpression ( SK . NumericLiteralExpression , SF . Literal ( 0 ) ) ;
278
+ expr = EmitNullableCheckExpression ( name , left , right ) ;
279
+ }
280
+
281
+ expressions . Add ( expr ) ;
282
+ }
283
+
284
+ syntax = SF . LiteralExpression ( SK . NumericLiteralExpression , SF . Literal ( constant ) ) ;
285
+ syntax = expressions . Aggregate ( syntax , ( current , expr ) => SF . BinaryExpression ( SK . AddExpression , current , expr ) ) ;
286
+ }
287
+
288
+ string classFullName = $ "global::{ parser . Namespace } .{ parser . Identifier } ";
289
+ var parameters = SF . ParameterList ( )
290
+ . AddParameters ( SF . Parameter ( SF . Identifier ( "obj" ) ) . WithType ( SF . ParseTypeName ( classFullName ) ) ) ;
291
+
292
+ return SF . MethodDeclaration ( SF . PredefinedType ( SF . Token ( SK . IntKeyword ) ) , "MeasureHandler" )
293
+ . AddModifiers ( SF . Token ( SK . PublicKeyword ) , SF . Token ( SK . StaticKeyword ) )
294
+ . WithParameterList ( parameters )
295
+ . WithBody ( SF . Block ( SF . ReturnStatement ( syntax ) ) ) ;
296
+ }
297
+
298
+ private static ExpressionSyntax EmitVarIntLengthExpression ( string identifier )
299
+ {
300
+ var obj = SF . MemberAccessExpression ( SK . SimpleMemberAccessExpression , SF . IdentifierName ( "obj" ) , SF . IdentifierName ( identifier ) ) ;
301
+ var access = SF . MemberAccessExpression ( SK . SimpleMemberAccessExpression , SF . IdentifierName ( "global::Lagrange.Proto.Utility.ProtoHelper" ) , SF . IdentifierName ( "GetVarIntLength" ) ) ;
302
+ return SF . InvocationExpression ( access ) . AddArgumentListArguments ( SF . Argument ( obj ) ) ;
303
+ }
304
+
305
+ private static ExpressionSyntax EmitStringLengthExpression ( string identifier )
306
+ {
307
+ var obj = SF . MemberAccessExpression ( SK . SimpleMemberAccessExpression , SF . IdentifierName ( "obj" ) , SF . IdentifierName ( identifier ) ) ;
308
+ var access = SF . MemberAccessExpression ( SK . SimpleMemberAccessExpression , SF . IdentifierName ( "global::Lagrange.Proto.Utility.ProtoHelper" ) , SF . IdentifierName ( "CountString" ) ) ;
309
+ return SF . InvocationExpression ( access ) . AddArgumentListArguments ( SF . Argument ( obj ) ) ;
310
+ }
311
+
312
+ private static ExpressionSyntax EmitBytesLengthExpression ( string identifier )
313
+ {
314
+ var obj = SF . MemberAccessExpression ( SK . SimpleMemberAccessExpression , SF . IdentifierName ( "obj" ) , SF . IdentifierName ( identifier ) ) ;
315
+ var access = SF . MemberAccessExpression ( SK . SimpleMemberAccessExpression , SF . IdentifierName ( "global::Lagrange.Proto.Utility.ProtoHelper" ) , SF . IdentifierName ( "CountBytes" ) ) ;
316
+ return SF . InvocationExpression ( access ) . AddArgumentListArguments ( SF . Argument ( obj ) ) ;
317
+ }
318
+
319
+ private static ExpressionSyntax EmitProtoPackableLengthExpression ( string name )
320
+ {
321
+ var obj = SF . MemberAccessExpression ( SK . SimpleMemberAccessExpression , SF . IdentifierName ( "obj" ) , SF . IdentifierName ( name ) ) ;
322
+ var access = SF . MemberAccessExpression ( SK . SimpleMemberAccessExpression , SF . IdentifierName ( "global::Lagrange.Proto.Utility.ProtoHelper" ) , SF . IdentifierName ( "CountProtoPackable" ) ) ;
323
+ return SF . InvocationExpression ( access ) . AddArgumentListArguments ( SF . Argument ( obj ) ) ;
324
+ }
325
+
326
+ /// <summary>
327
+ /// (obj == null ? left : right)
328
+ /// </summary>
329
+ private static ExpressionSyntax EmitNullableCheckExpression ( string identifier , ExpressionSyntax left , ExpressionSyntax right ) => SF . ParenthesizedExpression (
330
+ SF . ConditionalExpression ( SF . BinaryExpression ( SK . EqualsExpression , SF . MemberAccessExpression ( SK . SimpleMemberAccessExpression , SF . IdentifierName ( "obj" ) , SF . IdentifierName ( identifier ) ) , SF . LiteralExpression ( SK . NullLiteralExpression ) ) , left , right )
331
+ ) ;
332
+ #endregion
198
333
}
199
334
}
0 commit comments