/// <summary> /// Transforms open generic types to closed instantiation using context information. /// As an example, if <c>B{T}</c> inherits from <c>A{T}</c>, running it with <c>B{C}</c> as context and <c>A{B.T}</c> /// as type, it will return <c>A{C}</c>. /// </summary> public static TypeReference Process(TypeReference context, TypeReference type) { if (type is null) { return(null); } var parentContext = context; GenericInstanceType genericInstanceTypeContext = null; while (parentContext != null) { genericInstanceTypeContext = parentContext as GenericInstanceType; if (genericInstanceTypeContext != null) { break; } parentContext = parentContext.Resolve().BaseType; } if (genericInstanceTypeContext is null || genericInstanceTypeContext.ContainsGenericParameter) { return(type); } // Build dictionary that will map generic type to their real implementation type var genericTypeMapping = new Dictionary <TypeReference, TypeReference>(); while (parentContext != null) { var resolvedType = parentContext.Resolve(); for (int i = 0; i < resolvedType.GenericParameters.Count; ++i) { var genericParameter = parentContext.GetElementType().Resolve().GenericParameters[i]; genericTypeMapping.Add(genericParameter, genericInstanceTypeContext.GenericArguments[i]); } parentContext = parentContext.Resolve().BaseType; if (parentContext is GenericInstanceType) { genericInstanceTypeContext = parentContext as GenericInstanceType; } } var visitor = new ResolveGenericsVisitor(genericTypeMapping); var result = visitor.VisitDynamic(type); // Make sure type is closed now if (result.ContainsGenericParameter) { throw new InvalidOperationException("Unsupported generic resolution."); } return(result); }
public static void InflateGenericType(TypeDefinition genericType, TypeDefinition inflatedType, params TypeReference[] genericTypes) { // Base type var genericMapping = new Dictionary <TypeReference, TypeReference>(); for (int i = 0; i < genericTypes.Length; ++i) { genericMapping.Add(genericType.GenericParameters[i], genericTypes[i]); } var resolveGenericsVisitor = new ResolveGenericsVisitor(genericMapping); inflatedType.BaseType = inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(genericType.BaseType)); // Some stuff are not handled yet if (genericType.HasNestedTypes) { throw new NotImplementedException(); } foreach (var field in genericType.Fields) { var clonedField = new FieldDefinition(field.Name, field.Attributes, inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(field.FieldType))); inflatedType.Fields.Add(clonedField); } foreach (var property in genericType.Properties) { if (property.HasParameters) { throw new NotImplementedException(); } var clonedProperty = new PropertyDefinition(property.Name, property.Attributes, inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(property.PropertyType))) { HasThis = property.HasThis, GetMethod = property.GetMethod != null?InflateMethod(inflatedType, property.GetMethod, resolveGenericsVisitor) : null, SetMethod = property.SetMethod != null?InflateMethod(inflatedType, property.GetMethod, resolveGenericsVisitor) : null, }; inflatedType.Properties.Add(clonedProperty); } // Clone methods foreach (var method in genericType.Methods) { var clonedMethod = InflateMethod(inflatedType, method, resolveGenericsVisitor); inflatedType.Methods.Add(clonedMethod); } }
public static IEnumerable <SerializableItem> GetSerializableItems(TypeReference type, bool serializeFields, ComplexTypeSerializerFlags?flagsOverride = null) { foreach (var serializableItemOriginal in GetSerializableItems(type.Resolve(), serializeFields, flagsOverride)) { var serializableItem = serializableItemOriginal; // Try to resolve open generic types with context to have closed types. if (serializableItem.Type.ContainsGenericParameter()) { serializableItem.Type = ResolveGenericsVisitor.Process(type, serializableItem.Type); } yield return(serializableItem); } }
/// <summary> /// Generates serializer code using Cecil. /// </summary> /// <param name="registry"></param> private static void GenerateSerializerCode(ComplexSerializerRegistry registry, out ObjectId serializationHash) { var hash = new ObjectIdBuilder(); // First, hash global binary format version, in case it gets bumped hash.Write(DataSerializer.BinaryFormatVersion); var assembly = registry.Assembly; var strideCoreModule = assembly.GetStrideCoreModule(); var dataSerializerTypeRef = assembly.MainModule.ImportReference(strideCoreModule.GetType("Stride.Core.Serialization.DataSerializer`1")); var serializerSelectorType = strideCoreModule.GetType("Stride.Core.Serialization.SerializerSelector"); var serializerSelectorTypeRef = assembly.MainModule.ImportReference(serializerSelectorType); var serializerSelectorGetSerializerRef = assembly.MainModule.ImportReference(serializerSelectorType.Methods.Single(x => x.Name == "GetSerializer" && x.Parameters.Count == 0 && x.GenericParameters.Count == 1)); var memberSerializerCreateRef = assembly.MainModule.ImportReference(strideCoreModule.GetType("Stride.Core.Serialization.MemberSerializer`1").Methods.Single(x => x.Name == "Create")); var dataSerializerSerializeMethod = dataSerializerTypeRef.Resolve().Methods.Single(x => x.Name == "Serialize" && (x.Attributes & MethodAttributes.Abstract) != 0); var dataSerializerSerializeMethodRef = assembly.MainModule.ImportReference(dataSerializerSerializeMethod); // Generate serializer code for each type (we generate code similar to ComplexClassSerializerGenerator.tt, see this file for reference) foreach (var complexType in registry.Context.ComplexTypes) { var type = complexType.Key; var serializerType = (TypeDefinition)complexType.Value.SerializerType; var genericParameters = serializerType.GenericParameters.ToArray <TypeReference>(); var typeWithGenerics = type.MakeGenericType(genericParameters); // Hash hash.Write(typeWithGenerics.FullName); TypeReference parentType = null; FieldDefinition parentSerializerField = null; if (complexType.Value.IsComplexSerializerProcessParentType) { parentType = ResolveGenericsVisitor.Process(serializerType, type.BaseType); serializerType.Fields.Add(parentSerializerField = new FieldDefinition("parentSerializer", Mono.Cecil.FieldAttributes.Private, dataSerializerTypeRef.MakeGenericType(parentType))); hash.Write("parent"); } var serializableItems = ComplexSerializerRegistry.GetSerializableItems(type).ToArray(); var serializableItemInfos = new Dictionary <TypeReference, (FieldDefinition SerializerField, TypeReference Type)>(TypeReferenceEqualityComparer.Default); var localsByTypes = new Dictionary <TypeReference, VariableDefinition>(TypeReferenceEqualityComparer.Default); ResolveGenericsVisitor genericResolver = null; if (type.HasGenericParameters) { var genericMapping = new Dictionary <TypeReference, TypeReference>(); for (int i = 0; i < type.GenericParameters.Count; i++) { genericMapping[type.GenericParameters[i]] = serializerType.GenericParameters[i]; } genericResolver = new ResolveGenericsVisitor(genericMapping); } foreach (var serializableItem in serializableItems) { if (serializableItemInfos.ContainsKey(serializableItem.Type)) { continue; } var serializableItemType = serializableItem.Type; if (genericResolver != null) { serializableItemType = genericResolver.VisitDynamic(serializableItemType); } var fieldDefinition = new FieldDefinition($"{Utilities.BuildValidClassName(serializableItemType.FullName)}Serializer", Mono.Cecil.FieldAttributes.Private, dataSerializerTypeRef.MakeGenericType(serializableItemType)); serializableItemInfos.Add(serializableItem.Type, (fieldDefinition, serializableItemType)); serializerType.Fields.Add(fieldDefinition); hash.Write(serializableItem.Type.FullName); hash.Write(serializableItem.Name); hash.Write(serializableItem.AssignBack); } // Add constructor (call parent constructor) var ctor = new MethodDefinition(".ctor", MethodAttributes.RTSpecialName | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Public, assembly.MainModule.TypeSystem.Void); ctor.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); ctor.Body.Instructions.Add(Instruction.Create(OpCodes.Call, assembly.MainModule.ImportReference(serializerType.BaseType.Resolve().GetEmptyConstructor(true)).MakeGeneric(typeWithGenerics))); ctor.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); serializerType.Methods.Add(ctor); // Add Initialize method var initialize = new MethodDefinition("Initialize", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual, assembly.MainModule.TypeSystem.Void); initialize.Parameters.Add(new ParameterDefinition("serializerSelector", ParameterAttributes.None, serializerSelectorTypeRef)); if (complexType.Value.IsComplexSerializerProcessParentType) { initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1)); initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Callvirt, serializerSelectorGetSerializerRef.MakeGenericMethod(parentType))); initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Stfld, parentSerializerField.MakeGeneric(genericParameters))); } foreach (var serializableItem in serializableItemInfos) { initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1)); initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldc_I4_1)); initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Call, memberSerializerCreateRef.MakeGeneric(serializableItem.Value.Type))); initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Stfld, serializableItem.Value.SerializerField.MakeGeneric(genericParameters))); } initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); serializerType.Methods.Add(initialize); // Add Serialize method var serialize = new MethodDefinition("Serialize", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual, assembly.MainModule.TypeSystem.Void); serialize.Parameters.Add(new ParameterDefinition("obj", ParameterAttributes.None, typeWithGenerics.MakeByReferenceType())); // Copy other parameters from parent method for (int i = 1; i < dataSerializerSerializeMethod.Parameters.Count; ++i) { var parentParameter = dataSerializerSerializeMethod.Parameters[i]; serialize.Parameters.Add(new ParameterDefinition(parentParameter.Name, ParameterAttributes.None, assembly.MainModule.ImportReference(parentParameter.ParameterType))); } if (complexType.Value.IsComplexSerializerProcessParentType) { serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldfld, parentSerializerField.MakeGeneric(genericParameters))); serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1)); serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_2)); serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_3)); serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Callvirt, dataSerializerSerializeMethodRef.MakeGeneric(parentType))); } if (serializableItems.Length > 0) { var blockStartInstructions = new[] { Instruction.Create(OpCodes.Nop), Instruction.Create(OpCodes.Nop) }; // Iterate over ArchiveMode for (int i = 0; i < 2; ++i) { var archiveMode = i == 0 ? ArchiveMode.Serialize : ArchiveMode.Deserialize; // Check mode if (archiveMode == ArchiveMode.Serialize) { serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_2)); serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldc_I4, (int)archiveMode)); serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ceq)); serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Brfalse, blockStartInstructions[0])); } else { serialize.Body.Instructions.Add(blockStartInstructions[0]); } foreach (var serializableItem in serializableItems) { if (serializableItem.HasFixedAttribute) { throw new NotImplementedException("FixedBuffer attribute is not supported."); } var memberAssignBack = serializableItem.AssignBack; var memberVariableName = (serializableItem.MemberInfo is PropertyDefinition || !memberAssignBack) ? ComplexSerializerRegistry.CreateMemberVariableName(serializableItem.MemberInfo) : null; var serializableItemInfo = serializableItemInfos[serializableItem.Type]; serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldfld, serializableItemInfo.SerializerField.MakeGeneric(genericParameters))); var fieldReference = serializableItem.MemberInfo is FieldReference?assembly.MainModule.ImportReference((FieldReference)serializableItem.MemberInfo).MakeGeneric(genericParameters) : null; if (memberVariableName != null) { // Use a temporary variable if (!localsByTypes.TryGetValue(serializableItemInfo.Type, out var tempLocal)) { tempLocal = new VariableDefinition(serializableItemInfo.Type); localsByTypes.Add(serializableItemInfo.Type, tempLocal); serialize.Body.Variables.Add(tempLocal); serialize.Body.InitLocals = true; } if (!(archiveMode == ArchiveMode.Deserialize && memberAssignBack)) { // obj.Member serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1)); if (!type.IsValueType) { serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldind_Ref)); } if (serializableItem.MemberInfo is PropertyDefinition property) { var getMethod = property.Resolve().GetMethod; serialize.Body.Instructions.Add(Instruction.Create(getMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, assembly.MainModule.ImportReference(getMethod).MakeGeneric(genericParameters))); } else if (serializableItem.MemberInfo is FieldDefinition) { serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldfld, fieldReference)); } serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Stloc, tempLocal)); serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloca, tempLocal)); } else { // default(T) serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloca, tempLocal)); serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Dup)); serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Initobj, serializableItemInfo.Type)); } } else { // Use object directly serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1)); if (!type.IsValueType) { serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldind_Ref)); } serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldflda, fieldReference)); } serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_2)); serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_3)); serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Callvirt, dataSerializerSerializeMethodRef.MakeGeneric(serializableItemInfo.Type))); if (archiveMode == ArchiveMode.Deserialize && memberVariableName != null && memberAssignBack) { // Need to copy back to object serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1)); if (!type.IsValueType) { serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldind_Ref)); } serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc, localsByTypes[serializableItemInfo.Type])); if (serializableItem.MemberInfo is PropertyDefinition property) { var setMethod = property.Resolve().SetMethod; serialize.Body.Instructions.Add(Instruction.Create(setMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, assembly.MainModule.ImportReference(setMethod).MakeGeneric(genericParameters))); } else if (serializableItem.MemberInfo is FieldDefinition) { serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Stfld, fieldReference)); } } } if (archiveMode == ArchiveMode.Serialize) { serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Br, blockStartInstructions[1])); } } serialize.Body.Instructions.Add(blockStartInstructions[1]); } serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); serializerType.Methods.Add(serialize); //assembly.MainModule.Types.Add(serializerType); } var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assembly); var reflectionAssembly = CecilExtensions.FindReflectionAssembly(assembly); // String var stringType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(string).FullName); var stringTypeRef = assembly.MainModule.ImportReference(stringType); // Type var typeType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(Type).FullName); var typeTypeRef = assembly.MainModule.ImportReference(typeType); var getTypeFromHandleMethod = typeType.Methods.First(x => x.Name == nameof(Type.GetTypeFromHandle)); var getTokenInfoExMethod = reflectionAssembly.MainModule.GetTypeResolved("System.Reflection.IntrospectionExtensions").Resolve().Methods.First(x => x.Name == nameof(IntrospectionExtensions.GetTypeInfo)); var typeInfoType = reflectionAssembly.MainModule.GetTypeResolved(typeof(TypeInfo).FullName); // NOTE: TypeInfo.Assembly/Module could be on the type itself or on its parent MemberInfo depending on runtime var getTypeInfoAssembly = typeInfoType.Properties.Concat(typeInfoType.BaseType.Resolve().Properties).First(x => x.Name == nameof(TypeInfo.Assembly)).GetMethod; var getTypeInfoModule = typeInfoType.Properties.Concat(typeInfoType.BaseType.Resolve().Properties).First(x => x.Name == nameof(TypeInfo.Module)).GetMethod; var typeHandleProperty = typeType.Properties.First(x => x.Name == nameof(Type.TypeHandle)); var getTypeHandleMethodRef = assembly.MainModule.ImportReference(typeHandleProperty.GetMethod); // Generate code var serializerFactoryType = new TypeDefinition("Stride.Core.DataSerializers", Utilities.BuildValidClassName(assembly.Name.Name) + "SerializerFactory", TypeAttributes.BeforeFieldInit | TypeAttributes.AnsiClass | TypeAttributes.AutoClass | TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract, assembly.MainModule.TypeSystem.Object); assembly.MainModule.Types.Add(serializerFactoryType); var dataSerializerModeTypeRef = assembly.MainModule.ImportReference(strideCoreModule.GetType("Stride.Core.Serialization.DataSerializerGenericMode")); var dataSerializerGlobalAttribute = strideCoreModule.GetType("Stride.Core.Serialization.DataSerializerGlobalAttribute"); var dataSerializerGlobalCtorRef = assembly.MainModule.ImportReference(dataSerializerGlobalAttribute.GetConstructors().Single(x => !x.IsStatic && x.Parameters.Count == 5)); foreach (var profile in registry.Context.SerializableTypesProfiles) { foreach (var type in profile.Value.SerializableTypes.Where(x => x.Value.IsLocal)) { // Generating: [DataSerializerGlobalAttribute(<#= type.Value.SerializerType != null ? $"typeof({type.Value.SerializerType.ConvertCSharp(false)})" : "null" #>, typeof(<#= type.Key.ConvertCSharp(false) #>), DataSerializerGenericMode.<#= type.Value.Mode.ToString() #>, <#=type.Value.Inherited ? "true" : "false"#>, <#=type.Value.ComplexSerializer ? "true" : "false"#>, Profile = "<#=profile.Key#>")] serializerFactoryType.CustomAttributes.Add(new CustomAttribute(dataSerializerGlobalCtorRef) { ConstructorArguments = { new CustomAttributeArgument(typeTypeRef, type.Value.SerializerType != null ? assembly.MainModule.ImportReference(type.Value.SerializerType) : null), new CustomAttributeArgument(typeTypeRef, assembly.MainModule.ImportReference(type.Key)), new CustomAttributeArgument(dataSerializerModeTypeRef, type.Value.GenericsMode), new CustomAttributeArgument(assembly.MainModule.TypeSystem.Boolean, type.Value.IsInherited), new CustomAttributeArgument(assembly.MainModule.TypeSystem.Boolean, type.Value.IsComplexSerializer), }, Properties = { new CustomAttributeNamedArgument("Profile", new CustomAttributeArgument(assembly.MainModule.TypeSystem.String, profile.Key)) }, }); } foreach (var type in profile.Value.GenericSerializableTypes.Where(x => x.Value.IsLocal)) { // Generating: [DataSerializerGlobalAttribute(<#= type.Value.SerializerType != null ? $"typeof({type.Value.SerializerType.ConvertCSharp(true)})" : "null" #>, typeof(<#= type.Key.ConvertCSharp(true) #>), DataSerializerGenericMode.<#= type.Value.Mode.ToString() #>, <#=type.Value.Inherited ? "true" : "false"#>, <#=type.Value.ComplexSerializer ? "true" : "false"#>, Profile = "<#=profile.Key#>")] serializerFactoryType.CustomAttributes.Add(new CustomAttribute(dataSerializerGlobalCtorRef) { ConstructorArguments = { new CustomAttributeArgument(typeTypeRef, type.Value.SerializerType != null ? assembly.MainModule.ImportReference(type.Value.SerializerType) : null), new CustomAttributeArgument(typeTypeRef, assembly.MainModule.ImportReference(type.Key)), new CustomAttributeArgument(dataSerializerModeTypeRef, type.Value.GenericsMode), new CustomAttributeArgument(assembly.MainModule.TypeSystem.Boolean, type.Value.IsInherited), new CustomAttributeArgument(assembly.MainModule.TypeSystem.Boolean, type.Value.IsComplexSerializer), }, Properties = { new CustomAttributeNamedArgument("Profile", new CustomAttributeArgument(assembly.MainModule.TypeSystem.String, profile.Key)) }, }); } } // Create Initialize method var initializeMethod = new MethodDefinition("Initialize", MethodAttributes.Assembly | MethodAttributes.HideBySig | MethodAttributes.Static, assembly.MainModule.TypeSystem.Void); serializerFactoryType.Methods.Add(initializeMethod); // Make sure it is called at module startup initializeMethod.AddModuleInitializer(-1000); var initializeMethodIL = initializeMethod.Body.GetILProcessor(); // Generating: var assemblySerializers = new AssemblySerializers(typeof(<#=registry.ClassName#>).GetTypeInfo().Assembly); var assemblySerializersType = strideCoreModule.GetType("Stride.Core.Serialization.AssemblySerializers"); var assemblySerializersGetDataContractAliasesRef = assembly.MainModule.ImportReference(assemblySerializersType.Properties.First(x => x.Name == "DataContractAliases").GetMethod); var assemblySerializersGetDataContractAliasesAdd = assemblySerializersGetDataContractAliasesRef.ReturnType.Resolve().Methods.First(x => x.Name == "Add"); var dataContractAliasTypeRef = ((GenericInstanceType)assemblySerializersGetDataContractAliasesRef.ReturnType).GenericArguments[0]; var dataContractAliasTypeCtorRef = assembly.MainModule.ImportReference(dataContractAliasTypeRef.Resolve().GetConstructors().Single()); var assemblySerializersGetDataContractAliasesAddRef = assembly.MainModule.ImportReference(assemblySerializersGetDataContractAliasesAdd).MakeGeneric(dataContractAliasTypeRef); initializeMethodIL.Emit(OpCodes.Ldtoken, serializerFactoryType); initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod)); initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTokenInfoExMethod)); initializeMethodIL.Emit(OpCodes.Callvirt, assembly.MainModule.ImportReference(getTypeInfoAssembly)); initializeMethodIL.Emit(OpCodes.Newobj, assembly.MainModule.ImportReference(assemblySerializersType.Methods.Single(x => x.IsConstructor && x.Parameters.Count == 1))); foreach (var alias in registry.Context.DataContractAliases) { initializeMethodIL.Emit(OpCodes.Dup); // Generating: assemblySerializers.DataContractAliases.Add(new AssemblySerializers.DataContractAlias(@"<#= alias.Item1 #>", typeof(<#= alias.Item2.ConvertCSharp(true) #>), <#=alias.Item3 ? "true" : "false"#>)); initializeMethodIL.Emit(OpCodes.Call, assemblySerializersGetDataContractAliasesRef); initializeMethodIL.Emit(OpCodes.Ldstr, alias.Item1); initializeMethodIL.Emit(OpCodes.Ldtoken, assembly.MainModule.ImportReference(alias.Item2)); initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod)); initializeMethodIL.Emit(alias.Item3 ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); initializeMethodIL.Emit(OpCodes.Newobj, dataContractAliasTypeCtorRef); initializeMethodIL.Emit(OpCodes.Call, assemblySerializersGetDataContractAliasesAddRef); } var assemblySerializersGetModulesRef = assembly.MainModule.ImportReference(assemblySerializersType.Properties.First(x => x.Name == "Modules").GetMethod); var assemblySerializersGetModulesAdd = assemblySerializersGetModulesRef.ReturnType.Resolve().Methods.First(x => x.Name == "Add"); var moduleRef = ((GenericInstanceType)assemblySerializersGetModulesRef.ReturnType).GenericArguments[0]; var assemblySerializersGetModulesAddRef = assembly.MainModule.ImportReference(assemblySerializersGetModulesAdd).MakeGeneric(moduleRef); foreach (var referencedAssemblySerializerFactoryType in registry.ReferencedAssemblySerializerFactoryTypes) { initializeMethodIL.Emit(OpCodes.Dup); // Generating: assemblySerializers.Modules.Add(typeof(<#=referencedAssemblySerializerFactoryType.ConvertCSharp()#>).GetTypeInfo().Module); initializeMethodIL.Emit(OpCodes.Call, assemblySerializersGetModulesRef); initializeMethodIL.Emit(OpCodes.Ldtoken, assembly.MainModule.ImportReference(referencedAssemblySerializerFactoryType)); initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod)); initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTokenInfoExMethod)); initializeMethodIL.Emit(OpCodes.Callvirt, assembly.MainModule.ImportReference(getTypeInfoModule)); initializeMethodIL.Emit(OpCodes.Call, assemblySerializersGetModulesAddRef); } var objectIdCtorRef = assembly.MainModule.ImportReference(strideCoreModule.GetType("Stride.Core.Storage.ObjectId").GetConstructors().Single(x => x.Parameters.Count == 4)); var serializerEntryTypeCtorRef = assembly.MainModule.ImportReference(strideCoreModule.GetType("Stride.Core.Serialization.AssemblySerializerEntry").GetConstructors().Single()); var assemblySerializersPerProfileType = strideCoreModule.GetType("Stride.Core.Serialization.AssemblySerializersPerProfile"); var assemblySerializersPerProfileTypeAddRef = assembly.MainModule.ImportReference(assemblySerializersPerProfileType.BaseType.Resolve().Methods.First(x => x.Name == "Add")).MakeGeneric(serializerEntryTypeCtorRef.DeclaringType); var assemblySerializersPerProfileTypeCtorRef = assembly.MainModule.ImportReference(assemblySerializersPerProfileType.GetEmptyConstructor()); var assemblySerializersGetProfilesRef = assembly.MainModule.ImportReference(assemblySerializersType.Properties.First(x => x.Name == "Profiles").GetMethod); var assemblySerializersGetProfilesSetItemRef = assembly.MainModule.ImportReference(assemblySerializersGetProfilesRef.ReturnType.Resolve().Methods.First(x => x.Name == "set_Item")) .MakeGeneric(((GenericInstanceType)assemblySerializersGetProfilesRef.ReturnType).GenericArguments.ToArray()); var runtimeHelpersType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(RuntimeHelpers).FullName); var runClassConstructorMethod = assembly.MainModule.ImportReference(runtimeHelpersType.Methods.Single(x => x.IsPublic && x.Name == "RunClassConstructor" && x.Parameters.Count == 1 && x.Parameters[0].ParameterType.FullName == typeof(RuntimeTypeHandle).FullName)); foreach (var profile in registry.Context.SerializableTypesProfiles) { initializeMethodIL.Emit(OpCodes.Dup); // Generating: var assemblySerializersProfile = new AssemblySerializersPerProfile(); // Generating: assemblySerializers.Profiles["<#=profile.Key#>"] = assemblySerializersProfile; initializeMethodIL.Emit(OpCodes.Callvirt, assemblySerializersGetProfilesRef); initializeMethodIL.Emit(OpCodes.Ldstr, profile.Key); initializeMethodIL.Emit(OpCodes.Newobj, assemblySerializersPerProfileTypeCtorRef); foreach (var type in profile.Value.SerializableTypes.Where(x => x.Value.IsLocal)) { // Generating: assemblySerializersProfile.Add(new AssemblySerializerEntry(<#=type.Key.ConvertTypeId()#>, typeof(<#= type.Key.ConvertCSharp() #>), <# if (type.Value.SerializerType != null) { #>typeof(<#= type.Value.SerializerType.ConvertCSharp() #>)<# } else { #>null<# } #>)); initializeMethodIL.Emit(OpCodes.Dup); var typeName = type.Key.ConvertToValidCSharp(false); var typeId = ObjectId.FromBytes(Encoding.UTF8.GetBytes(typeName)); unsafe { var typeIdHash = (int *)&typeId; for (int i = 0; i < ObjectId.HashSize / 4; ++i) { initializeMethodIL.Emit(OpCodes.Ldc_I4, typeIdHash[i]); } } initializeMethodIL.Emit(OpCodes.Newobj, objectIdCtorRef); initializeMethodIL.Emit(OpCodes.Ldtoken, assembly.MainModule.ImportReference(type.Key)); initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod)); if (type.Value.SerializerType != null) { initializeMethodIL.Emit(OpCodes.Ldtoken, assembly.MainModule.ImportReference(type.Value.SerializerType)); initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod)); } else { initializeMethodIL.Emit(OpCodes.Ldnull); } initializeMethodIL.Emit(OpCodes.Newobj, serializerEntryTypeCtorRef); initializeMethodIL.Emit(OpCodes.Callvirt, assemblySerializersPerProfileTypeAddRef); if (type.Value.SerializerType?.Resolve()?.Methods.Any(x => x.IsConstructor && x.IsStatic) == true) { // Generating: System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(<#=type.Value.SerializerType.ConvertCSharp()#>).TypeHandle); initializeMethodIL.Append(Instruction.Create(OpCodes.Ldtoken, type.Value.SerializerType)); initializeMethodIL.Append(Instruction.Create(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod))); initializeMethodIL.Append(Instruction.Create(OpCodes.Callvirt, getTypeHandleMethodRef)); initializeMethodIL.Append(Instruction.Create(OpCodes.Call, runClassConstructorMethod)); } } initializeMethodIL.Emit(OpCodes.Callvirt, assemblySerializersGetProfilesSetItemRef); } // Generating: DataSerializerFactory.RegisterSerializationAssembly(assemblySerializers); var dataSerializerFactoryRegisterSerializationAssemblyMethodRef = assembly.MainModule.ImportReference(strideCoreModule.GetType("Stride.Core.Serialization.DataSerializerFactory").Methods.Single(x => x.Name == "RegisterSerializationAssembly" && x.Parameters[0].ParameterType.FullName == assemblySerializersType.FullName)); initializeMethodIL.Emit(OpCodes.Call, dataSerializerFactoryRegisterSerializationAssemblyMethodRef); // Generating: AssemblyRegistry.Register(typeof(<#=registry.ClassName#>).GetTypeInfo().Assembly, AssemblyCommonCategories.Engine); initializeMethodIL.Emit(OpCodes.Ldtoken, serializerFactoryType); initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod)); initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTokenInfoExMethod)); initializeMethodIL.Emit(OpCodes.Callvirt, assembly.MainModule.ImportReference(getTypeInfoAssembly)); // create new[] { AssemblyCommonCategories.Engine } initializeMethodIL.Emit(OpCodes.Ldc_I4_1); initializeMethodIL.Emit(OpCodes.Newarr, assembly.MainModule.TypeSystem.String); initializeMethodIL.Emit(OpCodes.Dup); initializeMethodIL.Emit(OpCodes.Ldc_I4_0); initializeMethodIL.Emit(OpCodes.Ldstr, Core.Reflection.AssemblyCommonCategories.Engine); initializeMethodIL.Emit(OpCodes.Stelem_Ref); var assemblyRegistryRegisterMethodRef = assembly.MainModule.ImportReference(strideCoreModule.GetType("Stride.Core.Reflection.AssemblyRegistry").Methods.Single(x => x.Name == "Register" && x.Parameters[1].ParameterType.IsArray)); initializeMethodIL.Emit(OpCodes.Call, assemblyRegistryRegisterMethodRef); initializeMethodIL.Emit(OpCodes.Ret); // Add AssemblySerializerFactoryAttribute var assemblySerializerFactoryAttribute = strideCoreModule.GetType("Stride.Core.Serialization.AssemblySerializerFactoryAttribute"); assembly.CustomAttributes.Add(new CustomAttribute(assembly.MainModule.ImportReference(assemblySerializerFactoryAttribute.GetEmptyConstructor())) { Fields = { new CustomAttributeNamedArgument("Type", new CustomAttributeArgument(typeTypeRef, serializerFactoryType)), } }); serializationHash = hash.ComputeHash(); }
private static MethodDefinition InflateMethod(TypeDefinition inflatedType, MethodDefinition method, ResolveGenericsVisitor resolveGenericsVisitor) { var clonedMethod = new MethodDefinition(method.Name, method.Attributes, inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(method.ReturnType))); clonedMethod.Parameters.AddRange( method.Parameters.Select(x => new ParameterDefinition(x.Name, x.Attributes, inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(x.ParameterType))))); if (method.Body != null) { clonedMethod.Body.Variables.AddRange( method.Body.Variables.Select(x => new VariableDefinition(inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(x.VariableType))))); clonedMethod.Body.InitLocals = method.Body.InitLocals; var mappedInstructions = new Dictionary <Instruction, Instruction>(); foreach (var instruction in method.Body.Instructions) { // Create nop instructions to start with (if we use actual opcode, it would do an operand check) var mappedInstruction = Instruction.Create(OpCodes.Nop); mappedInstruction.OpCode = instruction.OpCode; mappedInstruction.Operand = instruction.Operand; mappedInstructions[instruction] = mappedInstruction; } foreach (var instruction in method.Body.Instructions) { // Fix operand var mappedInstruction = mappedInstructions[instruction]; if (mappedInstruction.Operand is Instruction) { mappedInstruction.Operand = mappedInstructions[(Instruction)instruction.Operand]; } else if (mappedInstruction.Operand is ParameterDefinition) { var parameterIndex = method.Parameters.IndexOf((ParameterDefinition)instruction.Operand); mappedInstruction.Operand = clonedMethod.Parameters[parameterIndex]; } else if (mappedInstruction.Operand is VariableDefinition) { var variableIndex = method.Body.Variables.IndexOf((VariableDefinition)instruction.Operand); mappedInstruction.Operand = clonedMethod.Body.Variables[variableIndex]; } else if (mappedInstruction.Operand is TypeReference) { var newTypeReference = resolveGenericsVisitor.VisitDynamic((TypeReference)mappedInstruction.Operand); newTypeReference = inflatedType.Module.ImportReference(newTypeReference); mappedInstruction.Operand = newTypeReference; } else if (mappedInstruction.Operand is FieldReference) { var fieldReference = (FieldReference)mappedInstruction.Operand; var newFieldReference = new FieldReference(fieldReference.Name, inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(fieldReference.FieldType)), inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(fieldReference.DeclaringType))); mappedInstruction.Operand = newFieldReference; } else if (mappedInstruction.Operand is MethodReference) { var methodReference = (MethodReference)mappedInstruction.Operand; var genericInstanceMethod = methodReference as GenericInstanceMethod; if (genericInstanceMethod != null) { methodReference = genericInstanceMethod.ElementMethod; } methodReference = methodReference.GetElementMethod(); var newMethodReference = new MethodReference(methodReference.Name, inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(methodReference.ReturnType)), inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(methodReference.DeclaringType))) { HasThis = methodReference.HasThis, ExplicitThis = methodReference.ExplicitThis, CallingConvention = methodReference.CallingConvention, }; foreach (var parameter in methodReference.Parameters) { newMethodReference.Parameters.Add(new ParameterDefinition(inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(parameter.ParameterType)))); } if (methodReference.HasGenericParameters) { CopyGenericParameters(methodReference, newMethodReference); } if (genericInstanceMethod != null) { newMethodReference = newMethodReference.MakeGenericMethod(genericInstanceMethod.GenericArguments.Select(x => inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(x))).ToArray()); } mappedInstruction.Operand = newMethodReference; } else if (mappedInstruction.Operand is Mono.Cecil.CallSite) { var callSite = (Mono.Cecil.CallSite)mappedInstruction.Operand; var newCallSite = new Mono.Cecil.CallSite(inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(callSite.ReturnType))) { HasThis = callSite.HasThis, ExplicitThis = callSite.ExplicitThis, CallingConvention = callSite.CallingConvention, }; foreach (var parameter in callSite.Parameters) { newCallSite.Parameters.Add(new ParameterDefinition(inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(parameter.ParameterType)))); } mappedInstruction.Operand = newCallSite; } else if (mappedInstruction.Operand is Instruction[]) { // Not used in UpdatableProperty<T> throw new NotImplementedException(); } } clonedMethod.Body.Instructions.AddRange(method.Body.Instructions.Select(x => mappedInstructions[x])); } return(clonedMethod); }
public void ProcessSerializers(CecilSerializerContext context) { var references = new HashSet <AssemblyDefinition>(); EnumerateReferences(references, context.Assembly); var coreAssembly = CecilExtensions.FindCorlibAssembly(context.Assembly); // Only process assemblies depending on Stride.Engine if (!references.Any(x => x.Name.Name == "Stride.Engine")) { // Make sure Stride.Engine.Serializers can access everything internally var internalsVisibleToAttribute = coreAssembly.MainModule.GetTypeResolved(typeof(InternalsVisibleToAttribute).FullName); var serializationAssemblyName = "Stride.Engine.Serializers"; // Add [InteralsVisibleTo] attribute var internalsVisibleToAttributeCtor = context.Assembly.MainModule.ImportReference(internalsVisibleToAttribute.GetConstructors().Single()); var internalsVisibleAttribute = new CustomAttribute(internalsVisibleToAttributeCtor) { ConstructorArguments = { new CustomAttributeArgument(context.Assembly.MainModule.ImportReference(context.Assembly.MainModule.TypeSystem.String), serializationAssemblyName) } }; context.Assembly.CustomAttributes.Add(internalsVisibleAttribute); return; } var strideEngineAssembly = context.Assembly.Name.Name == "Stride.Engine" ? context.Assembly : context.Assembly.MainModule.AssemblyResolver.Resolve(new AssemblyNameReference("Stride.Engine", null)); var strideEngineModule = strideEngineAssembly.MainModule; // Generate IL for Stride.Core if (context.Assembly.Name.Name == "Stride.Engine") { ProcessStrideEngineAssembly(context); } var updatableFieldGenericType = strideEngineModule.GetType("Stride.Updater.UpdatableField`1"); updatableFieldGenericCtor = updatableFieldGenericType.Methods.First(x => x.IsConstructor && !x.IsStatic); updatablePropertyGenericType = strideEngineModule.GetType("Stride.Updater.UpdatableProperty`1"); updatablePropertyGenericCtor = updatablePropertyGenericType.Methods.First(x => x.IsConstructor && !x.IsStatic); var updatablePropertyObjectGenericType = strideEngineModule.GetType("Stride.Updater.UpdatablePropertyObject`1"); updatablePropertyObjectGenericCtor = updatablePropertyObjectGenericType.Methods.First(x => x.IsConstructor && !x.IsStatic); var updatableListUpdateResolverGenericType = strideEngineModule.GetType("Stride.Updater.ListUpdateResolver`1"); updatableListUpdateResolverGenericCtor = updatableListUpdateResolverGenericType.Methods.First(x => x.IsConstructor && !x.IsStatic); var updatableArrayUpdateResolverGenericType = strideEngineModule.GetType("Stride.Updater.ArrayUpdateResolver`1"); updatableArrayUpdateResolverGenericCtor = updatableArrayUpdateResolverGenericType.Methods.First(x => x.IsConstructor && !x.IsStatic); var parameterCollectionResolver = strideEngineModule.GetType("Stride.Engine.Design.ParameterCollectionResolver"); parameterCollectionResolverInstantiateValueAccessor = parameterCollectionResolver.Methods.First(x => x.Name == "InstantiateValueAccessor"); var registerMemberMethod = strideEngineModule.GetType("Stride.Updater.UpdateEngine").Methods.First(x => x.Name == "RegisterMember"); updateEngineRegisterMemberMethod = context.Assembly.MainModule.ImportReference(registerMemberMethod); var registerMemberResolverMethod = strideEngineModule.GetType("Stride.Updater.UpdateEngine").Methods.First(x => x.Name == "RegisterMemberResolver"); //pclVisitor.VisitMethod(registerMemberResolverMethod); updateEngineRegisterMemberResolverMethod = context.Assembly.MainModule.ImportReference(registerMemberResolverMethod); var typeType = coreAssembly.MainModule.GetTypeResolved(typeof(Type).FullName); getTypeFromHandleMethod = context.Assembly.MainModule.ImportReference(typeType.Methods.First(x => x.Name == "GetTypeFromHandle")); var mainPrepareMethod = CreateUpdateMethod(context.Assembly); // Emit serialization code for all the types we care about var processedTypes = new HashSet <TypeDefinition>(TypeReferenceEqualityComparer.Default); foreach (var serializableType in context.SerializableTypesProfiles.SelectMany(x => x.Value.SerializableTypes)) { // Special case: when processing Stride.Engine assembly, we automatically add dependent assemblies types too if (!serializableType.Value.Local && strideEngineAssembly != context.Assembly) { continue; } var typeDefinition = serializableType.Key as TypeDefinition; if (typeDefinition == null) { continue; } // Ignore already processed types if (!processedTypes.Add(typeDefinition)) { continue; } try { ProcessType(context, context.Assembly.MainModule.ImportReference(typeDefinition), mainPrepareMethod); } catch (Exception e) { throw new InvalidOperationException(string.Format("Error when generating update engine code for {0}", typeDefinition), e); } } // Force generic instantiations var il = mainPrepareMethod.Body.GetILProcessor(); foreach (var serializableType in context.SerializableTypesProfiles.SelectMany(x => x.Value.SerializableTypes).ToArray()) { // Special case: when processing Stride.Engine assembly, we automatically add dependent assemblies types too if (!serializableType.Value.Local && strideEngineAssembly != context.Assembly) { continue; } // Try to find if original method definition was generated var typeDefinition = serializableType.Key.Resolve(); // If using List<T>, register this type in UpdateEngine var parentTypeDefinition = typeDefinition; while (parentTypeDefinition != null) { var listInterfaceType = parentTypeDefinition.Interfaces.Select(x => x.InterfaceType).OfType <GenericInstanceType>().FirstOrDefault(x => x.ElementType.FullName == typeof(IList <>).FullName); if (listInterfaceType != null) { //call Updater.UpdateEngine.RegisterMemberResolver(new Updater.ListUpdateResolver<T>()); var elementType = ResolveGenericsVisitor.Process(serializableType.Key, listInterfaceType.GenericArguments[0]); il.Emit(OpCodes.Newobj, context.Assembly.MainModule.ImportReference(updatableListUpdateResolverGenericCtor).MakeGeneric(context.Assembly.MainModule.ImportReference(elementType))); il.Emit(OpCodes.Call, updateEngineRegisterMemberResolverMethod); } parentTypeDefinition = parentTypeDefinition.BaseType?.Resolve(); } // Same for arrays var arrayType = serializableType.Key as ArrayType; if (arrayType != null) { //call Updater.UpdateEngine.RegisterMemberResolver(new Updater.ArrayUpdateResolver<T>()); var elementType = ResolveGenericsVisitor.Process(serializableType.Key, arrayType.ElementType); il.Emit(OpCodes.Newobj, context.Assembly.MainModule.ImportReference(updatableArrayUpdateResolverGenericCtor).MakeGeneric(context.Assembly.MainModule.ImportReference(elementType))); il.Emit(OpCodes.Call, updateEngineRegisterMemberResolverMethod); } // Generic instantiation for AOT platforms if (context.Platform == Core.PlatformType.iOS && serializableType.Key.Name == "ValueParameterKey`1") { var keyType = ((GenericInstanceType)serializableType.Key).GenericArguments[0]; il.Emit(OpCodes.Call, context.Assembly.MainModule.ImportReference(parameterCollectionResolverInstantiateValueAccessor).MakeGenericMethod(context.Assembly.MainModule.ImportReference(keyType))); } var genericInstanceType = serializableType.Key as GenericInstanceType; if (genericInstanceType != null) { var expectedUpdateMethodName = ComputeUpdateMethodName(typeDefinition); var updateMethod = GetOrCreateUpdateType(typeDefinition.Module.Assembly, false)?.Methods.FirstOrDefault(x => x.Name == expectedUpdateMethodName && x.HasGenericParameters && x.GenericParameters.Count == genericInstanceType.GenericParameters.Count); // If nothing was found in main assembly, also look in Stride.Engine assembly, just in case (it might defines some shared/corlib types -- currently not the case) if (updateMethod == null) { updateMethod = GetOrCreateUpdateType(strideEngineAssembly, false)?.Methods.FirstOrDefault(x => x.Name == expectedUpdateMethodName && x.HasGenericParameters && x.GenericParameters.Count == genericInstanceType.GenericParameters.Count); } if (updateMethod != null) { // Emit call to update engine setup method with generic arguments of current type il.Emit(OpCodes.Call, context.Assembly.MainModule.ImportReference(updateMethod) .MakeGenericMethod(genericInstanceType.GenericArguments .Select(context.Assembly.MainModule.ImportReference) .ToArray())); } } } il.Emit(OpCodes.Ret); }
public void ProcessType(CecilSerializerContext context, TypeReference type, MethodDefinition updateMainMethod) { var typeDefinition = type.Resolve(); // No need to process enum if (typeDefinition.IsEnum) { return; } var updateCurrentMethod = updateMainMethod; ResolveGenericsVisitor replaceGenericsVisitor = null; if (typeDefinition.HasGenericParameters) { // Make a prepare method for just this object since it might need multiple instantiation updateCurrentMethod = new MethodDefinition(ComputeUpdateMethodName(typeDefinition), MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static, context.Assembly.MainModule.TypeSystem.Void); var genericsMapping = new Dictionary <TypeReference, TypeReference>(); foreach (var genericParameter in typeDefinition.GenericParameters) { var genericParameterCopy = new GenericParameter(genericParameter.Name, updateCurrentMethod) { Attributes = genericParameter.Attributes, }; foreach (var constraint in genericParameter.Constraints) { genericParameterCopy.Constraints.Add(new GenericParameterConstraint(context.Assembly.MainModule.ImportReference(constraint.ConstraintType))); } updateCurrentMethod.GenericParameters.Add(genericParameterCopy); genericsMapping[genericParameter] = genericParameterCopy; } replaceGenericsVisitor = new ResolveGenericsVisitor(genericsMapping); updateMainMethod.DeclaringType.Methods.Add(updateCurrentMethod); } var il = updateCurrentMethod.Body.GetILProcessor(); var typeIsValueType = type.IsResolvedValueType(); var emptyObjectField = typeIsValueType ? null : updateMainMethod.DeclaringType.Fields.FirstOrDefault(x => x.Name == "emptyObject"); VariableDefinition emptyStruct = null; // Note: forcing fields and properties to be processed in all cases foreach (var serializableItem in ComplexSerializerRegistry.GetSerializableItems(type, true, ComplexTypeSerializerFlags.SerializePublicFields | ComplexTypeSerializerFlags.SerializePublicProperties | ComplexTypeSerializerFlags.Updatable)) { if (serializableItem.MemberInfo is FieldReference fieldReference) { var field = fieldReference.Resolve(); // First time it is needed, let's create empty object in the class (var emptyObject = new object()) or empty local struct in the method if (typeIsValueType) { if (emptyStruct == null) { emptyStruct = new VariableDefinition(type); updateMainMethod.Body.Variables.Add(emptyStruct); } } else { if (emptyObjectField == null) { emptyObjectField = new FieldDefinition("emptyObject", FieldAttributes.Static | FieldAttributes.Private, context.Assembly.MainModule.TypeSystem.Object); // Create static ctor that will initialize this object var staticConstructor = new MethodDefinition(".cctor", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, context.Assembly.MainModule.TypeSystem.Void); var staticConstructorIL = staticConstructor.Body.GetILProcessor(); staticConstructorIL.Emit(OpCodes.Newobj, context.Assembly.MainModule.ImportReference(emptyObjectField.FieldType.Resolve().GetConstructors().Single(x => !x.IsStatic && !x.HasParameters))); staticConstructorIL.Emit(OpCodes.Stsfld, emptyObjectField); staticConstructorIL.Emit(OpCodes.Ret); updateMainMethod.DeclaringType.Fields.Add(emptyObjectField); updateMainMethod.DeclaringType.Methods.Add(staticConstructor); } } il.Emit(OpCodes.Ldtoken, type); il.Emit(OpCodes.Call, getTypeFromHandleMethod); il.Emit(OpCodes.Ldstr, field.Name); if (typeIsValueType) { il.Emit(OpCodes.Ldloca, emptyStruct); } else { il.Emit(OpCodes.Ldsfld, emptyObjectField); } il.Emit(OpCodes.Ldflda, context.Assembly.MainModule.ImportReference(fieldReference)); il.Emit(OpCodes.Conv_I); if (typeIsValueType) { il.Emit(OpCodes.Ldloca, emptyStruct); } else { il.Emit(OpCodes.Ldsfld, emptyObjectField); } il.Emit(OpCodes.Conv_I); il.Emit(OpCodes.Sub); il.Emit(OpCodes.Conv_I4); var fieldType = context.Assembly.MainModule.ImportReference(replaceGenericsVisitor != null ? replaceGenericsVisitor.VisitDynamic(field.FieldType) : field.FieldType); il.Emit(OpCodes.Newobj, context.Assembly.MainModule.ImportReference(updatableFieldGenericCtor).MakeGeneric(fieldType)); il.Emit(OpCodes.Call, updateEngineRegisterMemberMethod); } var propertyReference = serializableItem.MemberInfo as PropertyReference; if (propertyReference != null) { var property = propertyReference.Resolve(); var propertyGetMethod = context.Assembly.MainModule.ImportReference(property.GetMethod).MakeGeneric(updateCurrentMethod.GenericParameters.ToArray()); il.Emit(OpCodes.Ldtoken, type); il.Emit(OpCodes.Call, getTypeFromHandleMethod); il.Emit(OpCodes.Ldstr, property.Name); // If it's a virtual or interface call, we need to create a dispatcher using ldvirtftn if (property.GetMethod.IsVirtual) { propertyGetMethod = CreateDispatcher(context.Assembly, propertyGetMethod); } il.Emit(OpCodes.Ldftn, propertyGetMethod); // Set whether getter method uses a VirtualDispatch (static call) or instance call il.Emit(property.GetMethod.IsVirtual ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); // Only uses setter if it exists and it's public if (property.SetMethod != null && property.SetMethod.IsPublic) { var propertySetMethod = context.Assembly.MainModule.ImportReference(property.SetMethod).MakeGeneric(updateCurrentMethod.GenericParameters.ToArray()); if (property.SetMethod.IsVirtual) { propertySetMethod = CreateDispatcher(context.Assembly, propertySetMethod); } il.Emit(OpCodes.Ldftn, propertySetMethod); } else { // 0 (native int) il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Conv_I); } // Set whether setter method uses a VirtualDispatch (static call) or instance call il.Emit((property.SetMethod?.IsVirtual ?? false) ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); var propertyType = context.Assembly.MainModule.ImportReference(replaceGenericsVisitor != null ? replaceGenericsVisitor.VisitDynamic(property.PropertyType) : property.PropertyType); var updatablePropertyInflatedCtor = GetOrCreateUpdatablePropertyCtor(context.Assembly, propertyType); il.Emit(OpCodes.Newobj, updatablePropertyInflatedCtor); il.Emit(OpCodes.Call, updateEngineRegisterMemberMethod); } } if (updateCurrentMethod != updateMainMethod) { // If we have a local method, close it il.Emit(OpCodes.Ret); // Also call it from main method if it was a closed generic instantiation if (type is GenericInstanceType) { il = updateMainMethod.Body.GetILProcessor(); il.Emit(OpCodes.Call, updateCurrentMethod.MakeGeneric(((GenericInstanceType)type).GenericArguments.Select(context.Assembly.MainModule.ImportReference).ToArray())); } } }