Skip to content

Commit

Permalink
Add EnumSerializationMethod support to generator. Issue #102.
Browse files Browse the repository at this point in the history
* Add support for default EnumSerializationMethod value specification via configuration.
* Add constructor overloads taking EnumSerializationMethod explicitly to serializer code.
* Add constructor overloads using default EnumSerializationMethod implicitly to serializer assembly.
* Fix TracingILGenerator bugs when chained constructor is generated.
  • Loading branch information
yfakariya committed Aug 2, 2015
1 parent 9948eb1 commit 29c5166
Show file tree
Hide file tree
Showing 18 changed files with 497 additions and 152 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// MessagePack for CLI
//
// Copyright (C) 2014 FUJIWARA, Yusuke
// Copyright (C) 2014-2015 FUJIWARA, Yusuke
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -25,7 +25,7 @@ namespace MsgPack.Serialization.AbstractSerializers
{
partial class SerializerBuilder<TContext, TConstruct, TObject>
{
private void BuildEnumSerializer( TContext context )
protected void BuildEnumSerializer( TContext context )
{
Contract.Assert( typeof( TObject ).GetIsEnum() );
var underlyingType = Enum.GetUnderlyingType( typeof( TObject ) );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1030,7 +1030,15 @@ protected override void BuildSerializerCodeCore( ISerializerCodeGenerationContex

asCodeDomContext.Reset( typeof( TObject ), BaseClass );

this.BuildSerializer( asCodeDomContext, concreteType, itemSchema );
if ( !typeof( TObject ).GetIsEnum() )
{
this.BuildSerializer( asCodeDomContext, concreteType, itemSchema );
}
else
{
this.BuildEnumSerializer( asCodeDomContext );
}

this.Finish( asCodeDomContext, typeof( TObject ).GetIsEnum() );
}

Expand All @@ -1055,7 +1063,7 @@ protected override Func<SerializationContext, MessagePackSerializer<TObject>> Cr
var contextParameter = Expression.Parameter( typeof( SerializationContext ), "context" );
return
Expression.Lambda<Func<SerializationContext, MessagePackSerializer<TObject>>>(
Expression.New( targetType.GetConstructors().Single(), contextParameter ),
Expression.New( targetType.GetConstructors().Single( c => c.GetParameters().Length == 1 ), contextParameter ),
contextParameter
).Compile();
}
Expand Down Expand Up @@ -1210,6 +1218,39 @@ private void Finish( CodeDomContext context, bool isEnum )


// ctor
if ( isEnum )
{
var ctor1 =
new CodeConstructor
{
Attributes = MemberAttributes.Public
};
ctor1.Parameters.Add( new CodeParameterDeclarationExpression( typeof( SerializationContext ), "context" ) );
ctor1.ChainedConstructorArgs.Add( new CodeArgumentReferenceExpression( "context" ) );
ctor1.ChainedConstructorArgs.Add(
new CodeFieldReferenceExpression(
new CodeTypeReferenceExpression( typeof( EnumSerializationMethod ) ),
EnumMessagePackSerializerHelpers.DetermineEnumSerializationMethod(
context.SerializationContext,
typeof( TObject ),
EnumMemberSerializationMethod.Default
).ToString()
)
);
context.DeclaringType.Members.Add( ctor1 );

var ctor2 =
new CodeConstructor
{
Attributes = MemberAttributes.Public
};
ctor2.Parameters.Add( new CodeParameterDeclarationExpression( typeof( SerializationContext ), "context" ) );
ctor2.Parameters.Add( new CodeParameterDeclarationExpression( typeof( EnumSerializationMethod ), "enumSerializationMethod" ) );
ctor2.BaseConstructorArgs.Add( new CodeArgumentReferenceExpression( "context" ) );
ctor2.BaseConstructorArgs.Add( new CodeArgumentReferenceExpression( "enumSerializationMethod" ) );
context.DeclaringType.Members.Add( ctor2 );
}
else
{
var ctor =
new CodeConstructor
Expand All @@ -1221,20 +1262,7 @@ private void Finish( CodeDomContext context, bool isEnum )
var contextArgument = new CodeArgumentReferenceExpression( "context" );
ctor.BaseConstructorArgs.Add( contextArgument );

if ( isEnum )
{
ctor.BaseConstructorArgs.Add(
new CodeFieldReferenceExpression(
new CodeTypeReferenceExpression( typeof( EnumSerializationMethod ) ),
EnumMessagePackSerializerHelpers.DetermineEnumSerializationMethod(
context.SerializationContext,
typeof( TObject ),
EnumMemberSerializationMethod.Default
).ToString()
)
);
}
else if (
if (
BaseClass.GetConstructors( BindingFlags.NonPublic | BindingFlags.Instance )
.Any( c => c.GetParameters().Select( p => p.ParameterType ).SequenceEqual( CollectionSerializerHelpers.CollectionConstructorTypes ) )
)
Expand Down Expand Up @@ -1427,7 +1455,7 @@ private void Finish( CodeDomContext context, bool isEnum )
);
}
context.DeclaringType.Members.Add( ctor );
}
} // else of isEnum

