Skip to content

Commit 9212817

Browse files
committed
Implement asymmetric serializers. #68
This commit relaxes serialization target retrieval, so it allows 'asymmetric'(pack only) serializer generation. * This commit also adds unit tests. * Unpack only serializer is not supported yet because there are no patterns found which can be deserialize but cannot be serialize. * Supporting capabilities is needed to distingush pack only serializers from normal serializers.
1 parent 4fcc475 commit 9212817

35 files changed

Lines changed: 2859 additions & 216 deletions

File tree

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

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
using System.Diagnostics.Contracts;
3030
#endif // CORE_CLR || UNITY
3131
using System.Linq;
32+
using System.Reflection;
3233
#if FEATURE_TAP
3334
using System.Threading;
3435
using System.Threading.Tasks;
@@ -42,15 +43,16 @@ partial class SerializerBuilder<TContext, TConstruct>
4243
private void BuildCollectionSerializer(
4344
TContext context,
4445
Type concreteType,
45-
PolymorphismSchema schema
46+
PolymorphismSchema schema,
47+
out SerializationTarget targetInfo
4648
)
4749
{
4850
#if DEBUG
4951
Contract.Assert( this.CollectionTraits.DetailedCollectionType != CollectionDetailedKind.Array );
5052
#endif // DEBUG
5153
bool isUnpackFromRequired;
5254
bool isAddItemRequired;
53-
this.DetermineSerializationStrategy( out isUnpackFromRequired, out isAddItemRequired );
55+
this.DetermineSerializationStrategy( context, concreteType, out targetInfo, out isUnpackFromRequired, out isAddItemRequired );
5456

5557
if ( typeof( IPackable ).IsAssignableFrom( this.TargetType ) )
5658
{
@@ -68,13 +70,13 @@ PolymorphismSchema schema
6870

6971
#endif // FEATURE_TAP
7072

71-
this.BuildCollectionCreateInstance( context, concreteType );
73+
this.BuildCollectionCreateInstance( context, targetInfo.DeserializationConstructor, targetInfo.CanDeserialize );
7274

7375
var useUnpackable = false;
7476

7577
if ( typeof( IUnpackable ).IsAssignableFrom( concreteType ?? this.TargetType ) )
7678
{
77-
this.BuildIUnpackableUnpackFrom( context, this.GetUnpackableCollectionInstantiation( context ) );
79+
this.BuildIUnpackableUnpackFrom( context, this.GetUnpackableCollectionInstantiation( context ), targetInfo.CanDeserialize );
7880
useUnpackable = true;
7981
}
8082

@@ -84,7 +86,7 @@ PolymorphismSchema schema
8486
{
8587
if ( typeof( IAsyncUnpackable ).IsAssignableFrom( concreteType ?? this.TargetType ) )
8688
{
87-
this.BuildIAsyncUnpackableUnpackFrom( context, this.GetUnpackableCollectionInstantiation( context ) );
89+
this.BuildIAsyncUnpackableUnpackFrom( context, this.GetUnpackableCollectionInstantiation( context ), targetInfo.CanDeserialize );
8890
useUnpackable = true;
8991
}
9092
}
@@ -93,7 +95,7 @@ PolymorphismSchema schema
9395

9496
if ( isAddItemRequired )
9597
{
96-
if ( useUnpackable )
98+
if ( useUnpackable || !targetInfo.CanDeserialize )
9799
{
98100
// AddItem should never called because UnpackFromCore calls IUnpackable/IAsyncUnpackable
99101
this.BuildCollectionAddItemNotImplemented( context );
@@ -112,20 +114,32 @@ PolymorphismSchema schema
112114

113115
if ( isUnpackFromRequired && !useUnpackable )
114116
{
115-
this.BuildCollectionUnpackFromCore( context, concreteType, schema, false );
117+
this.BuildCollectionUnpackFromCore( context, concreteType, schema, targetInfo.CanDeserialize, isAsync: false );
116118
#if FEATURE_TAP
117119
if ( this.WithAsync( context ) )
118120
{
119-
this.BuildCollectionUnpackFromCore( context, concreteType, schema, true );
121+
this.BuildCollectionUnpackFromCore( context, concreteType, schema, targetInfo.CanDeserialize, isAsync: true );
120122
}
121123
#endif // FEATURE_TAP
122124
}
123125

124126
this.BuildRestoreSchema( context, schema );
125127
}
126128

127-
private void DetermineSerializationStrategy( out bool isUnpackFromRequired, out bool isAddItemRequired )
129+
private void DetermineSerializationStrategy(
130+
TContext context,
131+
Type concreteType,
132+
out SerializationTarget targetInfo,
133+
out bool isUnpackFromRequired,
134+
out bool isAddItemRequired
135+
)
128136
{
137+
targetInfo =
138+
UnpackHelpers.DetermineCollectionSerializationStrategy(
139+
concreteType ?? this.TargetType,
140+
context.SerializationContext.CompatibilityOptions.AllowAsymmetricSerializer
141+
);
142+
129143
switch ( this.CollectionTraits.DetailedCollectionType )
130144
{
131145
case CollectionDetailedKind.NonGenericEnumerable:
@@ -236,7 +250,7 @@ private void BuildCollectionAddItemNotImplemented( TContext context )
236250

237251
#region -- UnpackFromCore --
238252

239-
private void BuildCollectionUnpackFromCore( TContext context, Type concreteType, PolymorphismSchema schema, bool isAsync )
253+
private void BuildCollectionUnpackFromCore( TContext context, Type concreteType, PolymorphismSchema schema, bool canDeserialize, bool isAsync )
240254
{
241255
var methodName =
242256
#if FEATURE_TAP
@@ -250,11 +264,12 @@ private void BuildCollectionUnpackFromCore( TContext context, Type concreteType,
250264

251265
context.EndMethodOverride(
252266
methodName,
253-
this.EmitSequentialStatements(
267+
canDeserialize
268+
? this.EmitSequentialStatements(
254269
context,
255270
this.TargetType,
256271
this.EmitCollectionUnpackFromStatements( context, instanceType, schema, isAsync )
257-
)
272+
) : this.EmitThrowCannotUnpackFrom( context )
258273
);
259274
}
260275

@@ -434,22 +449,21 @@ private TConstruct EmitGetItemsCountExpression( TContext context, TConstruct unp
434449

435450
#region -- CreateInstance --
436451

437-
private void BuildCollectionCreateInstance( TContext context, Type concreteType )
452+
private void BuildCollectionCreateInstance( TContext context, ConstructorInfo collectionConstructor, bool canDeserialize )
438453
{
439454
context.BeginMethodOverride( MethodName.CreateInstance );
440455

441-
var instanceType = concreteType ?? this.TargetType;
442456
var collection =
443457
this.DeclareLocal(
444458
context,
445459
this.TargetType,
446460
"collection"
447461
);
448-
var ctor = UnpackHelpers.GetCollectionConstructor( instanceType );
449-
var ctorArguments = this.DetermineCollectionConstructorArguments( context, ctor );
462+
450463
context.EndMethodOverride(
451464
MethodName.CreateInstance,
452-
this.EmitSequentialStatements(
465+
canDeserialize
466+
? this.EmitSequentialStatements(
453467
context,
454468
this.TargetType,
455469
collection,
@@ -459,15 +473,15 @@ private void BuildCollectionCreateInstance( TContext context, Type concreteType
459473
this.EmitCreateNewObjectExpression(
460474
context,
461475
collection,
462-
ctor,
463-
ctorArguments
476+
collectionConstructor,
477+
this.DetermineCollectionConstructorArguments( context, collectionConstructor )
464478
)
465479
),
466480
this.EmitRetrunStatement(
467481
context,
468482
this.EmitLoadVariableExpression( context, collection )
469483
)
470-
)
484+
) : this.EmitThrowCannotCreateInstance( context )
471485
);
472486
}
473487

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,14 @@ protected virtual TConstruct EmitNotEqualsExpression( TContext context, TConstru
418418
/// <returns>The generated construct.</returns>
419419
protected abstract TConstruct EmitFieldOfExpression( TContext context, FieldInfo field );
420420

421+
/// <summary>
422+
/// Emits the 'throw' statement.
423+
/// </summary>
424+
/// <param name="context">The generation context.</param>
425+
/// <param name="exception">The expression to returns an exception.</param>
426+
/// <returns>The generated construct.</returns>
427+
protected abstract TConstruct EmitThrowStatement( TContext context, TConstruct exception );
428+
421429
#endregion -- Operations --
422430

423431
#region -- Aggregation --
@@ -1594,6 +1602,34 @@ private TConstruct EmitAppendDictionaryItem( TContext context, CollectionTraits
15941602
);
15951603
}
15961604

1605+
private TConstruct EmitThrowCannotUnpackFrom( TContext context )
1606+
{
1607+
return
1608+
this.EmitThrowStatement(
1609+
context,
1610+
this.EmitInvokeMethodExpression(
1611+
context,
1612+
null,
1613+
SerializationExceptions.NewUnpackFromIsNotSupportedMethod,
1614+
this.EmitTypeOfExpression( context, this.TargetType )
1615+
)
1616+
);
1617+
}
1618+
1619+
private TConstruct EmitThrowCannotCreateInstance( TContext context )
1620+
{
1621+
return
1622+
this.EmitThrowStatement(
1623+
context,
1624+
this.EmitInvokeMethodExpression(
1625+
context,
1626+
null,
1627+
SerializationExceptions.NewCreateInstanceIsNotSupportedMethod,
1628+
this.EmitTypeOfExpression( context, this.TargetType )
1629+
)
1630+
);
1631+
}
1632+
15971633
#endregion -- Unpack Constructs --
15981634

15991635
#region -- Helper Construcs --

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

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ namespace MsgPack.Serialization.AbstractSerializers
3838
{
3939
partial class SerializerBuilder<TContext, TConstruct>
4040
{
41-
private void BuildObjectSerializer( TContext context, out SerializationTarget targetInfo )
41+
private SerializationTarget BuildObjectSerializer( TContext context )
4242
{
4343
SerializationTarget.VerifyType( this.TargetType );
44-
targetInfo = SerializationTarget.Prepare( context.SerializationContext, this.TargetType );
44+
var targetInfo = SerializationTarget.Prepare( context.SerializationContext, this.TargetType );
4545

4646
if ( typeof( IPackable ).IsAssignableFrom( this.TargetType ) )
4747
{
@@ -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, this.GetUnpackableObjectInstantiation( context ) );
73+
this.BuildIUnpackableUnpackFrom( context, this.GetUnpackableObjectInstantiation( context ), targetInfo.CanDeserialize );
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, this.GetUnpackableObjectInstantiation( context ) );
86+
this.BuildIAsyncUnpackableUnpackFrom( context, this.GetUnpackableObjectInstantiation( context ), targetInfo.CanDeserialize );
8787
}
8888
else
8989
{
@@ -92,6 +92,8 @@ private void BuildObjectSerializer( TContext context, out SerializationTarget ta
9292
}
9393

9494
#endif // FEATURE_TAP
95+
96+
return targetInfo;
9597
}
9698

9799
#region -- IPackable --
@@ -646,16 +648,17 @@ private static string GetCheckNullMethodName( SerializingMember member )
646648

647649
#region -- IUnpackable --
648650

649-
private void BuildIUnpackableUnpackFrom( TContext context, TConstruct objectCreation )
651+
private void BuildIUnpackableUnpackFrom( TContext context, TConstruct objectCreation, bool canDeserialize )
650652
{
651653

652654
context.BeginMethodOverride( MethodName.UnpackFromCore );
653655
context.EndMethodOverride( MethodName.UnpackFromCore,
654-
this.EmitSequentialStatements(
656+
canDeserialize
657+
? this.EmitSequentialStatements(
655658
context,
656659
this.TargetType,
657660
this.BuildIUnpackableUnpackFromCore( context, typeof( IUnpackable ), objectCreation )
658-
)
661+
) : this.EmitThrowCannotUnpackFrom( context )
659662
);
660663
}
661664

@@ -676,17 +679,17 @@ private TConstruct GetUnpackableObjectInstantiation( TContext context )
676679

677680
#region -- IAsyncUnpackable --
678681

679-
private void BuildIAsyncUnpackableUnpackFrom( TContext context, TConstruct objectCreation )
682+
private void BuildIAsyncUnpackableUnpackFrom( TContext context, TConstruct objectCreation, bool canDeserialize )
680683
{
681-
682684
context.BeginMethodOverride( MethodName.UnpackFromAsyncCore );
683685
context.EndMethodOverride(
684686
MethodName.UnpackFromAsyncCore,
685-
this.EmitSequentialStatements(
687+
canDeserialize
688+
? this.EmitSequentialStatements(
686689
context,
687690
this.TargetType,
688691
this.BuildIUnpackableUnpackFromCore( context, typeof( IAsyncUnpackable ), objectCreation )
689-
)
692+
) : this.EmitThrowCannotUnpackFrom( context )
690693
);
691694
}
692695

@@ -771,11 +774,12 @@ private void BuildObjectUnpackFrom( TContext context, SerializationTarget target
771774
context.BeginMethodOverride( methodName );
772775
context.EndMethodOverride(
773776
methodName,
774-
this.EmitSequentialStatements(
777+
targetInfo.CanDeserialize
778+
? this.EmitSequentialStatements(
775779
context,
776780
this.TargetType,
777781
this.EmitObjectUnpackFromCore( context, targetInfo, isAsync )
778-
)
782+
) : this.EmitThrowCannotUnpackFrom( context )
779783
);
780784
}
781785

@@ -1315,6 +1319,11 @@ private IEnumerable<TConstruct> EmitUnpackActionCollectionInitializationCore(
13151319
bool isAsync
13161320
)
13171321
{
1322+
if ( !targetInfo.CanDeserialize )
1323+
{
1324+
yield break;
1325+
}
1326+
13181327
yield return actionCollection;
13191328

13201329
#if DEBUG
@@ -1410,7 +1419,7 @@ private IEnumerable<TConstruct> InitializeConstructorArgumentInitializationState
14101419
yield return argument;
14111420

14121421
constructorArguments.Add( argument );
1413-
var correspondingMemberName = target.FindCorrespondingMemberName( constructorParameters[ i ] );
1422+
var correspondingMemberName = target.GetCorrespondingMemberName( i );
14141423
if ( correspondingMemberName != null )
14151424
{
14161425
mappableConstructorArguments.Add( correspondingMemberName );

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

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,13 @@ public MessagePackSerializer BuildSerializerInstance( SerializationContext conte
205205
{
206206
SerializationTarget targetInfo;
207207
this.BuildSerializer( codeGenerationContext, concreteType, schema, out targetInfo );
208-
constructor = this.CreateSerializerConstructor( codeGenerationContext, targetInfo, schema );
208+
constructor =
209+
this.CreateSerializerConstructor(
210+
codeGenerationContext,
211+
targetInfo,
212+
schema,
213+
targetInfo == null ? default( SerializerCapabilities? ) : targetInfo.GetCapabilitiesForObject()
214+
);
209215
}
210216

211217
if ( constructor != null )
@@ -237,10 +243,6 @@ public MessagePackSerializer BuildSerializerInstance( SerializationContext conte
237243
/// <param name="concreteType">The substitution type if <see cref="TargetType"/> is abstract type. <c>null</c> when <see cref="TargetType"/> is not abstract type.</param>
238244
/// <param name="schema">The schema which contains schema for collection items, dictionary keys, or tuple items. This value may be <c>null</c>.</param>
239245
/// <param name="targetInfo">The parsed serialization target information.</param>
240-
/// <returns>
241-
/// Newly created serializer object.
242-
/// This value will not be <c>null</c>.
243-
/// </returns>
244246
protected void BuildSerializer( TContext context, Type concreteType, PolymorphismSchema schema, out SerializationTarget targetInfo )
245247
{
246248
#if DEBUG
@@ -253,8 +255,7 @@ protected void BuildSerializer( TContext context, Type concreteType, Polymorphis
253255
case CollectionKind.Array:
254256
case CollectionKind.Map:
255257
{
256-
targetInfo = null;
257-
this.BuildCollectionSerializer( context, concreteType, schema );
258+
this.BuildCollectionSerializer( context, concreteType, schema, out targetInfo );
258259
break;
259260
}
260261
case CollectionKind.NotCollection:
@@ -276,7 +277,7 @@ protected void BuildSerializer( TContext context, Type concreteType, Polymorphis
276277
#if DEBUG
277278
Contract.Assert( schema == null || schema.UseDefault );
278279
#endif // DEBUG
279-
this.BuildObjectSerializer( context, out targetInfo );
280+
targetInfo = this.BuildObjectSerializer( context );
280281
}
281282
break;
282283
}
@@ -293,14 +294,16 @@ protected void BuildSerializer( TContext context, Type concreteType, Polymorphis
293294
/// <param name="codeGenerationContext">The code generation context.</param>
294295
/// <param name="targetInfo">The parsed serialization target information.</param>
295296
/// <param name="schema">The polymorphism schema of this.</param>
297+
/// <param name="capabilities">The capabilities of the generating serializer.</param>
296298
/// <returns>
297299
/// <see cref="Func{T, TResult}"/> which refers newly created constructor.
298300
/// This value will not be <c>null</c>.
299301
/// </returns>
300302
protected abstract Func<SerializationContext, MessagePackSerializer> CreateSerializerConstructor(
301303
TContext codeGenerationContext,
302304
SerializationTarget targetInfo,
303-
PolymorphismSchema schema
305+
PolymorphismSchema schema,
306+
SerializerCapabilities? capabilities
304307
);
305308

306309
/// <summary>

0 commit comments

Comments
 (0)