// 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.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
using System.Threading;
using MessagePack.Formatters;
using MessagePack.Internal;
using MessagePack.Resolvers;
#pragma warning disable SA1403 // File may only contain a single namespace
namespace MessagePack.Resolvers
{
///
/// ObjectResolver by dynamic code generation.
///
public sealed class DynamicObjectResolver : IFormatterResolver
{
private const string ModuleName = "MessagePack.Resolvers.DynamicObjectResolver";
///
/// The singleton instance that can be used.
///
public static readonly DynamicObjectResolver Instance;
///
/// A instance with this formatter pre-configured.
///
public static readonly MessagePackSerializerOptions Options;
internal static readonly Lazy DynamicAssembly;
static DynamicObjectResolver()
{
Instance = new DynamicObjectResolver();
Options = new MessagePackSerializerOptions(Instance);
DynamicAssembly = new Lazy(() => new DynamicAssembly(ModuleName));
}
private DynamicObjectResolver()
{
}
#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.IsInterface || ti.IsAbstract)
{
return;
}
if (ti.IsNullable())
{
ti = ti.GenericTypeArguments[0].GetTypeInfo();
var innerFormatter = DynamicObjectResolver.Instance.GetFormatterDynamic(ti.AsType());
if (innerFormatter == null)
{
return;
}
Formatter = (IMessagePackFormatter)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(ti.AsType()), new object[] { innerFormatter });
return;
}
if (ti.IsAnonymous())
{
Formatter = (IMessagePackFormatter)DynamicObjectTypeBuilder.BuildFormatterToDynamicMethod(typeof(T), true, true, false);
return;
}
TypeInfo formatterTypeInfo;
try
{
formatterTypeInfo = DynamicObjectTypeBuilder.BuildType(DynamicAssembly.Value, typeof(T), false, false);
}
catch (InitAccessorInGenericClassNotSupportedException)
{
Formatter = (IMessagePackFormatter)DynamicObjectTypeBuilder.BuildFormatterToDynamicMethod(typeof(T), false, false, false);
return;
}
if (formatterTypeInfo == null)
{
return;
}
Formatter = (IMessagePackFormatter)ResolverUtilities.ActivateFormatter(formatterTypeInfo.AsType());
}
}
}
///
/// ObjectResolver by dynamic code generation, allow private member.
///
public sealed class DynamicObjectResolverAllowPrivate : IFormatterResolver
{
public static readonly DynamicObjectResolverAllowPrivate Instance = new DynamicObjectResolverAllowPrivate();
private DynamicObjectResolverAllowPrivate()
{
}
public IMessagePackFormatter GetFormatter()
{
return FormatterCache.Formatter;
}
private static class FormatterCache
{
internal static readonly IMessagePackFormatter Formatter;
static FormatterCache()
{
TypeInfo ti = typeof(T).GetTypeInfo();
if (ti.IsInterface || ti.IsAbstract)
{
return;
}
if (ti.IsNullable())
{
ti = ti.GenericTypeArguments[0].GetTypeInfo();
var innerFormatter = DynamicObjectResolverAllowPrivate.Instance.GetFormatterDynamic(ti.AsType());
if (innerFormatter == null)
{
return;
}
Formatter = (IMessagePackFormatter)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(ti.AsType()), new object[] { innerFormatter });
return;
}
if (ti.IsAnonymous())
{
Formatter = (IMessagePackFormatter)DynamicObjectTypeBuilder.BuildFormatterToDynamicMethod(typeof(T), true, true, false);
}
else
{
Formatter = (IMessagePackFormatter)DynamicObjectTypeBuilder.BuildFormatterToDynamicMethod(typeof(T), false, false, true);
}
}
}
}
///
/// ObjectResolver by dynamic code generation, no needs MessagePackObject attribute and serialized key as string.
///
public sealed class DynamicContractlessObjectResolver : IFormatterResolver
{
public static readonly DynamicContractlessObjectResolver Instance = new DynamicContractlessObjectResolver();
private const string ModuleName = "MessagePack.Resolvers.DynamicContractlessObjectResolver";
private static readonly Lazy DynamicAssembly;
private DynamicContractlessObjectResolver()
{
}
static DynamicContractlessObjectResolver()
{
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()
{
if (typeof(T) == typeof(object))
{
return;
}
TypeInfo ti = typeof(T).GetTypeInfo();
if (ti.IsInterface || ti.IsAbstract)
{
return;
}
if (ti.IsNullable())
{
ti = ti.GenericTypeArguments[0].GetTypeInfo();
var innerFormatter = DynamicContractlessObjectResolver.Instance.GetFormatterDynamic(ti.AsType());
if (innerFormatter == null)
{
return;
}
Formatter = (IMessagePackFormatter)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(ti.AsType()), new object[] { innerFormatter });
return;
}
if (ti.IsAnonymous() || ti.HasPrivateCtorForSerialization())
{
Formatter = (IMessagePackFormatter)DynamicObjectTypeBuilder.BuildFormatterToDynamicMethod(typeof(T), true, true, false);
return;
}
TypeInfo formatterTypeInfo = DynamicObjectTypeBuilder.BuildType(DynamicAssembly.Value, typeof(T), true, true);
if (formatterTypeInfo == null)
{
return;
}
Formatter = (IMessagePackFormatter)Activator.CreateInstance(formatterTypeInfo.AsType());
}
}
}
///
/// ObjectResolver by dynamic code generation, no needs MessagePackObject attribute and serialized key as string, allow private member.
///
public sealed class DynamicContractlessObjectResolverAllowPrivate : IFormatterResolver
{
public static readonly DynamicContractlessObjectResolverAllowPrivate Instance = new DynamicContractlessObjectResolverAllowPrivate();
public IMessagePackFormatter GetFormatter()
{
return FormatterCache.Formatter;
}
private static class FormatterCache
{
internal static readonly IMessagePackFormatter Formatter;
static FormatterCache()
{
if (typeof(T) == typeof(object))
{
return;
}
TypeInfo ti = typeof(T).GetTypeInfo();
if (ti.IsInterface || ti.IsAbstract)
{
return;
}
if (ti.IsNullable())
{
ti = ti.GenericTypeArguments[0].GetTypeInfo();
var innerFormatter = DynamicContractlessObjectResolverAllowPrivate.Instance.GetFormatterDynamic(ti.AsType());
if (innerFormatter == null)
{
return;
}
Formatter = (IMessagePackFormatter)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(ti.AsType()), new object[] { innerFormatter });
return;
}
if (ti.IsAnonymous())
{
Formatter = (IMessagePackFormatter)DynamicObjectTypeBuilder.BuildFormatterToDynamicMethod(typeof(T), true, true, false);
}
else
{
Formatter = (IMessagePackFormatter)DynamicObjectTypeBuilder.BuildFormatterToDynamicMethod(typeof(T), true, true, true);
}
}
}
}
}
namespace MessagePack.Internal
{
internal static class DynamicObjectTypeBuilder
{
#if !UNITY_2018_3_OR_NEWER
private static readonly Regex SubtractFullNameRegex = new Regex(@", Version=\d+.\d+.\d+.\d+, Culture=\w+, PublicKeyToken=\w+", RegexOptions.Compiled);
#else
static readonly Regex SubtractFullNameRegex = new Regex(@", Version=\d+.\d+.\d+.\d+, Culture=\w+, PublicKeyToken=\w+");
#endif
private static int nameSequence = 0;
private static HashSet ignoreTypes = new HashSet
{
{ typeof(object) },
{ typeof(short) },
{ typeof(int) },
{ typeof(long) },
{ typeof(ushort) },
{ typeof(uint) },
{ typeof(ulong) },
{ typeof(float) },
{ typeof(double) },
{ typeof(bool) },
{ typeof(byte) },
{ typeof(sbyte) },
{ typeof(decimal) },
{ typeof(char) },
{ typeof(string) },
{ typeof(System.Guid) },
{ typeof(System.TimeSpan) },
{ typeof(System.DateTime) },
{ typeof(System.DateTimeOffset) },
{ typeof(MessagePack.Nil) },
};
public static TypeInfo BuildType(DynamicAssembly assembly, Type type, bool forceStringKey, bool contractless)
{
if (ignoreTypes.Contains(type))
{
return null;
}
var serializationInfo = MessagePack.Internal.ObjectSerializationInfo.CreateOrNull(type, forceStringKey, contractless, false, dynamicMethod: false);
if (serializationInfo == null)
{
return null;
}
if (!(type.IsPublic || type.IsNestedPublic))
{
throw new MessagePackSerializationException("Building dynamic formatter only allows public type. Type: " + type.FullName);
}
using (MonoProtection.EnterRefEmitLock())
{
Type formatterType = typeof(IMessagePackFormatter<>).MakeGenericType(type);
TypeBuilder typeBuilder = assembly.DefineType("MessagePack.Formatters." + SubtractFullNameRegex.Replace(type.FullName, string.Empty).Replace(".", "_") + "Formatter" + Interlocked.Increment(ref nameSequence), TypeAttributes.Public | TypeAttributes.Sealed, null, new[] { formatterType });
FieldBuilder stringByteKeysField = null;
Dictionary customFormatterLookup = null;
// string key needs string->int mapper for deserialize switch statement
if (serializationInfo.IsStringKey)
{
ConstructorBuilder method = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes);
stringByteKeysField = typeBuilder.DefineField("stringByteKeys", typeof(byte[][]), FieldAttributes.Private | FieldAttributes.InitOnly);
ILGenerator il = method.GetILGenerator();
BuildConstructor(type, serializationInfo, method, stringByteKeysField, il);
customFormatterLookup = BuildCustomFormatterField(typeBuilder, serializationInfo, il);
il.Emit(OpCodes.Ret);
}
else
{
ConstructorBuilder method = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes);
ILGenerator il = method.GetILGenerator();
il.EmitLoadThis();
il.Emit(OpCodes.Call, objectCtor);
customFormatterLookup = BuildCustomFormatterField(typeBuilder, serializationInfo, il);
il.Emit(OpCodes.Ret);
}
{
MethodBuilder method = typeBuilder.DefineMethod(
"Serialize",
MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual,
returnType: null,
parameterTypes: new Type[] { typeof(MessagePackWriter).MakeByRefType(), type, typeof(MessagePackSerializerOptions) });
method.DefineParameter(1, ParameterAttributes.None, "writer");
method.DefineParameter(2, ParameterAttributes.None, "value");
method.DefineParameter(3, ParameterAttributes.None, "options");
ILGenerator il = method.GetILGenerator();
BuildSerialize(
type,
serializationInfo,
il,
() =>
{
il.EmitLoadThis();
il.EmitLdfld(stringByteKeysField);
},
(index, member) =>
{
FieldInfo fi;
if (!customFormatterLookup.TryGetValue(member, out fi))
{
return null;
}
return () =>
{
il.EmitLoadThis();
il.EmitLdfld(fi);
};
},
1);
}
{
MethodBuilder method = typeBuilder.DefineMethod(
"Deserialize",
MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual,
type,
new Type[] { refMessagePackReader, typeof(MessagePackSerializerOptions) });
method.DefineParameter(1, ParameterAttributes.None, "reader");
method.DefineParameter(2, ParameterAttributes.None, "options");
ILGenerator il = method.GetILGenerator();
BuildDeserialize(
type,
serializationInfo,
il,
(index, member) =>
{
FieldInfo fi;
if (!customFormatterLookup.TryGetValue(member, out fi))
{
return null;
}
return () =>
{
il.EmitLoadThis();
il.EmitLdfld(fi);
};
},
1); // firstArgIndex:0 is this.
}
return typeBuilder.CreateTypeInfo();
}
}
public static object BuildFormatterToDynamicMethod(Type type, bool forceStringKey, bool contractless, bool allowPrivate)
{
var serializationInfo = ObjectSerializationInfo.CreateOrNull(type, forceStringKey, contractless, allowPrivate, dynamicMethod: true);
if (serializationInfo == null)
{
return null;
}
// internal delegate void AnonymousSerializeFunc(byte[][] stringByteKeysField, object[] customFormatters, ref MessagePackWriter writer, T value, MessagePackSerializerOptions options);
// internal delegate T AnonymousDeserializeFunc(object[] customFormatters, ref MessagePackReader reader, MessagePackSerializerOptions options);
var serialize = new DynamicMethod("Serialize", null, new[] { typeof(byte[][]), typeof(object[]), typeof(MessagePackWriter).MakeByRefType(), type, typeof(MessagePackSerializerOptions) }, type, true);
DynamicMethod deserialize = null;
List stringByteKeysField = new List();
List