// Copyright (c) All contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Buffers; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Threading; using System.Threading.Tasks; namespace MessagePack { public partial class MessagePackSerializer { private static readonly Func CreateCompiledMethods; private static readonly MessagePack.Internal.ThreadsafeTypeKeyHashTable Serializes = new MessagePack.Internal.ThreadsafeTypeKeyHashTable(capacity: 64); static MessagePackSerializer() { CreateCompiledMethods = t => new CompiledMethods(t); } /// public static void Serialize(Type type, ref MessagePackWriter writer, object obj, MessagePackSerializerOptions options = null) { GetOrAdd(type).Serialize_MessagePackWriter_T_Options.Invoke(ref writer, obj, options); } /// public static void Serialize(Type type, IBufferWriter writer, object obj, MessagePackSerializerOptions options = null, CancellationToken cancellationToken = default) { GetOrAdd(type).Serialize_IBufferWriter_T_Options_CancellationToken.Invoke(writer, obj, options, cancellationToken); } /// public static byte[] Serialize(Type type, object obj, MessagePackSerializerOptions options = null, CancellationToken cancellationToken = default) { return GetOrAdd(type).Serialize_T_Options.Invoke(obj, options, cancellationToken); } /// public static void Serialize(Type type, Stream stream, object obj, MessagePackSerializerOptions options = null, CancellationToken cancellationToken = default) { GetOrAdd(type).Serialize_Stream_T_Options_CancellationToken.Invoke(stream, obj, options, cancellationToken); } /// public static Task SerializeAsync(Type type, Stream stream, object obj, MessagePackSerializerOptions options = null, CancellationToken cancellationToken = default) { return GetOrAdd(type).SerializeAsync_Stream_T_Options_CancellationToken.Invoke(stream, obj, options, cancellationToken); } /// public static object Deserialize(Type type, ref MessagePackReader reader, MessagePackSerializerOptions options = null) { return GetOrAdd(type).Deserialize_MessagePackReader_Options.Invoke(ref reader, options); } /// public static object Deserialize(Type type, Stream stream, MessagePackSerializerOptions options = null, CancellationToken cancellationToken = default) { return GetOrAdd(type).Deserialize_Stream_Options_CancellationToken.Invoke(stream, options, cancellationToken); } /// public static ValueTask DeserializeAsync(Type type, Stream stream, MessagePackSerializerOptions options = null, CancellationToken cancellationToken = default) { return GetOrAdd(type).DeserializeAsync_Stream_Options_CancellationToken.Invoke(stream, options, cancellationToken); } /// public static object Deserialize(Type type, ReadOnlyMemory bytes, MessagePackSerializerOptions options = null, CancellationToken cancellationToken = default) { return GetOrAdd(type).Deserialize_ReadOnlyMemory_Options.Invoke(bytes, options, cancellationToken); } /// public static object Deserialize(Type type, ReadOnlySequence bytes, MessagePackSerializerOptions options = null, CancellationToken cancellationToken = default) { return GetOrAdd(type).Deserialize_ReadOnlySequence_Options_CancellationToken.Invoke(bytes, options, cancellationToken); } private static async ValueTask DeserializeObjectAsync(Stream stream, MessagePackSerializerOptions options, CancellationToken cancellationToken) => await DeserializeAsync(stream, options, cancellationToken).ConfigureAwait(false); private static CompiledMethods GetOrAdd(Type type) { return Serializes.GetOrAdd(type, CreateCompiledMethods); } private class CompiledMethods { internal delegate void MessagePackWriterSerialize(ref MessagePackWriter writer, object value, MessagePackSerializerOptions options); internal delegate object MessagePackReaderDeserialize(ref MessagePackReader reader, MessagePackSerializerOptions options); private const bool PreferInterpretation = #if ENABLE_IL2CPP true; #else false; #endif #pragma warning disable SA1310 // Field names should not contain underscore #pragma warning disable SA1307 // Accessible fields should begin with upper-case letter #pragma warning disable SA1401 // Fields should be private internal readonly Func Serialize_T_Options; internal readonly Action Serialize_Stream_T_Options_CancellationToken; internal readonly Func SerializeAsync_Stream_T_Options_CancellationToken; internal readonly MessagePackWriterSerialize Serialize_MessagePackWriter_T_Options; internal readonly Action, object, MessagePackSerializerOptions, CancellationToken> Serialize_IBufferWriter_T_Options_CancellationToken; internal readonly MessagePackReaderDeserialize Deserialize_MessagePackReader_Options; internal readonly Func Deserialize_Stream_Options_CancellationToken; internal readonly Func> DeserializeAsync_Stream_Options_CancellationToken; internal readonly Func, MessagePackSerializerOptions, CancellationToken, object> Deserialize_ReadOnlyMemory_Options; internal readonly Func, MessagePackSerializerOptions, CancellationToken, object> Deserialize_ReadOnlySequence_Options_CancellationToken; #pragma warning restore SA1401 // Fields should be private #pragma warning restore SA1307 // Accessible fields should begin with upper-case letter #pragma warning restore SA1310 // Field names should not contain underscore internal CompiledMethods(Type type) { TypeInfo ti = type.GetTypeInfo(); { // public static byte[] Serialize(T obj, MessagePackSerializerOptions options, CancellationToken cancellationToken) MethodInfo serialize = GetMethod(nameof(Serialize), type, new Type[] { null, typeof(MessagePackSerializerOptions), typeof(CancellationToken) }); #if ENABLE_IL2CPP this.Serialize_T_Options = (x, y, z) => (byte[])serialize.Invoke(null, new object[] { x, y, z }); #else ParameterExpression param1 = Expression.Parameter(typeof(object), "obj"); ParameterExpression param2 = Expression.Parameter(typeof(MessagePackSerializerOptions), "options"); ParameterExpression param3 = Expression.Parameter(typeof(CancellationToken), "cancellationToken"); MethodCallExpression body = Expression.Call( null, serialize, ti.IsValueType ? Expression.Unbox(param1, type) : Expression.Convert(param1, type), param2, param3); Func lambda = Expression.Lambda>(body, param1, param2, param3).Compile(PreferInterpretation); this.Serialize_T_Options = lambda; #endif } { // public static void Serialize(Stream stream, T obj, MessagePackSerializerOptions options, CancellationToken cancellationToken) MethodInfo serialize = GetMethod(nameof(Serialize), type, new Type[] { typeof(Stream), null, typeof(MessagePackSerializerOptions), typeof(CancellationToken) }); #if ENABLE_IL2CPP this.Serialize_Stream_T_Options_CancellationToken = (x, y, z, a) => serialize.Invoke(null, new object[] { x, y, z, a }); #else ParameterExpression param1 = Expression.Parameter(typeof(Stream), "stream"); ParameterExpression param2 = Expression.Parameter(typeof(object), "obj"); ParameterExpression param3 = Expression.Parameter(typeof(MessagePackSerializerOptions), "options"); ParameterExpression param4 = Expression.Parameter(typeof(CancellationToken), "cancellationToken"); MethodCallExpression body = Expression.Call( null, serialize, param1, ti.IsValueType ? Expression.Unbox(param2, type) : Expression.Convert(param2, type), param3, param4); Action lambda = Expression.Lambda>(body, param1, param2, param3, param4).Compile(PreferInterpretation); this.Serialize_Stream_T_Options_CancellationToken = lambda; #endif } { // public static Task SerializeAsync(Stream stream, T obj, MessagePackSerializerOptions options, CancellationToken cancellationToken) MethodInfo serialize = GetMethod(nameof(SerializeAsync), type, new Type[] { typeof(Stream), null, typeof(MessagePackSerializerOptions), typeof(CancellationToken) }); #if ENABLE_IL2CPP this.SerializeAsync_Stream_T_Options_CancellationToken = (x, y, z, a) => (Task)serialize.Invoke(null, new object[] { x, y, z, a }); #else ParameterExpression param1 = Expression.Parameter(typeof(Stream), "stream"); ParameterExpression param2 = Expression.Parameter(typeof(object), "obj"); ParameterExpression param3 = Expression.Parameter(typeof(MessagePackSerializerOptions), "options"); ParameterExpression param4 = Expression.Parameter(typeof(CancellationToken), "cancellationToken"); MethodCallExpression body = Expression.Call( null, serialize, param1, ti.IsValueType ? Expression.Unbox(param2, type) : Expression.Convert(param2, type), param3, param4); Func lambda = Expression.Lambda>(body, param1, param2, param3, param4).Compile(PreferInterpretation); this.SerializeAsync_Stream_T_Options_CancellationToken = lambda; #endif } { // public static Task Serialize(IBufferWriter writer, T obj, MessagePackSerializerOptions options, CancellationToken cancellationToken) MethodInfo serialize = GetMethod(nameof(Serialize), type, new Type[] { typeof(IBufferWriter), null, typeof(MessagePackSerializerOptions), typeof(CancellationToken) }); #if ENABLE_IL2CPP this.Serialize_IBufferWriter_T_Options_CancellationToken = (x, y, z, a) => serialize.Invoke(null, new object[] { x, y, z, a }); #else ParameterExpression param1 = Expression.Parameter(typeof(IBufferWriter), "writer"); ParameterExpression param2 = Expression.Parameter(typeof(object), "obj"); ParameterExpression param3 = Expression.Parameter(typeof(MessagePackSerializerOptions), "options"); ParameterExpression param4 = Expression.Parameter(typeof(CancellationToken), "cancellationToken"); MethodCallExpression body = Expression.Call( null, serialize, param1, ti.IsValueType ? Expression.Unbox(param2, type) : Expression.Convert(param2, type), param3, param4); Action, object, MessagePackSerializerOptions, CancellationToken> lambda = Expression.Lambda, object, MessagePackSerializerOptions, CancellationToken>>(body, param1, param2, param3, param4).Compile(PreferInterpretation); this.Serialize_IBufferWriter_T_Options_CancellationToken = lambda; #endif } { // public static void Serialize(ref MessagePackWriter writer, T obj, MessagePackSerializerOptions options) MethodInfo serialize = GetMethod(nameof(Serialize), type, new Type[] { typeof(MessagePackWriter).MakeByRefType(), null, typeof(MessagePackSerializerOptions) }); #if ENABLE_IL2CPP this.Serialize_MessagePackWriter_T_Options = (ref MessagePackWriter x, object y, MessagePackSerializerOptions z) => ThrowRefStructNotSupported(); #else ParameterExpression param1 = Expression.Parameter(typeof(MessagePackWriter).MakeByRefType(), "writer"); ParameterExpression param2 = Expression.Parameter(typeof(object), "obj"); ParameterExpression param3 = Expression.Parameter(typeof(MessagePackSerializerOptions), "options"); MethodCallExpression body = Expression.Call( null, serialize, param1, ti.IsValueType ? Expression.Unbox(param2, type) : Expression.Convert(param2, type), param3); MessagePackWriterSerialize lambda = Expression.Lambda(body, param1, param2, param3).Compile(PreferInterpretation); this.Serialize_MessagePackWriter_T_Options = lambda; #endif } { // public static T Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) MethodInfo deserialize = GetMethod(nameof(Deserialize), type, new Type[] { typeof(MessagePackReader).MakeByRefType(), typeof(MessagePackSerializerOptions) }); #if ENABLE_IL2CPP this.Deserialize_MessagePackReader_Options = (ref MessagePackReader reader, MessagePackSerializerOptions options) => { ThrowRefStructNotSupported(); return null; }; #else ParameterExpression param1 = Expression.Parameter(typeof(MessagePackReader).MakeByRefType(), "reader"); ParameterExpression param2 = Expression.Parameter(typeof(MessagePackSerializerOptions), "options"); UnaryExpression body = Expression.Convert(Expression.Call(null, deserialize, param1, param2), typeof(object)); MessagePackReaderDeserialize lambda = Expression.Lambda(body, param1, param2).Compile(); this.Deserialize_MessagePackReader_Options = lambda; #endif } { // public static T Deserialize(Stream stream, MessagePackSerializerOptions options, CancellationToken cancellationToken) MethodInfo deserialize = GetMethod(nameof(Deserialize), type, new Type[] { typeof(Stream), typeof(MessagePackSerializerOptions), typeof(CancellationToken) }); #if ENABLE_IL2CPP this.Deserialize_Stream_Options_CancellationToken = (x, y, z) => deserialize.Invoke(null, new object[] { x, y, z }); #else ParameterExpression param1 = Expression.Parameter(typeof(Stream), "stream"); ParameterExpression param2 = Expression.Parameter(typeof(MessagePackSerializerOptions), "options"); ParameterExpression param3 = Expression.Parameter(typeof(CancellationToken), "cancellationToken"); UnaryExpression body = Expression.Convert(Expression.Call(null, deserialize, param1, param2, param3), typeof(object)); Func lambda = Expression.Lambda>(body, param1, param2, param3).Compile(PreferInterpretation); this.Deserialize_Stream_Options_CancellationToken = lambda; #endif } { // public static ValueTask DeserializeObjectAsync(Stream stream, MessagePackSerializerOptions options, CancellationToken cancellationToken) MethodInfo deserialize = GetMethod(nameof(DeserializeObjectAsync), type, new Type[] { typeof(Stream), typeof(MessagePackSerializerOptions), typeof(CancellationToken) }); #if ENABLE_IL2CPP this.DeserializeAsync_Stream_Options_CancellationToken = (x, y, z) => (ValueTask)deserialize.Invoke(null, new object[] { x, y, z }); #else ParameterExpression param1 = Expression.Parameter(typeof(Stream), "stream"); ParameterExpression param2 = Expression.Parameter(typeof(MessagePackSerializerOptions), "options"); ParameterExpression param3 = Expression.Parameter(typeof(CancellationToken), "cancellationToken"); UnaryExpression body = Expression.Convert(Expression.Call(null, deserialize, param1, param2, param3), typeof(ValueTask)); Func> lambda = Expression.Lambda>>(body, param1, param2, param3).Compile(PreferInterpretation); this.DeserializeAsync_Stream_Options_CancellationToken = lambda; #endif } { // public static T Deserialize(ReadOnlyMemory bytes, MessagePackSerializerOptions options, CancellationToken cancellationToken) MethodInfo deserialize = GetMethod(nameof(Deserialize), type, new Type[] { typeof(ReadOnlyMemory), typeof(MessagePackSerializerOptions), typeof(CancellationToken) }); #if ENABLE_IL2CPP this.Deserialize_ReadOnlyMemory_Options = (x, y, z) => deserialize.Invoke(null, new object[] { x, y, z }); #else ParameterExpression param1 = Expression.Parameter(typeof(ReadOnlyMemory), "bytes"); ParameterExpression param2 = Expression.Parameter(typeof(MessagePackSerializerOptions), "options"); ParameterExpression param3 = Expression.Parameter(typeof(CancellationToken), "cancellationToken"); UnaryExpression body = Expression.Convert(Expression.Call(null, deserialize, param1, param2, param3), typeof(object)); Func, MessagePackSerializerOptions, CancellationToken, object> lambda = Expression.Lambda, MessagePackSerializerOptions, CancellationToken, object>>(body, param1, param2, param3).Compile(PreferInterpretation); this.Deserialize_ReadOnlyMemory_Options = lambda; #endif } { // public static T Deserialize(ReadOnlySequence bytes, MessagePackSerializerOptions options, CancellationToken cancellationToken) MethodInfo deserialize = GetMethod(nameof(Deserialize), type, new Type[] { typeof(ReadOnlySequence).MakeByRefType(), typeof(MessagePackSerializerOptions), typeof(CancellationToken) }); #if ENABLE_IL2CPP this.Deserialize_ReadOnlySequence_Options_CancellationToken = (x, y, z) => deserialize.Invoke(null, new object[] { x, y, z }); #else ParameterExpression param1 = Expression.Parameter(typeof(ReadOnlySequence), "bytes"); ParameterExpression param2 = Expression.Parameter(typeof(MessagePackSerializerOptions), "options"); ParameterExpression param3 = Expression.Parameter(typeof(CancellationToken), "cancellationToken"); UnaryExpression body = Expression.Convert(Expression.Call(null, deserialize, param1, param2, param3), typeof(object)); Func, MessagePackSerializerOptions, CancellationToken, object> lambda = Expression.Lambda, MessagePackSerializerOptions, CancellationToken, object>>(body, param1, param2, param3).Compile(PreferInterpretation); this.Deserialize_ReadOnlySequence_Options_CancellationToken = lambda; #endif } } private static void ThrowRefStructNotSupported() { // C# 8.0 is not supported call `ref struct` via reflection. (It is milestoned at .NET 6) throw new NotSupportedException("MessagePackWriter/Reader overload is not supported in MessagePackSerializer.NonGenerics."); } // null is generic type marker. private static MethodInfo GetMethod(string methodName, Type type, Type[] parameters) { return typeof(MessagePackSerializer).GetRuntimeMethods().Single(x => { if (methodName != x.Name) { return false; } ParameterInfo[] ps = x.GetParameters(); if (ps.Length != parameters.Length) { return false; } for (int i = 0; i < ps.Length; i++) { if (parameters[i] == null && ps[i].ParameterType.IsGenericParameter) { continue; } if (ps[i].ParameterType != parameters[i]) { return false; } } return true; }) .MakeGenericMethod(type); } } } }