private static void EmitDeserializeConverter(ILGenerator il, SerializedMemberInfo member, Label nextLabel, LocalAllocator GetLocal, Action <ILGenerator> thisobj, Action <ILGenerator> parentobj) { if (!member.HasConverter) { throw new InvalidOperationException("EmitDeserializeConverter called for member without converter"); } using var stlocal = GetLocal.Allocate(typeof(Value)); using var valLocal = GetLocal.Allocate(member.Type); il.Emit(OpCodes.Stloc, stlocal); il.BeginExceptionBlock(); il.Emit(OpCodes.Ldsfld, member.ConverterField); il.Emit(OpCodes.Ldloc, stlocal); parentobj(il); if (member.IsGenericConverter) { var fromValueBase = member.ConverterBase.GetMethod(nameof(ValueConverter <int> .FromValue), new[] { typeof(Value), typeof(object) }); var fromValue = member.Converter.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .FirstOrDefault(m => m.GetBaseDefinition() == fromValueBase) ?? fromValueBase; il.Emit(OpCodes.Call, fromValue); } else { var fromValueBase = typeof(IValueConverter).GetMethod(nameof(IValueConverter.FromValue), new[] { typeof(Value), typeof(object) }); var fromValue = member.Converter.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .FirstOrDefault(m => m.GetBaseDefinition() == fromValueBase) ?? fromValueBase; il.Emit(OpCodes.Call, fromValue); if (member.Type.IsValueType) { il.Emit(OpCodes.Unbox); } } il.Emit(OpCodes.Stloc, valLocal); il.BeginCatchBlock(typeof(Exception)); EmitWarnException(il, "Error occurred while deserializing"); il.Emit(OpCodes.Leave, nextLabel); il.EndExceptionBlock(); il.Emit(OpCodes.Ldloc, valLocal); }
private static void EmitSerializeStructure(ILGenerator il, IEnumerable <SerializedMemberInfo> structure, LocalAllocator GetLocal, Action <ILGenerator> thisarg, Action <ILGenerator> parentobj) { var MapCreate = typeof(Value).GetMethod(nameof(Value.Map)); var MapAdd = typeof(Map).GetMethod(nameof(Map.Add)); using var mapLocal = GetLocal.Allocate(typeof(Map)); using var valueLocal = GetLocal.Allocate(typeof(Value)); il.Emit(OpCodes.Call, MapCreate); il.Emit(OpCodes.Stloc, mapLocal); foreach (var mem in structure) { EmitSerializeMember(il, mem, GetLocal, thisarg, parentobj); il.Emit(OpCodes.Stloc, valueLocal); il.Emit(OpCodes.Ldloc, mapLocal); il.Emit(OpCodes.Ldstr, mem.Name); il.Emit(OpCodes.Ldloc, valueLocal); il.Emit(OpCodes.Call, MapAdd); } il.Emit(OpCodes.Ldloc, mapLocal); }
private static void EmitDeserializeGeneratedValue(ILGenerator il, SerializedMemberInfo member, Type srcType, LocalAllocator GetLocal, Action <ILGenerator> thisarg, Action <ILGenerator> parentobj) { var IGeneratedStore_Deserialize = typeof(IGeneratedStore).GetMethod(nameof(IGeneratedStore.Deserialize)); using var valuel = GetLocal.Allocate(srcType); var noCreate = il.DefineLabel(); il.Emit(OpCodes.Stloc, valuel); EmitLoad(il, member, thisarg); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Isinst, typeof(IGeneratedStore)); il.Emit(OpCodes.Brtrue_S, noCreate); il.Emit(OpCodes.Pop); EmitCreateChildGenerated(il, member.Type, parentobj); il.MarkLabel(noCreate); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldloc, valuel); il.Emit(OpCodes.Callvirt, IGeneratedStore_Deserialize); }
// emit takes no args, leaves Value at top of stack private static void EmitSerializeMember(ILGenerator il, SerializedMemberInfo member, LocalAllocator GetLocal, Action <ILGenerator> thisarg, Action <ILGenerator> parentobj) { EmitLoad(il, member, thisarg); var endSerialize = il.DefineLabel(); if (member.AllowNull) { var passedNull = il.DefineLabel(); il.Emit(OpCodes.Dup); if (member.IsNullable) { il.Emit(OpCodes.Call, member.Nullable_HasValue.GetGetMethod()); } il.Emit(OpCodes.Brtrue, passedNull); il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Br, endSerialize); il.MarkLabel(passedNull); } if (member.IsNullable) { il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod()); } var memberConversionType = member.ConversionType; var targetType = GetExpectedValueTypeForType(memberConversionType); if (member.HasConverter) { using var stlocal = GetLocal.Allocate(member.Type); using var valLocal = GetLocal.Allocate(typeof(Value)); il.Emit(OpCodes.Stloc, stlocal); il.BeginExceptionBlock(); il.Emit(OpCodes.Ldsfld, member.ConverterField); il.Emit(OpCodes.Ldloc, stlocal); if (member.IsGenericConverter) { var toValueBase = member.ConverterBase.GetMethod(nameof(ValueConverter <int> .ToValue), new[] { member.ConverterTarget, typeof(object) }); var toValue = member.Converter.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .FirstOrDefault(m => m.GetBaseDefinition() == toValueBase) ?? toValueBase; il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, toValue); } else { var toValueBase = typeof(IValueConverter).GetMethod(nameof(IValueConverter.ToValue), new[] { typeof(object), typeof(object) }); var toValue = member.Converter.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .FirstOrDefault(m => m.GetBaseDefinition() == toValueBase) ?? toValueBase; il.Emit(OpCodes.Box); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, toValue); } il.Emit(OpCodes.Stloc, valLocal); il.BeginCatchBlock(typeof(Exception)); EmitWarnException(il, "Error serializing member using converter"); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Stloc, valLocal); il.EndExceptionBlock(); il.Emit(OpCodes.Ldloc, valLocal); } else if (targetType == typeof(Text)) { // only happens when arg is a string or char var TextCreate = typeof(Value).GetMethod(nameof(Value.Text)); if (member.Type == typeof(char)) { var strFromChar = typeof(char).GetMethod(nameof(char.ToString), new[] { typeof(char) }); il.Emit(OpCodes.Call, strFromChar); } il.Emit(OpCodes.Call, TextCreate); } else if (targetType == typeof(Boolean)) { var BoolCreate = typeof(Value).GetMethod(nameof(Value.Bool)); il.Emit(OpCodes.Call, BoolCreate); } else if (targetType == typeof(Integer)) { var IntCreate = typeof(Value).GetMethod(nameof(Value.Integer)); EmitNumberConvertTo(il, IntCreate.GetParameters()[0].ParameterType, member.Type); il.Emit(OpCodes.Call, IntCreate); } else if (targetType == typeof(FloatingPoint)) { var FloatCreate = typeof(Value).GetMethod(nameof(Value.Float)); EmitNumberConvertTo(il, FloatCreate.GetParameters()[0].ParameterType, member.Type); il.Emit(OpCodes.Call, FloatCreate); } else if (targetType == typeof(List)) { // TODO: impl this (enumerables) Logger.config.Warn($"Implicit conversions to {targetType} are not currently implemented"); il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldnull); } else if (targetType == typeof(Map)) { // TODO: support other aggregate types if (!memberConversionType.IsValueType) { // if it is a reference type, we assume that its a generated type implementing IGeneratedStore var IGeneratedStore_Serialize = typeof(IGeneratedStore).GetMethod(nameof(IGeneratedStore.Serialize)); var IGeneratedStoreT_CopyFrom = typeof(IGeneratedStore <>).MakeGenericType(member.Type) .GetMethod(nameof(IGeneratedStore <object> .CopyFrom)); if (!member.IsVirtual) { var noCreate = il.DefineLabel(); using var stlocal = GetLocal.Allocate(member.Type); // first check to make sure that this is an IGeneratedStore, because we don't control assignments to it il.Emit(OpCodes.Dup); il.Emit(OpCodes.Isinst, typeof(IGeneratedStore)); il.Emit(OpCodes.Brtrue_S, noCreate); il.Emit(OpCodes.Stloc, stlocal); EmitCreateChildGenerated(il, member.Type, parentobj); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldloc, stlocal); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Callvirt, IGeneratedStoreT_CopyFrom); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Stloc, stlocal); EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, stlocal), thisarg); il.MarkLabel(noCreate); } il.Emit(OpCodes.Callvirt, IGeneratedStore_Serialize); } else { // generate serialization for value types using var valueLocal = GetLocal.Allocate(memberConversionType); var structure = ReadObjectMembers(memberConversionType); if (!structure.Any()) { Logger.config.Warn($"Custom value type {memberConversionType.FullName} (when compiling serialization of" + $" {member.Name} on {member.Member.DeclaringType.FullName}) has no accessible members"); il.Emit(OpCodes.Pop); } else { il.Emit(OpCodes.Stloc, valueLocal); } EmitSerializeStructure(il, structure, GetLocal, il => il.Emit(OpCodes.Ldloca, valueLocal), parentobj); } } il.MarkLabel(endSerialize); }
private static void EmitDeserializeValue(ILGenerator il, SerializedMemberInfo member, Type targetType, Type expected, LocalAllocator GetLocal, Action <ILGenerator> thisarg, Action <ILGenerator> parentobj) { if (typeof(Value).IsAssignableFrom(targetType)) { return; // do nothing } if (expected == typeof(Text)) { var getter = expected.GetProperty(nameof(Text.Value)).GetGetMethod(); il.Emit(OpCodes.Call, getter); if (targetType == typeof(char)) { var strIndex = typeof(string).GetProperty("Chars").GetGetMethod(); // string's indexer is specially named Chars il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Call, strIndex); } } else if (expected == typeof(Boolean)) { var getter = expected.GetProperty(nameof(Boolean.Value)).GetGetMethod(); il.Emit(OpCodes.Call, getter); } else if (expected == typeof(Integer)) { var getter = expected.GetProperty(nameof(Integer.Value)).GetGetMethod(); il.Emit(OpCodes.Call, getter); EmitNumberConvertTo(il, targetType, getter.ReturnType); } else if (expected == typeof(FloatingPoint)) { var getter = expected.GetProperty(nameof(FloatingPoint.Value)).GetGetMethod(); il.Emit(OpCodes.Call, getter); EmitNumberConvertTo(il, targetType, getter.ReturnType); } // TODO: implement stuff for lists and maps of various types (probably call out somewhere else to figure out what to do) else if (expected == typeof(Map)) { if (!targetType.IsValueType) { EmitDeserializeGeneratedValue(il, member, expected, GetLocal, thisarg, parentobj); } else { using var mapLocal = GetLocal.Allocate(typeof(Map)); using var resultLocal = GetLocal.Allocate(targetType); using var valueLocal = GetLocal.Allocate(typeof(Value)); var structure = ReadObjectMembers(targetType); if (!structure.Any()) { Logger.Config.Warn($"Custom value type {targetType.FullName} (when compiling serialization of" + $" {member.Name} on {member.Member.DeclaringType.FullName}) has no accessible members"); il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldloca, resultLocal); il.Emit(OpCodes.Initobj, targetType); } else { il.Emit(OpCodes.Stloc, mapLocal); EmitLoad(il, member, thisarg); il.Emit(OpCodes.Stloc, resultLocal); EmitDeserializeStructure(il, structure, mapLocal, valueLocal, GetLocal, il => il.Emit(OpCodes.Ldloca, resultLocal), parentobj); } il.Emit(OpCodes.Ldloc, resultLocal); } } else { Logger.Config.Warn($"Implicit conversions to {expected} are not currently implemented"); il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldnull); } }
private static void EmitDeserializeMember(ILGenerator il, SerializedMemberInfo member, Label nextLabel, Action <ILGenerator> getValue, LocalAllocator GetLocal, Action <ILGenerator> thisobj, Action <ILGenerator> parentobj) { var Object_GetType = typeof(object).GetMethod(nameof(Object.GetType)); var implLabel = il.DefineLabel(); var passedTypeCheck = il.DefineLabel(); var expectType = GetExpectedValueTypeForType(member.ConversionType); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Brtrue_S, implLabel); // null check if (!member.AllowNull) { il.Emit(OpCodes.Pop); EmitLogError(il, $"Member {member.Name} ({member.Type}) not nullable", tailcall: false, expected: il => EmitTypeof(il, expectType)); il.Emit(OpCodes.Br, nextLabel); } else if (member.IsNullable) { il.Emit(OpCodes.Pop); using var valTLocal = GetLocal.Allocate(member.Type); il.Emit(OpCodes.Ldloca, valTLocal); il.Emit(OpCodes.Initobj, member.Type); EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, valTLocal), thisobj); il.Emit(OpCodes.Br, nextLabel); } else { il.Emit(OpCodes.Pop); EmitStore(il, member, il => il.Emit(OpCodes.Ldnull), thisobj); il.Emit(OpCodes.Br, nextLabel); } if (!member.HasConverter) { il.MarkLabel(implLabel); il.Emit(OpCodes.Isinst, expectType); //replaces on stack il.Emit(OpCodes.Dup); // duplicate cloned value il.Emit(OpCodes.Brtrue, passedTypeCheck); // null check } var errorHandle = il.DefineLabel(); // special cases to handle coersion between Float and Int if (member.HasConverter) { il.MarkLabel(implLabel); } else if (expectType == typeof(FloatingPoint)) { var specialTypeCheck = il.DefineLabel(); il.Emit(OpCodes.Pop); getValue(il); il.Emit(OpCodes.Isinst, typeof(Integer)); //replaces on stack il.Emit(OpCodes.Dup); // duplicate cloned value il.Emit(OpCodes.Brfalse, errorHandle); // null check var Integer_CoerceToFloat = typeof(Integer).GetMethod(nameof(Integer.AsFloat)); il.Emit(OpCodes.Call, Integer_CoerceToFloat); il.Emit(OpCodes.Br, passedTypeCheck); } else if (expectType == typeof(Integer)) { var specialTypeCheck = il.DefineLabel(); il.Emit(OpCodes.Pop); getValue(il); il.Emit(OpCodes.Isinst, typeof(FloatingPoint)); //replaces on stack il.Emit(OpCodes.Dup); // duplicate cloned value il.Emit(OpCodes.Brfalse, errorHandle); // null check var Float_CoerceToInt = typeof(FloatingPoint).GetMethod(nameof(FloatingPoint.AsInteger)); il.Emit(OpCodes.Call, Float_CoerceToInt); il.Emit(OpCodes.Br, passedTypeCheck); } if (!member.HasConverter) { il.MarkLabel(errorHandle); il.Emit(OpCodes.Pop); EmitLogError(il, $"Unexpected type deserializing {member.Name}", tailcall: false, expected: il => EmitTypeof(il, expectType), found: il => { getValue(il); il.Emit(OpCodes.Callvirt, Object_GetType); }); il.Emit(OpCodes.Br, nextLabel); } il.MarkLabel(passedTypeCheck); using var local = GetLocal.Allocate(member.Type); if (member.HasConverter) { EmitDeserializeConverter(il, member, nextLabel, GetLocal, thisobj, parentobj); } else if (member.IsNullable) { EmitDeserializeNullable(il, member, expectType, GetLocal, thisobj, parentobj); } else { EmitDeserializeValue(il, member, member.Type, expectType, GetLocal, thisobj, parentobj); } il.Emit(OpCodes.Stloc, local); EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, local), thisobj); }
private static void EmitCorrectMember(ILGenerator il, SerializedMemberInfo member, bool shouldLock, bool alwaysNew, LocalAllocator GetLocal, Action <ILGenerator> thisobj, Action <ILGenerator> parentobj) { if (!NeedsCorrection(member)) { return; } var endLabel = il.DefineLabel(); if (member.IsNullable) { il.Emit(OpCodes.Dup); il.Emit(OpCodes.Call, member.Nullable_HasValue.GetGetMethod()); il.Emit(OpCodes.Brfalse, endLabel); il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod()); } var convType = member.ConversionType; if (!convType.IsValueType) { // currently the only thing for this is where expect == Map, so do generate shit var copyFrom = typeof(IGeneratedStore <>).MakeGenericType(convType).GetMethod(nameof(IGeneratedStore <Config> .CopyFrom)); var noCreate = il.DefineLabel(); using var valLocal = GetLocal.Allocate(convType); if (member.AllowNull) { il.Emit(OpCodes.Dup); il.Emit(OpCodes.Brfalse_S, endLabel); // thing is null, just bypass it all } if (!alwaysNew) { il.Emit(OpCodes.Dup); il.Emit(OpCodes.Isinst, typeof(IGeneratedStore)); il.Emit(OpCodes.Brtrue_S, endLabel); // our input is already something we like } il.Emit(OpCodes.Stloc, valLocal); if (!alwaysNew) { EmitLoad(il, member, thisobj); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Isinst, typeof(IGeneratedStore)); il.Emit(OpCodes.Brtrue_S, noCreate); il.Emit(OpCodes.Pop); } EmitCreateChildGenerated(il, convType, parentobj); il.MarkLabel(noCreate); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldloc, valLocal); il.Emit(shouldLock ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); il.Emit(OpCodes.Callvirt, copyFrom); } else { // for special value types, we'll go ahead and correct each of their members var structure = ReadObjectMembers(convType); using var valueLocal = GetLocal.Allocate(convType); il.Emit(OpCodes.Stloc, valueLocal); void LdlocaValueLocal(ILGenerator il) => il.Emit(OpCodes.Ldloca, valueLocal); foreach (var mem in structure) { if (NeedsCorrection(mem)) { EmitLoadCorrectStore(il, mem, shouldLock, alwaysNew, GetLocal, LdlocaValueLocal, LdlocaValueLocal, parentobj); } } il.Emit(OpCodes.Ldloc, valueLocal); } if (member.IsNullable) { il.Emit(OpCodes.Newobj, member.Nullable_Construct); } il.MarkLabel(endLabel); }