diff --git a/MsgPack/Formatters/EnumFormatter.cs b/MsgPack/Formatters/EnumFormatter.cs new file mode 100644 index 0000000..80b5b00 --- /dev/null +++ b/MsgPack/Formatters/EnumFormatter.cs @@ -0,0 +1,83 @@ +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace CitizenFX.MsgPack.Formatters +{ + internal static class EnumFormatter + { + public static Tuple Build(Type enumType, Type unused = null) + { + string name = $"EnumFormatter_{enumType.FullName}"; + Type buildType = MsgPackRegistry.m_moduleBuilder.GetType(name); + + MethodInfo methodSerialize, methodDeserialize, methodObjectSerialize; + + if (buildType == null) + { + TypeBuilder typeBuilder = MsgPackRegistry.m_moduleBuilder.DefineType(name); + + methodSerialize = BuildSerializer(enumType, typeBuilder); + BuildDeserializer(enumType, typeBuilder); + BuildObjectSerializer(enumType, methodSerialize, typeBuilder); + + buildType = typeBuilder.CreateType(); + } + + methodSerialize = buildType.GetMethod("Serialize", new[] { typeof(MsgPackSerializer), enumType }); + methodDeserialize = buildType.GetMethod("Deserialize"); + methodObjectSerialize = buildType.GetMethod("Serialize", new[] { typeof(MsgPackSerializer), typeof(object) }); + + Serializer serializer = new Serializer(methodSerialize, methodObjectSerialize); + + MsgPackRegistry.RegisterSerializer(enumType, serializer); + MsgPackRegistry.RegisterDeserializer(enumType, methodDeserialize); + + return new Tuple(serializer, methodDeserialize); + } + + private static MethodInfo BuildObjectSerializer(Type enumType, MethodInfo methodSerialize, TypeBuilder typeBuilder) + { + MethodBuilder method = typeBuilder.DefineMethod("Serialize", + MethodAttributes.Public | MethodAttributes.Static, + typeof(void), + new[] { typeof(MsgPackSerializer), typeof(object) }); + + var g = method.GetILGenerator(); + g.Emit(OpCodes.Ldarg_0); + g.Emit(OpCodes.Ldarg_1); + g.Emit(OpCodes.Unbox_Any, enumType); + g.EmitCall(OpCodes.Call, methodSerialize, null); + g.Emit(OpCodes.Ret); + + return method; + } + + private static MethodInfo BuildSerializer(Type enumType, TypeBuilder typeBuilder) + { + MethodBuilder method = typeBuilder.DefineMethod("Serialize", + MethodAttributes.Public | MethodAttributes.Static, + typeof(void), + new[] { typeof(MsgPackSerializer), enumType }); + + var g = method.GetILGenerator(); + g.Emit(OpCodes.Ldarg_0); + g.Emit(OpCodes.Ldarg_1); + g.EmitCall(OpCodes.Call, MsgPackRegistry.GetOrCreateSerializer(typeof(uint)), null); + g.Emit(OpCodes.Ret); + return method; + } + + private static MethodInfo BuildDeserializer(Type enumType, TypeBuilder typeBuilder) + { + MethodBuilder methodDeserialize = typeBuilder.DefineMethod("Deserialize", + MethodAttributes.Public | MethodAttributes.Static, + enumType, new[] { typeof(MsgPackDeserializer).MakeByRefType() }); + var g = methodDeserialize.GetILGenerator(); + g.Emit(OpCodes.Ldarg_0); + g.EmitCall(OpCodes.Call, MsgPackRegistry.GetOrCreateDeserializer(typeof(uint)), null); + g.Emit(OpCodes.Ret); + return methodDeserialize; + } + } +} diff --git a/MsgPack/Formatters/TypeFormatter.cs b/MsgPack/Formatters/TypeFormatter.cs index 393b3e0..8e60080 100644 --- a/MsgPack/Formatters/TypeFormatter.cs +++ b/MsgPack/Formatters/TypeFormatter.cs @@ -227,7 +227,13 @@ private static void BuildSerializeArrayBody(Type type, ILGenerator g, MemberInfo // Value g.Emit(OpCodes.Ldarg_0); - g.Emit(OpCodes.Ldarg_1); + // if it's a struct.. we handle it differently + // using the address of the argument and not its value. + if (type.IsValueType) + g.Emit(OpCodes.Ldarga_S, 1); + else + g.Emit(OpCodes.Ldarg_1); + g.EmitCall(OpCodes.Call, property.GetGetMethod(), null); g.EmitCall(OpCodes.Call, serializer, null); } @@ -290,11 +296,11 @@ private static void BuildSerializeMapBody(Type type, ILGenerator g, DynamicArray case PropertyInfo property: { - var methodFieldSerializer = type == property.PropertyType + var methodPropertySerializer = type == property.PropertyType ? currentSerializer : MsgPackRegistry.GetOrCreateSerializer(property.PropertyType); - if (methodFieldSerializer == null) + if (methodPropertySerializer == null) throw new NotSupportedException($"Requested serializer for {type.Name}.{property.Name} of type {property.PropertyType} could not be found."); // Key @@ -304,9 +310,16 @@ private static void BuildSerializeMapBody(Type type, ILGenerator g, DynamicArray // Value g.Emit(OpCodes.Ldarg_0); - g.Emit(OpCodes.Ldarg_1); + // Same as above + // if it's a struct.. we handle it differently + // using the address of the argument and not its value. + if (type.IsValueType) + g.Emit(OpCodes.Ldarga_S, 1); + else + g.Emit(OpCodes.Ldarg_1); + g.EmitCall(OpCodes.Call, property.GetGetMethod(), null); - g.EmitCall(OpCodes.Call, methodFieldSerializer, null); + g.EmitCall(OpCodes.Call, methodPropertySerializer, null); } break; diff --git a/MsgPack/MsgPackRegistry.cs b/MsgPack/MsgPackRegistry.cs index ddd0c25..522b5d7 100644 --- a/MsgPack/MsgPackRegistry.cs +++ b/MsgPack/MsgPackRegistry.cs @@ -146,6 +146,10 @@ internal static void Serialize(MsgPackSerializer serializer, object obj) { serializer.Serialize(e); } + else if (type.IsEnum) + { + serializer.Serialize(Convert.ToUInt64(obj)); + } #endif else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>)) { @@ -228,6 +232,10 @@ private static Tuple CreateSerializer(Type type) else throw new NotSupportedException($"{type} should've already been registered"); } + else if (type.IsEnum) + { + return EnumFormatter.Build(type); + } else if (type.IsArray) { switch (type.GetArrayRank())