Skip to content

Commit cc06b80

Browse files
committed
Add IPackable/IUnpable and their async counterparts support for collection (that is, known collection types and unknown IEnumerable and Add(T) implementing types). Issue msgpack#153.
This commit add IPackable related interfaces recognization to serializer builder code for collection. This change also causes many file changes to remove 'sealed' from methods. In addition, reflection serializers are also changed because they do not rely on serializer builders. For consistency, the instance creation is done via CreateInstance method with capacity '0'. For compatibility, this commit also add SerializationCompatibilityOptions.IgnorePackabilityForCollection. This flag is false by default because the old behavior is design bug. To disable this new behavior, user can set the flag true.
1 parent 4bb67ca commit cc06b80

46 files changed

Lines changed: 28209 additions & 86 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

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

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,24 +51,65 @@ PolymorphismSchema schema
5151
bool isAddItemRequired;
5252
this.DetermineSerializationStrategy( out isUnpackFromRequired, out isAddItemRequired );
5353

54+
if ( typeof( IPackable ).IsAssignableFrom( this.TargetType ) )
55+
{
56+
this.BuildIPackablePackTo( context );
57+
}
58+
#if FEATURE_TAP
59+
60+
if ( this.WithAsync( context ) )
61+
{
62+
if ( typeof( IAsyncPackable ).IsAssignableFrom( this.TargetType ) )
63+
{
64+
this.BuildIAsyncPackablePackTo( context );
65+
}
66+
}
67+
68+
#endif // FEATURE_TAP
69+
70+
this.BuildCollectionCreateInstance( context, concreteType );
71+
72+
var useUnpackable = false;
73+
74+
if ( typeof( IUnpackable ).IsAssignableFrom( concreteType ?? this.TargetType ) )
75+
{
76+
this.BuildIUnpackableUnpackFrom( context, this.GetUnpackableCollectionInstantiation( context ) );
77+
useUnpackable = true;
78+
}
79+
80+
#if FEATURE_TAP
81+
82+
if ( this.WithAsync( context ) )
83+
{
84+
if ( typeof( IAsyncUnpackable ).IsAssignableFrom( concreteType ?? this.TargetType ) )
85+
{
86+
this.BuildIAsyncUnpackableUnpackFrom( context, this.GetUnpackableCollectionInstantiation( context ) );
87+
useUnpackable = true;
88+
}
89+
}
90+
91+
#endif // FEATURE_TAP
92+
5493
if ( isAddItemRequired )
5594
{
56-
// For IEnumerable implements and IReadOnlyXXX implements
57-
if ( this.CollectionTraits.AddMethod != null )
95+
if ( useUnpackable )
5896
{
59-
// For standard path.
60-
this.BuildCollectionAddItem( context, this.CollectionTraits );
97+
// AddItem should never called because UnpackFromCore calls IUnpackable/IAsyncUnpackable
98+
this.BuildCollectionAddItemNotImplemented( context );
6199
}
62100
else
63101
{
64-
// For concrete collection path.
65-
this.BuildCollectionAddItem( context, concreteType.GetCollectionTraits() );
102+
// For IEnumerable implements and IReadOnlyXXX implements
103+
this.BuildCollectionAddItem(
104+
context,
105+
this.CollectionTraits.AddMethod != null
106+
? this.CollectionTraits // For declared collection.
107+
: ( concreteType ?? this.TargetType ).GetCollectionTraits() // For concrete collection.
108+
);
66109
}
67110
}
68111

69-
this.BuildCollectionCreateInstance( context, concreteType );
70-
71-
if ( isUnpackFromRequired )
112+
if ( isUnpackFromRequired && !useUnpackable )
72113
{
73114
this.BuildCollectionUnpackFromCore( context, concreteType, schema, false );
74115
#if FEATURE_TAP
@@ -141,6 +182,17 @@ private void DetermineSerializationStrategy( out bool isUnpackFromRequired, out
141182
} // switch
142183
}
143184

