Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions MsgPack/Formatters/EnumFormatter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System;
using System.Reflection;
using System.Reflection.Emit;

namespace CitizenFX.MsgPack.Formatters
{
internal static class EnumFormatter
{
public static Tuple<Serializer, MethodInfo> 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, MethodInfo>(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;
}
}
}
23 changes: 18 additions & 5 deletions MsgPack/Formatters/TypeFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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
Expand All @@ -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;

Expand Down
8 changes: 8 additions & 0 deletions MsgPack/MsgPackRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<,>))
{
Expand Down Expand Up @@ -228,6 +232,10 @@ private static Tuple<Serializer, MethodInfo> 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())
Expand Down