// __Condition
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public AssemblyBuilderEmittingContext CreateEmittingContext( Type type, Type ser
this._context,
type,
() => this._generatorManager.CreateEmitter( type, serializerBaseClass, EmitterFlavor.FieldBased ),
() => this._generatorManager.CreateEnumEmitter( type, EmitterFlavor.FieldBased )
() => this._generatorManager.CreateEnumEmitter( this._context, type, EmitterFlavor.FieldBased )
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ protected override AssemblyBuilderEmittingContext CreateCodeGenerationContextFor
context,
typeof( TObject ),
() => SerializationMethodGeneratorManager.Get().CreateEmitter( typeof( TObject ), BaseClass, EmitterFlavor.FieldBased ),
() => SerializationMethodGeneratorManager.Get().CreateEnumEmitter( typeof( TObject ), EmitterFlavor.FieldBased )
() => SerializationMethodGeneratorManager.Get().CreateEnumEmitter( context, typeof( TObject ), EmitterFlavor.FieldBased )
);
}

Expand All @@ -146,9 +146,18 @@ protected override void BuildSerializerCodeCore( ISerializerCodeGenerationContex
BaseClass
);

this.BuildSerializer( emittingContext, concreteType, itemSchema );
// Finish type creation, and discard returned ctor.
emittingContext.Emitter.CreateConstructor<TObject>();
if ( !typeof( TObject ).GetIsEnum() )
{
this.BuildSerializer( emittingContext, concreteType, itemSchema );
// Finish type creation, and discard returned ctor.
emittingContext.Emitter.CreateConstructor<TObject>();
}
else
{
this.BuildEnumSerializer( emittingContext );
// Finish type creation, and discard returned ctor.
emittingContext.EnumEmitter.CreateConstructor<TObject>();
}
}
#endif
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,14 +232,14 @@ protected override SerializerEmitter CreateEmitterCore( Type targetType, Type ba
#endif
}

