You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
119 lines
4.4 KiB
119 lines
4.4 KiB
#if !UNITY_METRO |
|
|
|
using System; |
|
using MessagePack.Formatters; |
|
using MessagePack.Internal; |
|
using System.Reflection; |
|
using System.Reflection.Emit; |
|
|
|
namespace MessagePack.Resolvers |
|
{ |
|
/// <summary> |
|
/// EnumResolver by dynamic code generation, serialized underlying type. |
|
/// </summary> |
|
public sealed class DynamicEnumResolver : IFormatterResolver |
|
{ |
|
public static readonly DynamicEnumResolver Instance = new DynamicEnumResolver(); |
|
|
|
const string ModuleName = "MessagePack.Resolvers.DynamicEnumResolver"; |
|
|
|
static readonly DynamicAssembly assembly; |
|
|
|
DynamicEnumResolver() |
|
{ |
|
|
|
} |
|
|
|
static DynamicEnumResolver() |
|
{ |
|
assembly = new DynamicAssembly(ModuleName); |
|
} |
|
|
|
|
|
#if NET_35 |
|
public AssemblyBuilder Save() |
|
{ |
|
return assembly.Save(); |
|
} |
|
#endif |
|
|
|
public IMessagePackFormatter<T> GetFormatter<T>() |
|
{ |
|
return FormatterCache<T>.formatter; |
|
} |
|
|
|
static class FormatterCache<T> |
|
{ |
|
public static readonly IMessagePackFormatter<T> formatter; |
|
|
|
static FormatterCache() |
|
{ |
|
var ti = typeof(T).GetTypeInfo(); |
|
if (ti.IsNullable()) |
|
{ |
|
// build underlying type and use wrapped formatter. |
|
ti = ti.GenericTypeArguments[0].GetTypeInfo(); |
|
if (!ti.IsEnum) |
|
{ |
|
return; |
|
} |
|
|
|
var innerFormatter = DynamicEnumResolver.Instance.GetFormatterDynamic(ti.AsType()); |
|
if (innerFormatter == null) |
|
{ |
|
return; |
|
} |
|
formatter = (IMessagePackFormatter<T>)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(ti.AsType()), new object[] { innerFormatter }); |
|
return; |
|
} |
|
else if (!ti.IsEnum) |
|
{ |
|
return; |
|
} |
|
|
|
var formatterTypeInfo = BuildType(typeof(T)); |
|
formatter = (IMessagePackFormatter<T>)Activator.CreateInstance(formatterTypeInfo.AsType()); |
|
} |
|
} |
|
|
|
static TypeInfo BuildType(Type enumType) |
|
{ |
|
var underlyingType = Enum.GetUnderlyingType(enumType); |
|
var formatterType = typeof(IMessagePackFormatter<>).MakeGenericType(enumType); |
|
|
|
var typeBuilder = assembly.ModuleBuilder.DefineType("MessagePack.Formatters." + enumType.FullName.Replace(".", "_") + "Formatter", TypeAttributes.Public | TypeAttributes.Sealed, null, new[] { formatterType }); |
|
|
|
// int Serialize(ref byte[] bytes, int offset, T value, IFormatterResolver formatterResolver); |
|
{ |
|
var method = typeBuilder.DefineMethod("Serialize", MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual, |
|
typeof(int), |
|
new Type[] { typeof(byte[]).MakeByRefType(), typeof(int), enumType, typeof(IFormatterResolver) }); |
|
|
|
var il = method.GetILGenerator(); |
|
il.Emit(OpCodes.Ldarg_1); |
|
il.Emit(OpCodes.Ldarg_2); |
|
il.Emit(OpCodes.Ldarg_3); |
|
il.Emit(OpCodes.Call, typeof(MessagePackBinary).GetRuntimeMethod("Write" + underlyingType.Name, new[] { typeof(byte[]).MakeByRefType(), typeof(int), underlyingType })); |
|
il.Emit(OpCodes.Ret); |
|
} |
|
|
|
// T Deserialize(byte[] bytes, int offset, IFormatterResolver formatterResolver, out int readSize); |
|
{ |
|
var method = typeBuilder.DefineMethod("Deserialize", MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual, |
|
enumType, |
|
new Type[] { typeof(byte[]), typeof(int), typeof(IFormatterResolver), typeof(int).MakeByRefType() }); |
|
|
|
var il = method.GetILGenerator(); |
|
il.Emit(OpCodes.Ldarg_1); |
|
il.Emit(OpCodes.Ldarg_2); |
|
il.Emit(OpCodes.Ldarg_S, (byte)4); |
|
il.Emit(OpCodes.Call, typeof(MessagePackBinary).GetRuntimeMethod("Read" + underlyingType.Name, new[] { typeof(byte[]), typeof(int), typeof(int).MakeByRefType() })); |
|
il.Emit(OpCodes.Ret); |
|
} |
|
|
|
return typeBuilder.CreateTypeInfo(); |
|
} |
|
} |
|
} |
|
|
|
#endif |