// Copyright (c) All contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !(UNITY_2018_3_OR_NEWER && NET_STANDARD_2_0) using System; using System.Buffers; using System.Reflection; using System.Reflection.Emit; using System.Threading; using MessagePack.Formatters; using MessagePack.Internal; namespace MessagePack.Resolvers { /// /// EnumResolver by dynamic code generation, serialized underlying type. /// public sealed class DynamicEnumResolver : IFormatterResolver { /// /// The singleton instance that can be used. /// public static readonly DynamicEnumResolver Instance = new DynamicEnumResolver(); private const string ModuleName = "MessagePack.Resolvers.DynamicEnumResolver"; private static readonly Lazy DynamicAssembly; private static int nameSequence = 0; private DynamicEnumResolver() { } static DynamicEnumResolver() { DynamicAssembly = new Lazy(() => new DynamicAssembly(ModuleName)); } #if NETFRAMEWORK public AssemblyBuilder Save() { return DynamicAssembly.Value.Save(); } #endif public IMessagePackFormatter GetFormatter() { return FormatterCache.Formatter; } private static class FormatterCache { public static readonly IMessagePackFormatter Formatter; static FormatterCache() { TypeInfo 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)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(ti.AsType()), new object[] { innerFormatter }); return; } else if (!ti.IsEnum) { return; } TypeInfo formatterTypeInfo = BuildType(typeof(T)); Formatter = (IMessagePackFormatter)Activator.CreateInstance(formatterTypeInfo.AsType()); } } private static TypeInfo BuildType(Type enumType) { Type underlyingType = Enum.GetUnderlyingType(enumType); Type formatterType = typeof(IMessagePackFormatter<>).MakeGenericType(enumType); using (MonoProtection.EnterRefEmitLock()) { TypeBuilder typeBuilder = DynamicAssembly.Value.DefineType("MessagePack.Formatters." + enumType.FullName.Replace(".", "_") + "Formatter" + Interlocked.Increment(ref nameSequence), TypeAttributes.Public | TypeAttributes.Sealed, null, new[] { formatterType }); // void Serialize(ref MessagePackWriter writer, T value, MessagePackSerializerOptions options); { MethodBuilder method = typeBuilder.DefineMethod( "Serialize", MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual, null, new Type[] { typeof(MessagePackWriter).MakeByRefType(), enumType, typeof(MessagePackSerializerOptions) }); ILGenerator il = method.GetILGenerator(); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Call, typeof(MessagePackWriter).GetRuntimeMethod(nameof(MessagePackWriter.Write), new[] { underlyingType })); il.Emit(OpCodes.Ret); } // T Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options); { MethodBuilder method = typeBuilder.DefineMethod( "Deserialize", MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual, enumType, new Type[] { typeof(MessagePackReader).MakeByRefType(), typeof(MessagePackSerializerOptions) }); ILGenerator il = method.GetILGenerator(); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Call, typeof(MessagePackReader).GetRuntimeMethod("Read" + underlyingType.Name, Type.EmptyTypes)); il.Emit(OpCodes.Ret); } return typeBuilder.CreateTypeInfo(); } } } } #endif