Skip to content

Commit ba1a4a3

Browse files
committed
Add ValueTuple support. #227
1 parent 09fb4aa commit ba1a4a3

File tree

3 files changed

+185
-109
lines changed

3 files changed

+185
-109
lines changed

src/MsgPack/Serialization/AbstractSerializers/SerializerBuilder`2.Tuple.cs

Lines changed: 82 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
#region -- License Terms --
1+
#region -- License Terms --
22
//
33
// MessagePack for CLI
44
//
5-
// Copyright (C) 2010-2016 FUJIWARA, Yusuke
5+
// Copyright (C) 2010-2017 FUJIWARA, Yusuke
66
//
77
// Licensed under the Apache License, Version 2.0 (the "License");
88
// you may not use this file except in compliance with the License.
@@ -41,29 +41,30 @@ private void BuildTupleSerializer( TContext context, IList<PolymorphismSchema> i
4141
{
4242
var itemTypes = TupleItems.GetTupleItemTypes( this.TargetType );
4343
targetInfo = SerializationTarget.CreateForTuple( itemTypes );
44+
var isValueTuple = this.TargetType.GetIsValueType();
4445

45-
this.BuildTuplePackTo( context, itemTypes, itemSchemaList, false );
46+
this.BuildTuplePackTo( context, itemTypes, itemSchemaList, isValueTuple, false );
4647

4748
#if FEATURE_TAP
4849
if ( this.WithAsync( context ) )
4950
{
50-
this.BuildTuplePackTo( context, itemTypes, itemSchemaList, true );
51+
this.BuildTuplePackTo( context, itemTypes, itemSchemaList, isValueTuple, true );
5152
}
5253
#endif // FEATURE_TAP
5354

54-
this.BuildTupleUnpackFrom( context, itemTypes, itemSchemaList, false );
55+
this.BuildTupleUnpackFrom( context, itemTypes, itemSchemaList, isValueTuple, false );
5556

5657
#if FEATURE_TAP
5758
if ( this.WithAsync( context ) )
5859
{
59-
this.BuildTupleUnpackFrom( context, itemTypes, itemSchemaList, true );
60+
this.BuildTupleUnpackFrom( context, itemTypes, itemSchemaList, isValueTuple, true );
6061
}
6162
#endif // FEATURE_TAP
6263
}
6364

6465
#region -- PackTo --
6566

66-
private void BuildTuplePackTo( TContext context, IList<Type> itemTypes, IList<PolymorphismSchema> itemSchemaList, bool isAsync )
67+
private void BuildTuplePackTo( TContext context, IList<Type> itemTypes, IList<PolymorphismSchema> itemSchemaList, bool isValueTuple, bool isAsync )
6768
{
6869
/*
6970
packer.PackArrayHeader( cardinarity );
@@ -84,46 +85,54 @@ private void BuildTuplePackTo( TContext context, IList<Type> itemTypes, IList<Po
8485
this.EmitSequentialStatements(
8586
context,
8687
TypeDefinition.VoidType,
87-
this.BuildTuplePackToCore( context, itemTypes, itemSchemaList, isAsync )
88+
this.BuildTuplePackToCore( context, itemTypes, itemSchemaList, isValueTuple, isAsync )
8889
)
8990
);
9091
}
9192

92-
private IEnumerable<TConstruct> BuildTuplePackToCore( TContext context, IList<Type> itemTypes, IList<PolymorphismSchema> itemSchemaList, bool isAsync )
93+
private IEnumerable<TConstruct> BuildTuplePackToCore( TContext context, IList<Type> itemTypes, IList<PolymorphismSchema> itemSchemaList, bool isValueTuple, bool isAsync )
94+
{
95+
return
96+
isValueTuple
97+
? BuildTuplePackToCore( context, itemTypes, itemSchemaList, ( t, n ) => t.GetField( n ), ( c, s, m ) => this.EmitGetFieldExpression( c, s, m ), isAsync )
98+
: BuildTuplePackToCore( context, itemTypes, itemSchemaList, ( t, n ) => t.GetProperty( n ), ( c, s, m )=> this.EmitGetPropertyExpression( c, s, m ), isAsync );
99+
}
100+
101+
private IEnumerable<TConstruct> BuildTuplePackToCore<TInfo>( TContext context, IList<Type> itemTypes, IList<PolymorphismSchema> itemSchemaList, Func<Type, string, TInfo> memberFactory, Func<TContext, TConstruct, TInfo, TConstruct> chainConstructFactory, bool isAsync )
93102
{
94103
// Note: cardinality is put as array length by PackHelper.
95104
var depth = -1;
96-
var tupleTypeList = TupleItems.CreateTupleTypeList( itemTypes );
97-
var propertyInvocationChain = new List<PropertyInfo>( itemTypes.Count % 7 + 1 );
105+
var tupleTypeList = TupleItems.CreateTupleTypeList( this.TargetType );
106+
var memberInvocationChain = new List<TInfo>( itemTypes.Count % 7 + 1 );
98107
var packValueArguments =
99108
new[] { context.Packer, context.PackToTarget }
100109
#if FEATURE_TAP
101110
.Concat( isAsync ? new[] { this.ReferCancellationToken( context, 3 ) } : NoConstructs ).ToArray()
102111
#endif // FEATURE_TAP
103112
;
104113

105-
for ( int i = 0; i < itemTypes.Count; i++ )
114+
for ( var i = 0; i < itemTypes.Count; i++ )
106115
{
107116
if ( i % 7 == 0 )
108117
{
109118
depth++;
110119
}
111120

112-
for ( int j = 0; j < depth; j++ )
121+
for ( var j = 0; j < depth; j++ )
113122
{
114123
// .TRest.TRest ...
115-
var restProperty = tupleTypeList[ j ].GetProperty( "Rest" );
124+
var restMember = memberFactory( tupleTypeList[ j ], "Rest" );
116125
#if DEBUG
117-
Contract.Assert( restProperty != null );
126+
Contract.Assert( restMember != null, tupleTypeList[ j ].GetFullName() + ".Rest is not defined" );
118127
#endif
119-
propertyInvocationChain.Add( restProperty );
128+
memberInvocationChain.Add( restMember );
120129
}
121130

122-
var itemNProperty = tupleTypeList[ depth ].GetProperty( "Item" + ( ( i % 7 ) + 1 ) );
123-
propertyInvocationChain.Add( itemNProperty );
131+
var itemNMember = memberFactory( tupleTypeList[ depth ], "Item" + ( ( i % 7 ) + 1 ) );
132+
memberInvocationChain.Add( itemNMember );
124133
#if DEBUG
125134
Contract.Assert(
126-
itemNProperty != null,
135+
itemNMember != null,
127136
tupleTypeList[ depth ].GetFullName() + "::Item" + ( ( i % 7 ) + 1 ) + " [ " + depth + " ] @ " + i
128137
);
129138
#endif
@@ -144,15 +153,16 @@ tupleTypeList[ depth ].GetFullName() + "::Item" + ( ( i % 7 ) + 1 ) + " [ " + de
144153
itemTypes[ count ],
145154
context.Packer,
146155
context.PackToTarget,
147-
propertyInvocationChain,
156+
memberInvocationChain,
148157
itemSchemaList.Count == 0 ? null : itemSchemaList[ count ],
158+
chainConstructFactory,
149159
isAsync
150160
)
151161
),
152162
packValueArguments
153163
);
154164

155-
propertyInvocationChain.Clear();
165+
memberInvocationChain.Clear();
156166
}
157167

158168
var packHelperArguments =
@@ -215,13 +225,14 @@ tupleTypeList[ depth ].GetFullName() + "::Item" + ( ( i % 7 ) + 1 ) + " [ " + de
215225
yield return methodInvocation;
216226
}
217227

218-
private IEnumerable<TConstruct> EmitPackTupleItemStatements(
228+
private IEnumerable<TConstruct> EmitPackTupleItemStatements<TInfo>(
219229
TContext context,
220230
Type itemType,
221231
TConstruct currentPacker,
222232
TConstruct tuple,
223-
IEnumerable<PropertyInfo> propertyInvocationChain,
233+
IEnumerable<TInfo> memberInvocationChain,
224234
PolymorphismSchema itemsSchema,
235+
Func<TContext, TConstruct, TInfo, TConstruct> chainConstructFactory,
225236
bool isAsync
226237
)
227238
{
@@ -232,8 +243,8 @@ bool isAsync
232243
itemType,
233244
NilImplication.Null,
234245
null,
235-
propertyInvocationChain.Aggregate(
236-
tuple, ( propertySource, property ) => this.EmitGetPropertyExpression( context, propertySource, property )
246+
memberInvocationChain.Aggregate(
247+
tuple, ( memberSource, member ) => chainConstructFactory( context, memberSource, member )
237248
),
238249
null,
239250
itemsSchema,
@@ -245,7 +256,7 @@ bool isAsync
245256

246257
#region -- UnpackFrom --
247258

248-
private void BuildTupleUnpackFrom( TContext context, IList<Type> itemTypes, IList<PolymorphismSchema> itemSchemaList, bool isAsync )
259+
private void BuildTupleUnpackFrom( TContext context, IList<Type> itemTypes, IList<PolymorphismSchema> itemSchemaList, bool isValueTuple, bool isAsync )
249260
{
250261
/*
251262
* checked
@@ -283,14 +294,22 @@ private void BuildTupleUnpackFrom( TContext context, IList<Type> itemTypes, ILis
283294
this.EmitSequentialStatements(
284295
context,
285296
this.TargetType,
286-
this.BuildTupleUnpackFromCore( context, itemTypes, itemSchemaList, isAsync )
297+
this.BuildTupleUnpackFromCore( context, itemTypes, itemSchemaList, isValueTuple, isAsync )
287298
)
288299
);
289300
}
290301

291-
private IEnumerable<TConstruct> BuildTupleUnpackFromCore( TContext context, IList<Type> itemTypes, IList<PolymorphismSchema> itemSchemaList, bool isAsync )
302+
private IEnumerable<TConstruct> BuildTupleUnpackFromCore( TContext context, IList<Type> itemTypes, IList<PolymorphismSchema> itemSchemaList, bool isValueTuple, bool isAsync )
303+
{
304+
return
305+
isValueTuple
306+
? BuildTupleUnpackFromCore( context, itemTypes, itemSchemaList, ( t, n ) => t.GetField( n ), isAsync )
307+
: BuildTupleUnpackFromCore( context, itemTypes, itemSchemaList, ( t, n ) => t.GetProperty( n ), isAsync );
308+
}
309+
310+
private IEnumerable<TConstruct> BuildTupleUnpackFromCore<TInfo>( TContext context, IList<Type> itemTypes, IList<PolymorphismSchema> itemSchemaList, Func<Type, string, TInfo> memberFactory, bool isAsync )
292311
{
293-
var tupleTypeList = TupleItems.CreateTupleTypeList( itemTypes );
312+
var tupleTypeList = TupleItems.CreateTupleTypeList( this.TargetType );
294313
yield return
295314
this.EmitCheckIsArrayHeaderExpression( context, context.Unpacker );
296315

@@ -316,14 +335,14 @@ private IEnumerable<TConstruct> BuildTupleUnpackFromCore( TContext context, ILis
316335
;
317336
for ( var i = 0; i < itemTypes.Count; i++ )
318337
{
319-
var propertyName = SerializationTarget.GetTupleItemNameFromIndex( i );
338+
var memberName = SerializationTarget.GetTupleItemNameFromIndex( i );
320339
var unpackedItem = context.DefineUnpackedItemParameterInSetValueMethods( itemTypes[ i ] );
321-
var setUnpackValueOfMethodName = MethodNamePrefix.SetUnpackedValueOf + propertyName;
340+
var setUnpackValueOfMethodName = MethodNamePrefix.SetUnpackedValueOf + memberName;
322341

323342
var index = i;
324343
this.ExtractPrivateMethod(
325344
context,
326-
AdjustName( MethodNamePrefix.UnpackValue + propertyName, isAsync ),
345+
AdjustName( MethodNamePrefix.UnpackValue + memberName, isAsync ),
327346
false, // isStatic
328347
#if FEATURE_TAP
329348
isAsync ? TypeDefinition.TaskType :
@@ -332,7 +351,7 @@ private IEnumerable<TConstruct> BuildTupleUnpackFromCore( TContext context, ILis
332351
() => this.EmitUnpackItemValueStatement(
333352
context,
334353
itemTypes[ index ],
335-
this.MakeStringLiteral( context, propertyName ),
354+
this.MakeStringLiteral( context, memberName ),
336355
context.TupleItemNilImplication,
337356
null, // memberInfo
338357
itemSchemaList.Count == 0 ? null : itemSchemaList[ index ],
@@ -355,7 +374,7 @@ private IEnumerable<TConstruct> BuildTupleUnpackFromCore( TContext context, ILis
355374
context,
356375
context.UnpackingContextInSetValueMethods,
357376
unpackingContext.VariableType,
358-
propertyName,
377+
memberName,
359378
unpackedItem
360379
),
361380
context.UnpackingContextInSetValueMethods,
@@ -368,15 +387,15 @@ private IEnumerable<TConstruct> BuildTupleUnpackFromCore( TContext context, ILis
368387
}
369388

370389
TConstruct currentTuple = null;
371-
for ( int nest = tupleTypeList.Count - 1; nest >= 0; nest-- )
390+
for ( var nest = tupleTypeList.Count - 1; nest >= 0; nest-- )
372391
{
373392
var gets =
374393
Enumerable.Range( nest * 7, Math.Min( itemTypes.Count - nest * 7, 7 ) )
375394
.Select( i =>
376395
this.EmitGetFieldExpression(
377396
context,
378397
context.UnpackingContextInCreateObjectFromContext,
379-
new FieldDefinition(
398+
new FieldDefinition(
380399
unpackingContext.VariableType,
381400
SerializationTarget.GetTupleItemNameFromIndex( i ),
382401
itemTypes[ i ]
@@ -388,13 +407,33 @@ itemTypes[ i ]
388407
gets = gets.Concat( new[] { currentTuple } );
389408
}
390409

391-
currentTuple =
392-
this.EmitCreateNewObjectExpression(
393-
context,
394-
null, // Tuple is reference contextType.
395-
tupleTypeList[ nest ].GetConstructors().Single(),
396-
gets.ToArray()
397-
);
410+
var constructor = tupleTypeList[ nest ].GetConstructors().SingleOrDefault();
411+
if ( constructor == null )
412+
{
413+
// arity 0 value tuple
414+
#if DEBUG
415+
Contract.Assert( tupleTypeList[ nest ].GetFullName() == "System.ValueTuple", tupleTypeList[ nest ].GetFullName() + " == System.ValueTuple");
416+
#endif
417+
currentTuple = this.MakeDefaultLiteral( context, tupleTypeList[ nest ] );
418+
}
419+
else
420+
{
421+
var tempVariable = default( TConstruct );
422+
423+
if ( tupleTypeList[ nest ].GetIsValueType() )
424+
{
425+
// Temp var is required for value type (that is, ValueTuple)
426+
tempVariable = this.DeclareLocal( context, tupleTypeList[ nest ], context.GetUniqueVariableName( "tuple" ) );
427+
}
428+
429+
currentTuple =
430+
this.EmitCreateNewObjectExpression(
431+
context,
432+
tempVariable, // Tuple is reference contextType.
433+
constructor,
434+
gets.ToArray()
435+
);
436+
}
398437
}
399438

400439
#if DEBUG

0 commit comments

Comments
 (0)