// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; namespace EpicGames.Serialization.Converters { class CbEnumConverter : CbConverter, ICbConverterMethods where T : Enum { readonly Func _readFunc; readonly Action _writeFunc; readonly Action _writeNamedFunc; public CbEnumConverter() { Type type = typeof(T); ReadMethod = new DynamicMethod($"Read_{type.Name}", type, [typeof(CbField)]); CreateEnumReader(ReadMethod.GetILGenerator()); _readFunc = (Func)ReadMethod.CreateDelegate(typeof(Func)); WriteMethod = new DynamicMethod($"Write_{type.Name}", null, [typeof(CbWriter), type]); CreateEnumWriter(WriteMethod.GetILGenerator()); _writeFunc = (Action)WriteMethod.CreateDelegate(typeof(Action)); WriteNamedMethod = new DynamicMethod($"WriteNamed_{type.Name}", null, [typeof(CbWriter), typeof(CbFieldName), type]); CreateNamedEnumWriter(WriteNamedMethod.GetILGenerator()); _writeNamedFunc = (Action)WriteNamedMethod.CreateDelegate(typeof(Action)); } public DynamicMethod ReadMethod { get; } MethodInfo ICbConverterMethods.ReadMethod => ReadMethod; public DynamicMethod WriteMethod { get; } MethodInfo ICbConverterMethods.WriteMethod => WriteMethod; public DynamicMethod WriteNamedMethod { get; } MethodInfo ICbConverterMethods.WriteNamedMethod => WriteNamedMethod; public override T Read(CbField field) => _readFunc(field); public override void Write(CbWriter writer, T value) => _writeFunc(writer, value); public override void WriteNamed(CbWriter writer, CbFieldName name, T value) => _writeNamedFunc(writer, name, value); static void CreateEnumReader(ILGenerator generator) { generator.Emit(OpCodes.Ldarg_0); generator.EmitCall(OpCodes.Call, GetMethodInfo(x => x.AsInt32()), null); generator.Emit(OpCodes.Ret); } static void CreateEnumWriter(ILGenerator generator) { generator.Emit(OpCodes.Ldarg_1); Label skipLabel = generator.DefineLabel(); generator.Emit(OpCodes.Brfalse, skipLabel); generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldarg_1); generator.Emit(OpCodes.Conv_I8); generator.EmitCall(OpCodes.Call, GetMethodInfo(x => x.WriteIntegerValue(0L)), null); generator.MarkLabel(skipLabel); generator.Emit(OpCodes.Ret); } static void CreateNamedEnumWriter(ILGenerator generator) { generator.Emit(OpCodes.Ldarg_2); Label skipLabel = generator.DefineLabel(); generator.Emit(OpCodes.Brfalse, skipLabel); generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldarg_1); generator.Emit(OpCodes.Ldarg_2); generator.Emit(OpCodes.Conv_I8); generator.EmitCall(OpCodes.Call, GetMethodInfo(x => x.WriteInteger(default, 0L)), null); generator.MarkLabel(skipLabel); generator.Emit(OpCodes.Ret); } static MethodInfo GetMethodInfo(Expression> expr) { return ((MethodCallExpression)expr.Body).Method; } } class CbEnumConverterFactory : CbConverterFactory { public override CbConverter? CreateConverter(Type type) { CbConverter? converter = null; if (type.IsEnum) { Type converterType = typeof(CbEnumConverter<>).MakeGenericType(type); converter = (CbConverter?)Activator.CreateInstance(converterType); } return converter; } } }