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.
124 lines
4.5 KiB
124 lines
4.5 KiB
4 years ago
|
#if !UNITY_WSA
|
||
|
#if !NET_STANDARD_2_0
|
||
|
|
||
|
using System;
|
||
|
using MessagePack.Formatters;
|
||
|
using MessagePack.Internal;
|
||
|
using System.Reflection;
|
||
|
using System.Reflection.Emit;
|
||
|
using System.Threading;
|
||
|
|
||
|
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;
|
||
|
|
||
|
static int nameSequence = 0;
|
||
|
|
||
|
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.DefineType("MessagePack.Formatters." + enumType.FullName.Replace(".", "_") + "Formatter" + Interlocked.Increment(ref nameSequence), 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
|
||
|
#endif
|