Example #1
0
        /// <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);
        }
Example #2
0
        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);
            }
        }
Example #4
0
        /// <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();
        }
Example #5
0
        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);
        }
Example #6
0
        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);
        }
Example #7
0
        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()));
                }
            }
        }