185+
private TConstruct GetUnpackableCollectionInstantiation( TContext context )
186+
{
187+
return
188+
this.EmitInvokeMethodExpression(
189+
context,
190+
this.EmitThisReferenceExpression( context ),
191+
context.GetDeclaredMethod( MethodName.CreateInstance ),
192+
this.MakeInt32Literal( context, 0 ) // Always 0
193+
);
194+
}
195+
144196
#region -- AddItem --
145197

146198
private void BuildCollectionAddItem( TContext context, CollectionTraits traits )
@@ -170,6 +222,15 @@ private void BuildCollectionAddItem( TContext context, CollectionTraits traits )
170222
);
171223
}
172224

225+
private void BuildCollectionAddItemNotImplemented( TContext context )
226+
{
227+
context.BeginMethodOverride( MethodName.AddItem );
228+
context.EndMethodOverride(
229+
MethodName.AddItem,
230+
this.EmitSequentialStatements( context, typeof( void ) ) // nop
231+
);
232+
}
233+
173234
#endregion -- AddItem --
174235

175236
#region -- UnpackFromCore --

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

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ private void BuildObjectSerializer( TContext context, out SerializationTarget ta
7070

7171
if ( typeof( IUnpackable ).IsAssignableFrom( this.TargetType ) )
7272
{
73-
this.BuildIUnpackableUnpackFrom( context );
73+
this.BuildIUnpackableUnpackFrom( context, this.GetUnpackableObjectInstantiation( context ) );
7474
}
7575
else
7676
{
@@ -83,7 +83,7 @@ private void BuildObjectSerializer( TContext context, out SerializationTarget ta
8383
{
8484
if ( typeof( IAsyncUnpackable ).IsAssignableFrom( this.TargetType ) )
8585
{
86-
this.BuildIAsyncUnpackableUnpackFrom( context );
86+
this.BuildIAsyncUnpackableUnpackFrom( context, this.GetUnpackableObjectInstantiation( context ) );
8787
}
8888
else
8989
{
@@ -490,26 +490,37 @@ private static string GetPackValueMethodName( SerializingMember member, bool isA
490490

491491
#region -- IUnpackable --
492492

493-
private void BuildIUnpackableUnpackFrom( TContext context )
493+
private void BuildIUnpackableUnpackFrom( TContext context, TConstruct objectCreation )
494494
{
495495

496496
context.BeginMethodOverride( MethodName.UnpackFromCore );
497497
context.EndMethodOverride( MethodName.UnpackFromCore,
498498
this.EmitSequentialStatements(
499499
context,
500500
this.TargetType,
501-
this.BuildIUnpackableUnpackFromCore( context, typeof( IUnpackable ) )
501+
this.BuildIUnpackableUnpackFromCore( context, typeof( IUnpackable ), objectCreation )
502502
)
503503
);
504504
}
505505

506+
private TConstruct GetUnpackableObjectInstantiation( TContext context )
507+
{
508+
return
509+
this.EmitCreateNewObjectExpression(
510+
context,
511+
null,
512+
// reference contextType.
513+
this.GetDefaultConstructor( this.TargetType )
514+
);
515+
}
516+
506517
#endregion -- IUnpackable --
507518

508519
#if FEATURE_TAP
509520

510521
#region -- IAsyncUnpackable --
511522

512-
private void BuildIAsyncUnpackableUnpackFrom( TContext context )
523+
private void BuildIAsyncUnpackableUnpackFrom( TContext context, TConstruct objectCreation )
513524
{
514525

515526
context.BeginMethodOverride( MethodName.UnpackFromAsyncCore );
@@ -518,7 +529,7 @@ private void BuildIAsyncUnpackableUnpackFrom( TContext context )
518529
this.EmitSequentialStatements(
519530
context,
520531
this.TargetType,
521-
this.BuildIUnpackableUnpackFromCore( context, typeof( IAsyncUnpackable ) )
532+
this.BuildIUnpackableUnpackFromCore( context, typeof( IAsyncUnpackable ), objectCreation )
522533
)
523534
);
524535
}
@@ -529,7 +540,7 @@ private void BuildIAsyncUnpackableUnpackFrom( TContext context )
529540

530541
#region -- UnpackFrom --
531542

532-
private IEnumerable<TConstruct> BuildIUnpackableUnpackFromCore( TContext context, Type @interface )
543+
private IEnumerable<TConstruct> BuildIUnpackableUnpackFromCore( TContext context, Type @interface, TConstruct objectCreation )
533544
{
534545
var result =
535546
this.DeclareLocal(
@@ -546,11 +557,7 @@ private IEnumerable<TConstruct> BuildIUnpackableUnpackFromCore( TContext context
546557
this.EmitStoreVariableStatement(
547558
context,
548559
result,
549-
this.EmitCreateNewObjectExpression(
550-
context,
551-
null, // reference contextType.
552-
this.GetDefaultConstructor( this.TargetType )
553-
)
560+
objectCreation
554561
);
555562
}
556563

src/MsgPack/Serialization/CollectionSerializers/CollectionMessagePackSerializerBase`2.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// MessagePack for CLI
44
//
5-
// Copyright (C) 2015 FUJIWARA, Yusuke
5+
// Copyright (C) 2015-2016 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.
@@ -65,7 +65,7 @@ protected CollectionMessagePackSerializerBase( SerializationContext ownerContext
6565
/// </exception>
6666
[System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", MessageId = "0", Justification = "Validated by caller in base class" )]
6767
[System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", MessageId = "1", Justification = "Validated by caller in base class" )]
68-
protected internal sealed override void PackToCore( Packer packer, TCollection objectTree )
68+
protected internal override void PackToCore( Packer packer, TCollection objectTree )
6969
{
7070
packer.PackArrayHeader( this.GetCount( objectTree ) );
7171
#if ( !UNITY && !XAMIOS ) || AOT_CHECK
@@ -75,8 +75,8 @@ protected internal sealed override void PackToCore( Packer packer, TCollection o
7575
itemSerializer.PackTo( packer, item );
7676
}
7777
#else
78-
// .constraind call for TCollection.get_Count/TCollection.GetEnumerator() causes AOT error.
79-
// So use cast and invoke as normal call (it might cause boxing, but most collection should be reference type).
78+
// .constraind call for TCollection.get_Count/TCollection.GetEnumerator() causes AOT error.
79+
// So use cast and invoke as normal call (it might cause boxing, but most collection should be reference type).
8080
var itemSerializer = this.ItemSerializer;
8181
foreach ( var item in objectTree as IEnumerable<TItem> )
8282
{
@@ -103,7 +103,7 @@ protected internal sealed override void PackToCore( Packer packer, TCollection o
103103
/// <typeparamref name="TCollection"/> is not serializable even if it can be deserialized.
104104
/// </exception>
105105
/// <seealso cref="P:Capabilities"/>
106-
protected internal sealed override async Task PackToAsyncCore( Packer packer, TCollection objectTree, CancellationToken cancellationToken )
106+
protected internal override async Task PackToAsyncCore( Packer packer, TCollection objectTree, CancellationToken cancellationToken )
107107
{
108108
await packer.PackArrayHeaderAsync( this.GetCount( objectTree ), cancellationToken ).ConfigureAwait( false );
109109
#if ( !UNITY && !XAMIOS ) || AOT_CHECK
@@ -153,7 +153,7 @@ protected internal sealed override async Task PackToAsyncCore( Packer packer, TC
153153
/// This method invokes <see cref="EnumerableMessagePackSerializerBase{TCollection,TItem}.CreateInstance(int)"/>, and then fill deserialized items to resultong collection.
154154
/// </remarks>
155155
[System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", MessageId = "0", Justification = "Asserted internally" )]
156-
protected internal sealed override TCollection UnpackFromCore( Unpacker unpacker )
156+
protected internal override TCollection UnpackFromCore( Unpacker unpacker )
157157
{
158158
if ( !unpacker.IsArrayHeader )
159159
{
@@ -195,7 +195,7 @@ internal virtual TCollection InternalUnpackFromCore( Unpacker unpacker )
195195
/// <typeparamref name="TCollection"/> is not serializable even if it can be serialized.
196196
/// </exception>
197197
/// <seealso cref="P:Capabilities"/>
198-
protected internal sealed override Task<TCollection> UnpackFromAsyncCore( Unpacker unpacker, CancellationToken cancellationToken )
198+
protected internal override Task<TCollection> UnpackFromAsyncCore( Unpacker unpacker, CancellationToken cancellationToken )
199199
{
200200
if ( !unpacker.IsArrayHeader )
201201
{

src/MsgPack/Serialization/CollectionSerializers/CollectionMessagePackSerializer`2.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// MessagePack for CLI
44
//
5-
// Copyright (C) 2015 FUJIWARA, Yusuke
5+
// Copyright (C) 2015-2016 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.
@@ -107,7 +107,7 @@ PolymorphismSchema schema
107107
this._add = traits.AddMethod;
108108
}
109109

110-
protected internal sealed override void PackToCore( Packer packer, object objectTree )
110+
protected internal override void PackToCore( Packer packer, object objectTree )
111111
{
112112
packer.PackArrayHeader( ( int )this._getCount.InvokePreservingExceptionType( objectTree ) );
113113
var itemSerializer = this.ItemSerializer;
@@ -119,7 +119,7 @@ protected internal sealed override void PackToCore( Packer packer, object object
119119
}
120120
}
121121

122-
protected internal sealed override object UnpackFromCore( Unpacker unpacker )
122+
protected internal override object UnpackFromCore( Unpacker unpacker )
123123
{
124124
if ( !unpacker.IsArrayHeader )
125125
{

src/MsgPack/Serialization/CollectionSerializers/DictionaryMessagePackSerializerBase`3.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// MessagePack for CLI
44
//
5-
// Copyright (C) 2015 FUJIWARA, Yusuke
5+
// Copyright (C) 2015-2016 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.
@@ -79,7 +79,7 @@ protected DictionaryMessagePackSerializerBase( SerializationContext ownerContext
7979
/// </exception>
8080
[System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", MessageId = "0", Justification = "Validated by caller in base class" )]
8181
[System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", MessageId = "1", Justification = "Validated by caller in base class" )]
82-
protected internal sealed override void PackToCore( Packer packer, TDictionary objectTree )
82+
protected internal override void PackToCore( Packer packer, TDictionary objectTree )
8383
{
8484
#if ( !UNITY && !XAMIOS ) || AOT_CHECK
8585
packer.PackMapHeader( this.GetCount( objectTree ) );
@@ -118,7 +118,7 @@ protected internal sealed override void PackToCore( Packer packer, TDictionary o
118118
/// <typeparamref name="TDictionary"/> is not serializable even if it can be deserialized.
119119
/// </exception>
120120
/// <seealso cref="P:Capabilities"/>
121-
protected internal sealed override async Task PackToAsyncCore( Packer packer, TDictionary objectTree, CancellationToken cancellationToken )
121+
protected internal override async Task PackToAsyncCore( Packer packer, TDictionary objectTree, CancellationToken cancellationToken )
122122
{
123123
#if ( !UNITY && !XAMIOS ) || AOT_CHECK
124124
await packer.PackMapHeaderAsync( this.GetCount( objectTree ), cancellationToken ).ConfigureAwait( false );
@@ -169,7 +169,7 @@ protected internal sealed override async Task PackToAsyncCore( Packer packer, TD
169169
/// This method invokes <see cref="CreateInstance(int)"/>, and then fill deserialized items to resultong collection.
170170
/// </remarks>
171171
[System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", MessageId = "0", Justification = "Validated by caller in base class" )]
172-
protected internal sealed override TDictionary UnpackFromCore( Unpacker unpacker )
172+
protected internal override TDictionary UnpackFromCore( Unpacker unpacker )
173173
{
174174
if ( !unpacker.IsMapHeader )
175175
{
@@ -211,7 +211,7 @@ internal virtual TDictionary InternalUnpackFromCore( Unpacker unpacker )
211211
/// <typeparamref name="TDictionary"/> is not serializable even if it can be serialized.
212212
/// </exception>
213213
/// <seealso cref="P:Capabilities"/>
214-
protected internal sealed override Task<TDictionary> UnpackFromAsyncCore( Unpacker unpacker, CancellationToken cancellationToken )
214+
protected internal override Task<TDictionary> UnpackFromAsyncCore( Unpacker unpacker, CancellationToken cancellationToken )
215215
{
216216
if ( !unpacker.IsMapHeader )
217217
{
@@ -267,7 +267,7 @@ object ICollectionInstanceFactory.CreateInstance( int initialCapacity )
267267
/// </exception>
268268
[System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", MessageId = "0", Justification = "Validated by caller in base class" )]
269269
[System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", MessageId = "1", Justification = "Validated by caller in base class" )]
270-
protected internal sealed override void UnpackToCore( Unpacker unpacker, TDictionary collection )
270+
protected internal override void UnpackToCore( Unpacker unpacker, TDictionary collection )
271271
{
272272
if ( !unpacker.IsMapHeader )
273273
{
@@ -346,7 +346,7 @@ private void UnpackToCore( Unpacker unpacker, TDictionary collection, int itemsC
346346
/// <typeparamref name="TDictionary"/> is not mutable collection.
347347
/// </exception>
348348
/// <seealso cref="P:Capabilities"/>
349-
protected internal sealed override Task UnpackToAsyncCore( Unpacker unpacker, TDictionary collection, CancellationToken cancellationToken )
349+
protected internal override Task UnpackToAsyncCore( Unpacker unpacker, TDictionary collection, CancellationToken cancellationToken )
350350
{
351351
if ( !unpacker.IsMapHeader )
352352
{

src/MsgPack/Serialization/CollectionSerializers/DictionaryMessagePackSerializer`3.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// MessagePack for CLI
44
//
5-
// Copyright (C) 2015 FUJIWARA, Yusuke
5+
// Copyright (C) 2015-2016 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.
@@ -128,7 +128,7 @@ PolymorphismSchema schema
128128
this._getValue = traits.ElementType.GetProperty( "Value" ).GetGetMethod();
129129
}
130130

131-
protected internal override sealed void PackToCore( Packer packer, object objectTree )
131+
protected internal override void PackToCore( Packer packer, object objectTree )
132132
{
133133
packer.PackMapHeader( ( int )this._getCount.InvokePreservingExceptionType( objectTree ) );
134134
// ReSharper disable once PossibleNullReferenceException
@@ -139,7 +139,7 @@ protected internal override sealed void PackToCore( Packer packer, object object
139139
}
140140
}
141141

142-
protected internal override sealed object UnpackFromCore( Unpacker unpacker )
142+
protected internal override object UnpackFromCore( Unpacker unpacker )
143143
{
144144
if ( !unpacker.IsMapHeader )
145145
{
@@ -164,7 +164,7 @@ object ICollectionInstanceFactory.CreateInstance( int initialCapacity )
164164
return this.CreateInstance( initialCapacity );
165165
}
166166

167-
protected internal override sealed void UnpackToCore( Unpacker unpacker, object collection )
167+
protected internal override void UnpackToCore( Unpacker unpacker, object collection )
168168
{
169169
if ( !unpacker.IsMapHeader )
170170
{

0 commit comments

Comments
 (0)