protected override EnumSerializerEmitter CreateEnumEmitterCore( Type targetType, EmitterFlavor emitterFlavor )
protected override EnumSerializerEmitter CreateEnumEmitterCore( SerializationContext context, Type targetType, EmitterFlavor emitterFlavor )
{
#if !WINDOWS_PHONE
switch ( emitterFlavor )
{
case EmitterFlavor.FieldBased:
{
return new FieldBasedEnumSerializerEmitter( this._module, this._isExternalAssemblyBuilder ? default( int? ) : Interlocked.Increment( ref this._typeSequence ), targetType, this._isDebuggable );
return new FieldBasedEnumSerializerEmitter( context, this._module, this._isExternalAssemblyBuilder ? default( int? ) : Interlocked.Increment( ref this._typeSequence ), targetType, this._isDebuggable );
}
default:
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ protected override DynamicMethodEmittingContext CreateCodeGenerationContextForSe
context,
typeof( TObject ),
() => SerializationMethodGeneratorManager.Get().CreateEmitter( typeof( TObject ), BaseClass, EmitterFlavor.ContextBased ),
() => SerializationMethodGeneratorManager.Get().CreateEnumEmitter( typeof( TObject ), EmitterFlavor.ContextBased )
() => SerializationMethodGeneratorManager.Get().CreateEnumEmitter( context, typeof( TObject ), EmitterFlavor.ContextBased )
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// MessagePack for CLI
//
// Copyright (C) 2014 FUJIWARA, Yusuke
// Copyright (C) 2014-2015 FUJIWARA, Yusuke
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -35,8 +35,18 @@ namespace MsgPack.Serialization.EmittingSerializers
/// </summary>
internal sealed class FieldBasedEnumSerializerEmitter : EnumSerializerEmitter
{
private readonly Type[] _constructorParameterTypes;
private static readonly Type[] ContextConstructorParameterTypes =
{
typeof( SerializationContext )
};
private static readonly Type[] ContextAndEnumSerializationMethodConstructorParameterTypes =
{
typeof( SerializationContext ),
typeof( EnumSerializationMethod )
};
private readonly ConstructorBuilder _contextConstructorBuilder;
private readonly ConstructorBuilder _contextAndEnumSerializationMethodConstructorBuilder;
private readonly EnumSerializationMethod _defaultEnumSerializationMethod;
private readonly TypeBuilder _typeBuilder;
private readonly MethodBuilder _packUnderlyingValueToMethodBuilder;
private readonly MethodBuilder _unpackFromUnderlyingValueMethodBuilder;
Expand All @@ -45,30 +55,24 @@ internal sealed class FieldBasedEnumSerializerEmitter : EnumSerializerEmitter
/// <summary>
/// Initializes a new instance of the <see cref="FieldBasedSerializerEmitter"/> class.
/// </summary>
/// <param name="context">A <see cref="SerializationContext"/>.</param>
/// <param name="host">The host <see cref="ModuleBuilder"/>.</param>
/// <param name="sequence">The sequence number to name new type.</param>
/// <param name="targetType">Type of the serialization target.</param>
/// <param name="isDebuggable">Set to <c>true</c> when <paramref name="host"/> is debuggable.</param>
public FieldBasedEnumSerializerEmitter( ModuleBuilder host, int? sequence, Type targetType, bool isDebuggable )
public FieldBasedEnumSerializerEmitter( SerializationContext context, ModuleBuilder host, int? sequence, Type targetType, bool isDebuggable )
{
Contract.Requires( host != null );
Contract.Requires( targetType != null );

this._constructorParameterTypes =
new[]
{
typeof( SerializationContext ),
typeof( EnumSerializationMethod )
};

string typeName =
#if !NETFX_35
String.Join(
String.Join(
Type.Delimiter.ToString( CultureInfo.InvariantCulture ),
typeof( SerializerEmitter ).Namespace,
"Generated",
IdentifierUtility.EscapeTypeName( targetType ) + "Serializer" + sequence
);
);
#else
String.Join(
Type.Delimiter.ToString(),
Expand All @@ -89,11 +93,19 @@ public FieldBasedEnumSerializerEmitter( ModuleBuilder host, int? sequence, Type
typeof( EnumMessagePackSerializer<> ).MakeGenericType( targetType )
);

this._contextConstructorBuilder = this._typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
this._constructorParameterTypes
);
this._contextConstructorBuilder =
this._typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
ContextConstructorParameterTypes
);
this._defaultEnumSerializationMethod = context.EnumSerializationMethod;
this._contextAndEnumSerializationMethodConstructorBuilder =
this._typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
ContextAndEnumSerializationMethodConstructorParameterTypes
);

this._packUnderlyingValueToMethodBuilder =
this._typeBuilder.DefineMethod(
Expand Down Expand Up @@ -165,30 +177,47 @@ public override Func<SerializationContext, EnumSerializationMethod, MessagePackS
{
if ( !this._typeBuilder.IsCreated() )
{

Contract.Assert( this._typeBuilder.BaseType != null );

/*
* .ctor( PackerCompatibilityOptions c, EnumSerializerMethod method )
* .ctor( SerializationContext c )
* : this( c, DEFAULT_METHOD )
* {
* }
*/
var il1 = new TracingILGenerator( this._contextConstructorBuilder, TextWriter.Null, this._isDebuggable );
// : this( c, DEFAULT_METHOD )
il1.EmitLdarg_0();
il1.EmitLdarg_1();
il1.EmitAnyLdc_I4( ( int ) this._defaultEnumSerializationMethod );

il1.EmitCallConstructor( this._contextAndEnumSerializationMethodConstructorBuilder );

il1.EmitRet();

/*
* .ctor( SerializationContext c, EnumSerializerMethod method )
* : base( c, method )
* {
* }
*/
var il = new TracingILGenerator( this._contextConstructorBuilder, TextWriter.Null, this._isDebuggable );
var il2 = new TracingILGenerator( this._contextAndEnumSerializationMethodConstructorBuilder, TextWriter.Null, this._isDebuggable );
// : base( c, method )
il.EmitLdarg_0();
il.EmitLdarg_1();
il.EmitLdarg_2();

Contract.Assert( this._typeBuilder.BaseType != null );
il2.EmitLdarg_0();
il2.EmitLdarg_1();
il2.EmitLdarg_2();

il.EmitCallConstructor(
il2.EmitCallConstructor(
this._typeBuilder.BaseType.GetConstructor(
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, this._constructorParameterTypes, null
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, ContextAndEnumSerializationMethodConstructorParameterTypes, null
)
);

il.EmitRet();
il2.EmitRet();
}

var ctor = this._typeBuilder.CreateType().GetConstructor( this._constructorParameterTypes );
var ctor = this._typeBuilder.CreateType().GetConstructor( ContextAndEnumSerializationMethodConstructorParameterTypes );
var contextParameter = Expression.Parameter( typeof( SerializationContext ), "context" );
var methodParameter = Expression.Parameter( typeof( EnumSerializationMethod ), "method" );
#if DEBUG
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,23 +123,26 @@ public SerializerEmitter CreateEmitter( Type targetType, Type baseClass, Emitter
/// <summary>
/// Creates new <see cref="EnumSerializerEmitter"/> which corresponds to the specified <see cref="EmitterFlavor"/>.
/// </summary>
/// <param name="context">The <see cref="SerializationContext"/>.</param>
/// <param name="targetType">The type of the serialization target.</param>
/// <param name="emitterFlavor"><see cref="EmitterFlavor"/>.</param>
/// <returns>New <see cref="EnumSerializerEmitter"/> which corresponds to the specified <see cref="EmitterFlavor"/>.</returns>
public EnumSerializerEmitter CreateEnumEmitter( Type targetType, EmitterFlavor emitterFlavor )
public EnumSerializerEmitter CreateEnumEmitter( SerializationContext context, Type targetType, EmitterFlavor emitterFlavor )
{
Contract.Requires( context != null );
Contract.Requires( targetType != null );
Contract.Ensures( Contract.Result<EnumSerializerEmitter>() != null );

return this.CreateEnumEmitterCore( targetType, emitterFlavor );
return this.CreateEnumEmitterCore( context, targetType, emitterFlavor );
}

/// <summary>
/// Creates new <see cref="EnumSerializerEmitter"/> which corresponds to the specified <see cref="EmitterFlavor"/>.
/// </summary>
/// <param name="context">The <see cref="SerializationContext"/>.</param>
/// <param name="targetType">The type of the serialization target.</param>
/// <param name="emitterFlavor"><see cref="EmitterFlavor"/>.</param>
/// <returns>New <see cref="SerializerEmitter"/> which corresponds to the specified <see cref="EmitterFlavor"/>.</returns>
protected abstract EnumSerializerEmitter CreateEnumEmitterCore( Type targetType, EmitterFlavor emitterFlavor );
protected abstract EnumSerializerEmitter CreateEnumEmitterCore( SerializationContext context, Type targetType, EmitterFlavor emitterFlavor );
}
}
11 changes: 10 additions & 1 deletion src/MsgPack/Serialization/ISerializerGeneratorConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// MessagePack for CLI
//
// Copyright (C) 2010-2013 FUJIWARA, Yusuke
// Copyright (C) 2010-2015 FUJIWARA, Yusuke
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -46,6 +46,15 @@ internal interface ISerializerGeneratorConfiguration
/// <exception cref="ArgumentOutOfRangeException">Specified value is not valid <see cref="SerializationMethod"/>.</exception>
SerializationMethod SerializationMethod { get; set; }

/// <summary>
/// Gets or sets the default enum serialization method for generating enum type serializers.
/// </summary>
/// <value>
/// A value of <see cref="EnumSerializationMethod"/>.
/// </value>
/// <exception cref="ArgumentOutOfRangeException">Specified value is not valid <see cref="EnumSerializationMethod"/>.</exception>
EnumSerializationMethod EnumSerializationMethod { get; set; }

/// <summary>
/// Validates this instance state.
/// </summary>
Expand Down
Loading

0 comments on commit 29c5166

Please sign in to comment.