@@ -194,6 +194,7 @@ private IEnumerable<TConstruct> BuildObjectPackToCore( TContext context, IList<S
194194 isAsync ? new [ ] { this . ReferCancellationToken ( context , 3 ) } :
195195#endif // FEATURE_TAP
196196 NoConstructs ;
197+ var nullCheckParameters = new [ ] { context . NullCheckTarget } ;
197198 var methodForNull =
198199#if FEATURE_TAP
199200 isAsync ? Metadata . _Packer . PackNullAsync :
@@ -217,9 +218,10 @@ private IEnumerable<TConstruct> BuildObjectPackToCore( TContext context, IList<S
217218 }
218219
219220 // missing member, always nil
220- this . ExtractPrivateMethod (
221+ this . DefinePrivateMethod (
221222 context ,
222223 AdjustName ( MethodName . PackMemberPlaceHolder , isAsync ) ,
224+ false , // isStatic
223225#if FEATURE_TAP
224226 isAsync ? typeof ( Task ) :
225227#endif // FEATURE_TAP
@@ -232,9 +234,12 @@ private IEnumerable<TConstruct> BuildObjectPackToCore( TContext context, IList<S
232234 }
233235 else
234236 {
235- this . ExtractPrivateMethod (
237+ var itemType = entries [ count ] . Member . GetMemberValueType ( ) ;
238+ // PackValue
239+ this . DefinePrivateMethod (
236240 context ,
237241 GetPackValueMethodName ( entries [ i ] , isAsync ) ,
242+ false , // isStatic
238243#if FEATURE_TAP
239244 isAsync ? typeof ( Task ) :
240245#endif // FEATURE_TAP
@@ -245,7 +250,7 @@ private IEnumerable<TConstruct> BuildObjectPackToCore( TContext context, IList<S
245250 this . EmitPackItemStatements (
246251 context ,
247252 context . Packer ,
248- entries [ count ] . Member . GetMemberValueType ( ) ,
253+ itemType ,
249254 entries [ count ] . Contract . NilImplication ,
250255 entries [ count ] . Member . ToString ( ) ,
251256 this . EmitGetMemberValueExpression ( context , context . PackToTarget , entries [ count ] . Member ) ,
@@ -256,6 +261,32 @@ entries[ count ].Member.ToString(),
256261 ) ,
257262 parameters
258263 ) ;
264+
265+ if ( ! SerializerDebugging . UseLegacyNullMapEntryHandling
266+ && ( ! itemType . GetIsValueType ( ) || Nullable . GetUnderlyingType ( itemType ) != null ) )
267+ {
268+ var nullCheckTarget = this . EmitGetMemberValueExpression ( context , context . NullCheckTarget , entries [ count ] . Member ) ;
269+ // Trying uses nullCheckTarget.ContextType because it may be Object when using reflection.
270+ var nullCheckTargetType = nullCheckTarget . ContextType . TryGetRuntimeType ( ) ?? itemType ;
271+ // CheckNull
272+ this . DefinePrivateMethod (
273+ context ,
274+ GetCheckNullMethodName ( entries [ i ] ) ,
275+ false , // isStatic
276+ typeof ( bool ) ,
277+ ( ) =>
278+ nullCheckTargetType . GetIsValueType ( ) // Is Nullable<T> ?
279+ ? this . EmitSequentialStatements (
280+ context ,
281+ typeof ( bool ) ,
282+ this . EmitHasValueCore ( context , nullCheckTarget , nullCheckTargetType )
283+ ) : this . EmitRetrunStatement (
284+ context ,
285+ this . EmitEqualsExpression ( context , nullCheckTarget , this . MakeNullLiteral ( context , nullCheckTargetType ) )
286+ ) ,
287+ nullCheckParameters
288+ ) ;
289+ }
259290 }
260291 }
261292
@@ -276,6 +307,16 @@ entries[ count ].Member.ToString(),
276307 }
277308 } ;
278309
310+ if ( method == SerializationMethod . Map && ! SerializerDebugging . UseLegacyNullMapEntryHandling )
311+ {
312+ packHelperArguments . Add (
313+ "SerializationContext" ,
314+ this . EmitGetPropertyExpression ( context , this . EmitThisReferenceExpression ( context ) , Metadata . _MessagePackSerializer . OwnerContext )
315+ ) ;
316+ // isAsync is always false to prevent _nullCheckersTableAsync creation.
317+ packHelperArguments . Add ( "NullCheckers" , this . EmitGetActionsExpression ( context , ActionType . IsNull , isAsync : false ) ) ;
318+ }
319+
279320#if FEATURE_TAP
280321 if ( isAsync )
281322 {
@@ -306,6 +347,7 @@ entries[ count ].Member.ToString(),
306347 packHelperMethodName ,
307348 new TypeDefinition [ ] { this . TargetType } ,
308349 typeof ( PackHelpers ) ,
350+ true , // isStatic
309351#if FEATURE_TAP
310352 isAsync ? typeof ( Task ) :
311353#endif // FEATURE_TAP
@@ -365,6 +407,22 @@ entries[ count ].Member.ToString(),
365407 ) ;
366408 }
367409
410+ private IEnumerable < TConstruct > EmitHasValueCore ( TContext context , TConstruct nullCheckTarget , Type itemType )
411+ {
412+ var nullable = this . DeclareLocal ( context , itemType , "nullable" ) ;
413+ yield return nullable ;
414+ yield return this . EmitStoreVariableStatement ( context , nullable , nullCheckTarget ) ;
415+ yield return
416+ this . EmitRetrunStatement (
417+ context ,
418+ this . EmitEqualsExpression (
419+ context ,
420+ this . EmitGetPropertyExpression ( context , nullable , itemType . GetProperty ( "HasValue" ) ) ,
421+ this . MakeBooleanLiteral ( context , false )
422+ )
423+ ) ;
424+ }
425+
368426 #endregion -- IPackable --
369427
370428 #region -- Pack Operation Initialization --
@@ -510,6 +568,64 @@ knownActions[ i ].Value
510568 ) ;
511569 }
512570
571+ protected internal TConstruct EmitPackNullCheckerTableInitialization ( TContext context , SerializationTarget targetInfo )
572+ {
573+ var listType = typeof ( Dictionary < , > ) . MakeGenericType ( typeof ( string ) , typeof ( Func < , > ) . MakeGenericType ( this . TargetType , typeof ( bool ) ) ) ;
574+ return
575+ this . EmitSequentialStatements (
576+ context ,
577+ listType ,
578+ this . EmitPackNullCheckerTableInitializationCore (
579+ context ,
580+ targetInfo ,
581+ this . DeclareLocal ( context , listType , "nullCheckerTable" )
582+ )
583+ ) ;
584+ }
585+
586+ private IEnumerable < TConstruct > EmitPackNullCheckerTableInitializationCore ( TContext context , SerializationTarget targetInfo , TConstruct actionCollection )
587+ {
588+ yield return actionCollection ;
589+
590+ var knownActions = GetDeclaredKnownActions ( context , targetInfo , m => GetCheckNullMethodName ( m ) ) ;
591+
592+ yield return
593+ this . EmitStoreVariableStatement (
594+ context ,
595+ actionCollection ,
596+ this . EmitCreateNewObjectExpression (
597+ context ,
598+ actionCollection ,
599+ new ConstructorDefinition ( actionCollection . ContextType , typeof ( int ) ) ,
600+ this . MakeInt32Literal ( context , knownActions . Length )
601+ )
602+ ) ;
603+
604+ for ( int i = 0 ; i < knownActions . Length ; i ++ )
605+ {
606+ yield return
607+ this . EmitSetIndexedProperty (
608+ context ,
609+ actionCollection ,
610+ actionCollection . ContextType ,
611+ "Item" ,
612+ // Set key as transformed.
613+ this . MakeStringLiteral ( context , context . SerializationContext . DictionarySerlaizationOptions . SafeKeyTransformer ( knownActions [ i ] . Key ) ) ,
614+ this . EmitNewPrivateMethodDelegateExpression (
615+ context ,
616+ knownActions [ i ] . Value
617+ )
618+ ) ;
619+ }
620+
621+ yield return
622+ this . EmitFinishFieldInitializationStatement (
623+ context ,
624+ FieldName . NullCheckersTable ,
625+ actionCollection
626+ ) ;
627+ }
628+
513629 private static string GetPackValueMethodName ( SerializingMember member , bool isAsync )
514630 {
515631 return
@@ -521,6 +637,11 @@ private static string GetPackValueMethodName( SerializingMember member, bool isA
521637 ) ;
522638 }
523639
640+ private static string GetCheckNullMethodName ( SerializingMember member )
641+ {
642+ return "Is" + member . MemberName + "Null" ;
643+ }
644+
524645 #endregion -- Pack Operation Initialization --
525646
526647 #region -- IUnpackable --
@@ -775,6 +896,7 @@ targetInfo.Members[ count ].Member.GetMemberValueType(),
775896 MethodNamePrefix . SetUnpackedValueOf + targetInfo . Members [ count ] . Member . Name ,
776897 null ,
777898 null ,
899+ false , // isStatic
778900 typeof ( void ) ,
779901 unpackingContext . VariableType ,
780902 targetInfo . Members [ count ] . Member . GetMemberValueType ( )
@@ -787,9 +909,10 @@ targetInfo.Members[ count ].Member.GetMemberValueType()
787909 ) ;
788910 }
789911
790- this . ExtractPrivateMethod (
912+ this . DefinePrivateMethod (
791913 context ,
792914 GetUnpackValueMethodName ( targetInfo . Members [ i ] , isAsync ) ,
915+ false , // isStatic
793916#if FEATURE_TAP
794917 isAsync ? typeof ( Task ) :
795918#endif // FEATURE_TAP
@@ -833,6 +956,7 @@ targetInfo.Members[ count ].Member.GetMemberValueType()
833956 AdjustName ( MethodNamePrefix . UnpackFrom + method , isAsync ) ,
834957 new [ ] { unpackingContext . Type , this . TargetType } ,
835958 typeof ( UnpackHelpers ) ,
959+ true , // isStatic
836960 this . TargetType ,
837961 unpackHelperArguments . Select ( a => a . ContextType ) . ToArray ( )
838962 ) ,
@@ -1099,6 +1223,7 @@ private MethodDefinition GetCreateObjectFromContextMethod( UnpackingContextInfo
10991223 MethodName . CreateObjectFromContext ,
11001224 null ,
11011225 null ,
1226+ true , // isStatic
11021227 this . TargetType ,
11031228 unpackingContext . Type
11041229 ) ;
@@ -1414,6 +1539,7 @@ private TConstruct EmitNewPrivateMethodDelegateExpressionWithCreation( TContext
14141539 this . ExtractPrivateMethod (
14151540 context ,
14161541 method . MethodName ,
1542+ false , // isStatic
14171543 method . ReturnType ,
14181544 bodyFactory ,
14191545 privateMethodParameters
@@ -1436,6 +1562,22 @@ private static KeyValuePair<string, MethodDefinition>[] GetKnownActions( TContex
14361562
14371563 }
14381564
1565+ private static KeyValuePair < string , MethodDefinition > [ ] GetDeclaredKnownActions ( TContext context , SerializationTarget targetInfo , Func < SerializingMember , string > nameFactory )
1566+ {
1567+ return
1568+ targetInfo . Members
1569+ . Where ( m => m . MemberName != null )
1570+ . Select ( m =>
1571+ new KeyValuePair < string , MethodDefinition > (
1572+ m . MemberName ,
1573+ context . TryGetDeclaredMethod ( nameFactory ( m ) )
1574+ )
1575+ ) . Where ( kv => kv . Value != null )
1576+ . ToArray ( ) ;
1577+
1578+ }
1579+
14391580 #endregion -- Operation Helpers --
14401581 }
14411582}
1583+
0 commit comments