public UpdatableListCodeGenerator(AssemblyDefinition assembly) : base(assembly) { declaringType = assembly.MainModule.GetType("Stride.Updater.UpdatableListAccessor`1"); indexField = assembly.MainModule.GetType("Stride.Updater.UpdatableListAccessor").Fields.First(x => x.Name == "Index"); // TODO: Update to new method to resolve collection assembly var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assembly); var ilistType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(IList <>).FullName); var ilistItem = ilistType.Properties.First(x => x.Name == "Item"); ilistGetItem = assembly.MainModule.ImportReference(ilistItem.GetMethod).MakeGeneric(declaringType.GenericParameters[0]); ilistSetItem = assembly.MainModule.ImportReference(ilistItem.SetMethod).MakeGeneric(declaringType.GenericParameters[0]); }
public bool Process(AssemblyProcessorContext context) { var assembly = context.Assembly; var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assembly); if (mscorlibAssembly == null) { throw new InvalidOperationException("Missing mscorlib.dll from assembly"); } // Resolve mscorlib types var assemblyFileVersionAttributeType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(AssemblyFileVersionAttribute).FullName); var assemblyMethodConstructor = assembly.MainModule.ImportReference(assemblyFileVersionAttributeType.Methods.FirstOrDefault(method => method.IsConstructor && method.Parameters.Count == 1)); var stringType = assembly.MainModule.TypeSystem.String; // TODO: Git Commit SHA var gitCommitShortId = "0"; // Use epoch time to get a "unique" build number (different each time) var build = (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds; // Get current AssemblyVersion and clone it var version = assembly.Name.Version; var fileVersion = string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, build, gitCommitShortId); // Copy build/revision to the AssemblyFileVersion bool fileVersionUpdated = false; for (int i = 0; i < assembly.CustomAttributes.Count; i++) { var customAttribute = assembly.CustomAttributes[i]; if (customAttribute.AttributeType.FullName == typeof(AssemblyFileVersionAttribute).FullName) { customAttribute.ConstructorArguments.Clear(); customAttribute.ConstructorArguments.Add(new CustomAttributeArgument(stringType, fileVersion)); fileVersionUpdated = true; break; } } if (!fileVersionUpdated) { var assemblyFileVersion = new CustomAttribute(assemblyMethodConstructor); assemblyFileVersion.ConstructorArguments.Add(new CustomAttributeArgument(stringType, fileVersion)); assembly.CustomAttributes.Add(assemblyFileVersion); } return(true); }
private void EnsureInitialized(AssemblyProcessorContext context) { if (isInitialized) { return; } var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(context.Assembly); strideCoreModule = context.Assembly.GetStrideCoreModule(); pooledClosureType = context.Assembly.MainModule.ImportReference(strideCoreModule.GetType("Stride.Core.Threading.IPooledClosure")); // Func type and it's contructor var funcType = mscorlibAssembly.MainModule.GetTypeResolved("System.Func`1"); funcConstructor = context.Assembly.MainModule.ImportReference(funcType.Methods.FirstOrDefault(x => x.Name == ".ctor")); // Pool type and it's constructor var poolTypeDefinition = strideCoreModule.GetType("Stride.Core.Threading.ConcurrentPool`1"); poolType = context.Assembly.MainModule.ImportReference(poolTypeDefinition); poolConstructor = context.Assembly.MainModule.ImportReference(poolTypeDefinition.Methods.FirstOrDefault(x => x.Name == ".ctor")); poolAcquireMethod = context.Assembly.MainModule.ImportReference(poolTypeDefinition.Methods.FirstOrDefault(x => x.Name == "Acquire")); poolReleaseMethod = context.Assembly.MainModule.ImportReference(poolTypeDefinition.Methods.FirstOrDefault(x => x.Name == "Release")); // Interlocked (either from mscorlib.dll or System.Threading.dll) var interlockedTypeDefinition = mscorlibAssembly.MainModule.GetTypeResolved("System.Threading.Interlocked"); if (interlockedTypeDefinition == null) { var threadingAssembly = context.AssemblyResolver.Resolve(new AssemblyNameReference("System.Threading", null)); interlockedTypeDefinition = threadingAssembly.MainModule.GetTypeResolved("System.Threading.Interlocked"); } var interlockedType = context.Assembly.MainModule.ImportReference(interlockedTypeDefinition); // NOTE: We create method references manually, because ImportReference imports a different version of the runtime, causing interlockedIncrementMethod = new MethodReference("Increment", context.Assembly.MainModule.TypeSystem.Int32, interlockedType); interlockedIncrementMethod.Parameters.Add(new ParameterDefinition("", ParameterAttributes.None, context.Assembly.MainModule.TypeSystem.Int32.MakeByReferenceType())); //interlockedIncrementMethod = context.Assembly.MainModule.ImportReference(interlockedType.Methods.FirstOrDefault(x => x.Name == "Increment" && x.ReturnType.MetadataType == MetadataType.Int32)); interlockedDecrementMethod = new MethodReference("Decrement", context.Assembly.MainModule.TypeSystem.Int32, interlockedType); interlockedDecrementMethod.Parameters.Add(new ParameterDefinition("", ParameterAttributes.None, context.Assembly.MainModule.TypeSystem.Int32.MakeByReferenceType())); //interlockedDecrementMethod = context.Assembly.MainModule.ImportReference(interlockedType.Methods.FirstOrDefault(x => x.Name == "Decrement" && x.ReturnType.MetadataType == MetadataType.Int32)); isInitialized = true; }
private static void RegisterDefaultSerializationProfile(IAssemblyResolver assemblyResolver, AssemblyDefinition assembly, ComplexSerializerRegistry registry, System.IO.TextWriter log) { var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assembly); if (mscorlibAssembly == null) { log.WriteLine($"Missing mscorlib.dll from assembly {assembly.FullName}"); throw new InvalidOperationException("Missing mscorlib.dll from assembly."); } var coreSerializationAssembly = assemblyResolver.Resolve(new AssemblyNameReference("Stride.Core", null)); // Register serializer factories (determine which type requires which serializer) registry.SerializerFactories.Add(new CecilGenericSerializerFactory(typeof(IList <>), coreSerializationAssembly.MainModule.GetTypeResolved("Stride.Core.Serialization.Serializers.ListInterfaceSerializer`1"))); registry.SerializerFactories.Add(new CecilGenericSerializerFactory(typeof(List <>), coreSerializationAssembly.MainModule.GetTypeResolved("Stride.Core.Serialization.Serializers.ListSerializer`1"))); registry.SerializerFactories.Add(new CecilGenericSerializerFactory(typeof(KeyValuePair <,>), coreSerializationAssembly.MainModule.GetTypeResolved("Stride.Core.Serialization.Serializers.KeyValuePairSerializer`2"))); registry.SerializerFactories.Add(new CecilGenericSerializerFactory(typeof(IDictionary <,>), coreSerializationAssembly.MainModule.GetTypeResolved("Stride.Core.Serialization.Serializers.DictionaryInterfaceSerializer`2"))); registry.SerializerFactories.Add(new CecilGenericSerializerFactory(typeof(Dictionary <,>), coreSerializationAssembly.MainModule.GetTypeResolved("Stride.Core.Serialization.Serializers.DictionarySerializer`2"))); registry.SerializerFactories.Add(new CecilGenericSerializerFactory(typeof(Nullable <>), coreSerializationAssembly.MainModule.GetTypeResolved("Stride.Core.Serialization.Serializers.NullableSerializer`1"))); registry.SerializerFactories.Add(new CecilEnumSerializerFactory(coreSerializationAssembly.MainModule.GetTypeResolved("Stride.Core.Serialization.Serializers.EnumSerializer`1"))); registry.SerializerFactories.Add(new CecilArraySerializerFactory(coreSerializationAssembly.MainModule.GetTypeResolved("Stride.Core.Serialization.Serializers.ArraySerializer`1"))); // Iterate over tuple size for (int i = 1; i <= 4; ++i) { registry.SerializerDependencies.Add(new CecilSerializerDependency( $"System.Tuple`{i}", coreSerializationAssembly.MainModule.GetTypeResolved($"Stride.Core.Serialization.Serializers.TupleSerializer`{i}"))); registry.SerializerDependencies.Add(new CecilSerializerDependency($"Stride.Core.Serialization.Serializers.TupleSerializer`{i}")); } // Register serializer dependencies (determine which serializer serializes which sub-type) registry.SerializerDependencies.Add(new CecilSerializerDependency("Stride.Core.Serialization.Serializers.ArraySerializer`1")); registry.SerializerDependencies.Add(new CecilSerializerDependency("Stride.Core.Serialization.Serializers.KeyValuePairSerializer`2")); registry.SerializerDependencies.Add(new CecilSerializerDependency("Stride.Core.Serialization.Serializers.ListSerializer`1")); registry.SerializerDependencies.Add(new CecilSerializerDependency("Stride.Core.Serialization.Serializers.ListInterfaceSerializer`1")); registry.SerializerDependencies.Add(new CecilSerializerDependency("Stride.Core.Serialization.Serializers.NullableSerializer`1")); registry.SerializerDependencies.Add(new CecilSerializerDependency("Stride.Core.Serialization.Serializers.DictionarySerializer`2", mscorlibAssembly.MainModule.GetTypeResolved(typeof(KeyValuePair <,>).FullName))); registry.SerializerDependencies.Add(new CecilSerializerDependency("Stride.Core.Serialization.Serializers.DictionaryInterfaceSerializer`2", mscorlibAssembly.MainModule.GetTypeResolved(typeof(KeyValuePair <,>).FullName))); }
/// <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(); }
public bool Run(ref AssemblyDefinition assemblyDefinition, out bool modified, out ObjectId?serializationHash) { modified = false; serializationHash = null; try { // Check if there is a AssemblyProcessedAttribute (already processed, we can skip). // Note that we should probably also match the command line as well so that we throw an error if processing is different (need to rebuild). if (assemblyDefinition.HasCustomAttribute("Stride.Core.AssemblyProcessedAttribute")) { OnInfoAction($"Assembly [{assemblyDefinition.Name}] has already been processed. Skipping it."); return(true); } var assemblyResolver = (CustomAssemblyResolver)assemblyDefinition.MainModule.AssemblyResolver; // Register self assemblyResolver.Register(assemblyDefinition); var processors = new List <IAssemblyDefinitionProcessor>(); processors.Add(new AddReferenceProcessor(ReferencesToAdd)); if (ParameterKey) { processors.Add(new ParameterKeyProcessor()); } if (NewAssemblyName != null) { processors.Add(new RenameAssemblyProcessor(NewAssemblyName)); } // Always applies the interop processor processors.Add(new InteropProcessor()); processors.Add(new AssemblyVersionProcessor()); if (DocumentationFile != null) { processors.Add(new GenerateUserDocumentationProcessor(DocumentationFile)); } if (SerializationAssembly) { processors.Add(new AssemblyScanProcessor()); processors.Add(new SerializationProcessor()); } if (ModuleInitializer) { processors.Add(new ModuleInitializerProcessor()); } processors.Add(new InitLocalsProcessor()); processors.Add(new DispatcherProcessor()); // Register references so that our assembly resolver can use them foreach (var reference in References) { assemblyResolver.RegisterReference(reference); } var assemblyProcessorContext = new AssemblyProcessorContext(assemblyResolver, assemblyDefinition, log); foreach (var processor in processors) { modified = processor.Process(assemblyProcessorContext) || modified; } // Assembly might have been recreated (i.e. IL-Repack), so let's use it from now on assemblyDefinition = assemblyProcessorContext.Assembly; serializationHash = assemblyProcessorContext.SerializationHash; if (modified) { // Add AssemblyProcessedAttribute to assembly so that it doesn't get processed again var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assemblyDefinition); if (mscorlibAssembly is null) { OnErrorAction("Missing reference to mscorlib.dll or System.Runtime.dll in assembly!"); return(false); } var attributeType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(Attribute).FullName); var attributeTypeRef = assemblyDefinition.MainModule.ImportReference(attributeType); var attributeCtorRef = assemblyDefinition.MainModule.ImportReference( attributeType.GetConstructors().Single(x => x.Parameters.Count == 0)); var voidType = assemblyDefinition.MainModule.TypeSystem.Void; // Create custom attribute var assemblyProcessedAttributeType = new TypeDefinition( "Stride.Core", "AssemblyProcessedAttribute", TypeAttributes.BeforeFieldInit | TypeAttributes.AnsiClass | TypeAttributes.AutoClass | TypeAttributes.Public, attributeTypeRef); // Add constructor (call parent constructor) var assemblyProcessedAttributeConstructor = new MethodDefinition(".ctor", MethodAttributes.RTSpecialName | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Public, voidType); assemblyProcessedAttributeConstructor.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); assemblyProcessedAttributeConstructor.Body.Instructions.Add(Instruction.Create(OpCodes.Call, attributeCtorRef)); assemblyProcessedAttributeConstructor.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); assemblyProcessedAttributeType.Methods.Add(assemblyProcessedAttributeConstructor); // Add AssemblyProcessedAttribute to assembly assemblyDefinition.MainModule.Types.Add(assemblyProcessedAttributeType); assemblyDefinition.CustomAttributes.Add(new CustomAttribute(assemblyProcessedAttributeConstructor)); } } catch (Exception ex) { OnErrorAction(errorMessage: null, ex); return(false); } return(true); }
public bool Process(AssemblyProcessorContext context) { var assembly = context.Assembly; var fields = new List <FieldDefinition>(); var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assembly); if (mscorlibAssembly == null) { throw new InvalidOperationException("Missing mscorlib.dll from assembly"); } MethodDefinition parameterKeysMergeMethod = null; TypeDefinition assemblyEffectKeysAttributeType = null; var getTypeFromHandleMethod = new Lazy <MethodReference>(() => { // Find Type.GetTypeFromHandle var typeType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(Type).FullName); return(assembly.MainModule.ImportReference(typeType.Methods.First(x => x.Name == "GetTypeFromHandle"))); }); var effectKeysStaticConstructors = new List <MethodReference>(); var effectKeysArrayElemementTypes = new HashSet <TypeReference>(); foreach (var type in assembly.MainModule.GetTypes()) { fields.Clear(); foreach (var field in type.Fields.Where(x => x.IsStatic)) { var fieldBaseType = field.FieldType; while (fieldBaseType != null) { if (fieldBaseType.FullName == "Stride.Rendering.ParameterKey") { break; } // TODO: Get PropertyKey.PropertyType instead var genericInstance = fieldBaseType as GenericInstanceType; if (genericInstance != null && genericInstance.ElementType.FullName == "Stride.Rendering.ParameterKey`1") { var genericArgument = genericInstance.GenericArguments.First(); if (genericArgument.IsArray) { effectKeysArrayElemementTypes.Add(genericArgument.GetElementType()); } } var resolvedFieldBaseType = fieldBaseType.Resolve(); if (resolvedFieldBaseType == null) { fieldBaseType = null; break; } fieldBaseType = resolvedFieldBaseType.BaseType; } if (fieldBaseType == null) { continue; } fields.Add(field); } if (fields.Count == 0) { continue; } // ParameterKey present means we should have a static cctor. var cctor = type.GetStaticConstructor(); if (cctor == null) { continue; } // Load necessary Stride methods/attributes if (parameterKeysMergeMethod == null) { AssemblyDefinition strideEngineAssembly; try { strideEngineAssembly = assembly.Name.Name == "Stride" ? assembly : context.AssemblyResolver.Resolve(new AssemblyNameReference("Stride", null)); } catch (Exception) { context.Log.WriteLine("Error, cannot find [Stride] assembly for processing ParameterKeyProcessor"); // We can't generate an exception, so we are just returning. It means that Stride has not been generated so far. return(true); } var parameterKeysType = strideEngineAssembly.MainModule.GetTypes().First(x => x.Name == "ParameterKeys"); parameterKeysMergeMethod = parameterKeysType.Methods.First(x => x.Name == "Merge"); assemblyEffectKeysAttributeType = strideEngineAssembly.MainModule.GetTypes().First(x => x.Name == "AssemblyEffectKeysAttribute"); } var cctorIL = cctor.Body.GetILProcessor(); var cctorInstructions = cctor.Body.Instructions; var keyClassName = type.Name; if (keyClassName.EndsWith("Keys")) { keyClassName = keyClassName.Substring(0, keyClassName.Length - 4); } keyClassName += '.'; bool cctorModified = false; // Find field store instruction for (int i = 0; i < cctorInstructions.Count; ++i) { var fieldInstruction = cctorInstructions[i]; if (fieldInstruction.OpCode == OpCodes.Stsfld && fields.Contains(fieldInstruction.Operand)) { var activeField = (FieldReference)fieldInstruction.Operand; var nextInstruction = cctorInstructions[i + 1]; cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Ldsfld, activeField)); cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Ldtoken, type)); cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Call, getTypeFromHandleMethod.Value)); cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Ldstr, keyClassName + activeField.Name)); cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Call, assembly.MainModule.ImportReference(parameterKeysMergeMethod))); cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Castclass, activeField.FieldType)); cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Stsfld, activeField)); i = cctorInstructions.IndexOf(nextInstruction); cctorModified = true; } } if (cctorModified) { effectKeysStaticConstructors.Add(cctor); } } if (effectKeysStaticConstructors.Count > 0) { // Add [AssemblyEffectKeysAttribute] to the assembly assembly.CustomAttributes.Add(new CustomAttribute(assembly.MainModule.ImportReference(assemblyEffectKeysAttributeType.GetConstructors().First(x => !x.HasParameters)))); // Get or create module static constructor var voidType = assembly.MainModule.TypeSystem.Void; var moduleClass = assembly.MainModule.Types.First(t => t.Name == "<Module>"); var staticConstructor = moduleClass.GetStaticConstructor(); if (staticConstructor == null) { staticConstructor = new MethodDefinition(".cctor", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, voidType); staticConstructor.Body.GetILProcessor().Append(Instruction.Create(OpCodes.Ret)); moduleClass.Methods.Add(staticConstructor); } var il = staticConstructor.Body.GetILProcessor(); var returnInstruction = staticConstructor.Body.Instructions.Last(); var newReturnInstruction = Instruction.Create(returnInstruction.OpCode); newReturnInstruction.Operand = returnInstruction.Operand; returnInstruction.OpCode = OpCodes.Nop; returnInstruction.Operand = null; var typeType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(Type).FullName); var typeHandleProperty = typeType.Properties.First(x => x.Name == "TypeHandle"); var getTypeHandleMethod = assembly.MainModule.ImportReference(typeHandleProperty.GetMethod); 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)); // Call every key class static constructor from the module static constructor so that they are properly constructed (because accessing through reflection might cause problems) staticConstructor.Body.SimplifyMacros(); foreach (var effectKeysStaticConstructor in effectKeysStaticConstructors) { il.Append(Instruction.Create(OpCodes.Ldtoken, effectKeysStaticConstructor.DeclaringType)); il.Append(Instruction.Create(OpCodes.Call, getTypeFromHandleMethod.Value)); il.Append(Instruction.Create(OpCodes.Callvirt, getTypeHandleMethod)); il.Append(Instruction.Create(OpCodes.Call, runClassConstructorMethod)); } if (effectKeysArrayElemementTypes.Count > 0) { var methodImplAttributeType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(MethodImplAttribute).FullName); var methodImplAttributesType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(MethodImplOptions).FullName); var attribute = new CustomAttribute(methodImplAttributeType.GetConstructors().First(x => x.HasParameters && x.Parameters[0].ParameterType.FullName == methodImplAttributesType.FullName)); attribute.ConstructorArguments.Add(new CustomAttributeArgument(methodImplAttributesType, MethodImplOptions.NoOptimization)); staticConstructor.CustomAttributes.Add(attribute); } // Create instances of InternalValueArray<T>. Required for LLVM AOT foreach (var elementType in effectKeysArrayElemementTypes) { var strideAssembly = assembly.Name.Name == "Stride" ? assembly : assembly.MainModule.AssemblyResolver.Resolve(new AssemblyNameReference("Stride", null)); var parameterCollectionType = strideAssembly.MainModule.GetTypeResolved("Stride.Rendering.ParameterCollection"); var internalValueArrayType = parameterCollectionType.NestedTypes.First(x => x.Name == "InternalValueArray`1"); var constructor = internalValueArrayType.GetConstructors().First(); var internalValueArrayConstructor = strideAssembly.MainModule.ImportReference(constructor).MakeGeneric(elementType); il.Append(Instruction.Create(OpCodes.Ldc_I4_0)); il.Append(Instruction.Create(OpCodes.Newobj, internalValueArrayConstructor)); il.Append(Instruction.Create(OpCodes.Pop)); } il.Append(newReturnInstruction); staticConstructor.Body.OptimizeMacros(); } return(true); }
public bool Process(AssemblyProcessorContext context) { var registry = new AssemblyScanRegistry(); foreach (var type in context.Assembly.MainModule.GetAllTypes()) { // Ignore interface types as well as types with generics // Note: we could support generic types at some point but we probably need // to get static generic instantiation type list from serializer code generator if (type.IsInterface || type.HasGenericParameters) { continue; } var currentType = type; // Scan type and parent types while (currentType != null) { // Scan interfaces foreach (var @interface in currentType.Interfaces) { ScanAttributes(context.Log, registry, @interface.InterfaceType, type); } ScanAttributes(context.Log, registry, currentType, type); currentType = currentType.BaseType?.Resolve(); } } if (registry.HasScanTypes) { // This code should mirror what AssemblyScanCodeGenerator.tt generates var assembly = context.Assembly; var strideCoreModule = assembly.GetStrideCoreModule(); var assemblyRegistryType = strideCoreModule.GetType("Stride.Core.Reflection.AssemblyRegistry"); // Generate code var assemblyScanType = new TypeDefinition("Stride.Core.Serialization.AssemblyScan", Utilities.BuildValidClassName(assembly.Name.Name) + "AssemblyScan", TypeAttributes.BeforeFieldInit | TypeAttributes.AnsiClass | TypeAttributes.AutoClass | TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract, assembly.MainModule.TypeSystem.Object); assembly.MainModule.Types.Add(assemblyScanType); // Create Initialize method var initializeMethod = new MethodDefinition("Initialize", MethodAttributes.Assembly | MethodAttributes.HideBySig | MethodAttributes.Static, assembly.MainModule.TypeSystem.Void); assemblyScanType.Methods.Add(initializeMethod); // Make sure it is called at module startup initializeMethod.AddModuleInitializer(-2000); var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assembly); var collectionAssembly = CecilExtensions.FindCollectionsAssembly(assembly); var reflectionAssembly = CecilExtensions.FindReflectionAssembly(assembly); // 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; // List<Type> var listType = collectionAssembly.MainModule.GetTypeResolved(typeof(List <>).FullName); var listTypeTypeRef = assembly.MainModule.ImportReference(listType).MakeGenericType(typeTypeRef); // Dictionary<Type, List<Type>> var dictionaryType = collectionAssembly.MainModule.GetType(typeof(Dictionary <,>).FullName); var initializeMethodIL = initializeMethod.Body.GetILProcessor(); // AssemblyRegistry.RegisterScanTypes(typeof(AssemblyScanType).GetTypeInfo().Assembly, dictionary); initializeMethodIL.Emit(OpCodes.Ldtoken, assemblyScanType); initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod)); initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTokenInfoExMethod)); initializeMethodIL.Emit(OpCodes.Callvirt, assembly.MainModule.ImportReference(getTypeInfoAssembly)); // dictionary = new Dictionary<Type, List<Type>>(); initializeMethodIL.Emit(OpCodes.Newobj, assembly.MainModule.ImportReference(dictionaryType.GetEmptyConstructor()).MakeGeneric(typeTypeRef, listTypeTypeRef)); foreach (var scanTypeEntry in registry.ScanTypes) { initializeMethodIL.Emit(OpCodes.Dup); // typeof(X) initializeMethodIL.Emit(OpCodes.Ldtoken, assembly.MainModule.ImportReference(scanTypeEntry.Key.Resolve())); initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod)); // new List<Type>(); initializeMethodIL.Emit(OpCodes.Newobj, assembly.MainModule.ImportReference(listType.GetEmptyConstructor()).MakeGeneric(typeTypeRef)); foreach (var scanType in scanTypeEntry.Value) { initializeMethodIL.Emit(OpCodes.Dup); initializeMethodIL.Emit(OpCodes.Ldtoken, assembly.MainModule.ImportReference(scanType)); initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod)); initializeMethodIL.Emit(OpCodes.Callvirt, assembly.MainModule.ImportReference(listType.Methods.First(x => x.Name == "Add")).MakeGeneric(typeTypeRef)); } initializeMethodIL.Emit(OpCodes.Callvirt, assembly.MainModule.ImportReference(dictionaryType.Methods.First(x => x.Name == "Add")).MakeGeneric(typeTypeRef, listTypeTypeRef)); } initializeMethodIL.Emit(OpCodes.Newobj, assembly.MainModule.ImportReference(assemblyRegistryType.NestedTypes.First(x => x.Name == "ScanTypes").Methods.Single(x => x.IsConstructor && x.Parameters.Count == 1))); initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(assemblyRegistryType.Methods.First(x => x.Name == "RegisterScanTypes"))); initializeMethodIL.Emit(OpCodes.Ret); //var assemblyScanCodeGenerator = new AssemblyScanCodeGenerator(assembly, registry); //sourceCodeRegisterAction(assemblyScanCodeGenerator.TransformText(), "AssemblyScan"); } return(registry.HasScanTypes); }
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 bool Process(AssemblyProcessorContext context) { var assembly = context.Assembly; var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assembly); if (mscorlibAssembly == null) { throw new InvalidOperationException("Missing mscorlib.dll from assembly"); } // For now, use import, but this can cause mixed framework versions when processing an assembly with a different framework version. voidType = assembly.MainModule.TypeSystem.Void; stringType = assembly.MainModule.TypeSystem.String; objectType = assembly.MainModule.TypeSystem.Object; var propertyInfoType = assembly.MainModule.ImportReference(mscorlibAssembly.MainModule.GetTypeResolved(typeof(PropertyInfo).FullName)); var typeType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(Type).FullName); TypeDefinition propertyChangedExtendedEventArgsType; ModuleDefinition strideCoreModule; try { strideCoreModule = assembly.GetStrideCoreModule(); } catch (Exception) { return(true); } propertyChangedExtendedEventArgsType = strideCoreModule.GetTypes().First(x => x.Name == "PropertyChangedExtendedEventArgs").Resolve(); var typeTokenInfoEx = mscorlibAssembly.MainModule.GetTypeResolved("System.Reflection.TypeInfo").Resolve(); var getPropertyMethod = typeTokenInfoEx.Methods.First(x => x.Name == "GetDeclaredProperty" && x.Parameters.Count == 1); var getTypeFromHandleMethod = typeType.Methods.First(x => x.Name == "GetTypeFromHandle"); var getTokenInfoExMethod = CecilExtensions.FindReflectionAssembly(assembly).MainModule.GetTypeResolved("System.Reflection.IntrospectionExtensions").Resolve().Methods.First(x => x.Name == "GetTypeInfo"); var propertyChangedExtendedEventArgsConstructor = assembly.MainModule.ImportReference(propertyChangedExtendedEventArgsType.Methods.First(x => x.IsConstructor)); bool modified = false; foreach (var type in assembly.MainModule.GetTypes()) { MethodReference getPropertyChangedMethod; getPropertyChangedMethod = GetGetPropertyChangedMethod(assembly, type); //var propertyChangedField = GetPropertyChangedField(type); //if (propertyChangedField == null) // continue; var propertyChangedField = GetPropertyChangedField(type); if (getPropertyChangedMethod == null && propertyChangedField == null) { continue; } TypeReference propertyChangedFieldType; if (getPropertyChangedMethod == null) { modified = true; getPropertyChangedMethod = GetOrCreateGetPropertyChangedMethod(assembly, type, propertyChangedField); } if (propertyChangedField != null) { propertyChangedField = assembly.MainModule.ImportReference(propertyChangedField); propertyChangedFieldType = propertyChangedField.FieldType; } else { propertyChangedFieldType = getPropertyChangedMethod.ReturnType; } // Add generic to declaring type if (getPropertyChangedMethod.DeclaringType.HasGenericParameters) { getPropertyChangedMethod = getPropertyChangedMethod.MakeGeneric(getPropertyChangedMethod.DeclaringType.GenericParameters.ToArray()); } var propertyChangedInvoke = assembly.MainModule.ImportReference(propertyChangedFieldType.Resolve().Methods.First(x => x.Name == "Invoke")); foreach (var property in type.Properties) { if (property.SetMethod == null || !property.HasThis) { continue; } MethodReference propertyGetMethod = property.GetMethod; // Only patch properties that have a public Getter var methodDefinition = propertyGetMethod.Resolve(); if ((methodDefinition.Attributes & MethodAttributes.Public) != MethodAttributes.Public) { continue; } // Add generic to declaring type if (propertyGetMethod.DeclaringType.HasGenericParameters) { propertyGetMethod = propertyGetMethod.MakeGeneric(propertyGetMethod.DeclaringType.GenericParameters.ToArray()); } //var versionableAttribute = property.CustomAttributes.FirstOrDefault(x => x.AttributeType.FullName == typeof(VersionableAttribute).FullName); //if (versionableAttribute == null) // continue; modified = true; FieldReference staticField = new FieldDefinition(property.Name + "PropertyInfo", FieldAttributes.Static | FieldAttributes.Private | FieldAttributes.InitOnly, propertyInfoType); type.Fields.Add((FieldDefinition)staticField); // Add generic to declaring type if (staticField.DeclaringType.HasGenericParameters) { staticField = staticField.MakeGeneric(staticField.DeclaringType.GenericParameters.ToArray()); } // In static constructor, find PropertyInfo and store it in static field { var staticConstructor = type.GetStaticConstructor(); if (staticConstructor == null) { staticConstructor = new MethodDefinition(".cctor", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, voidType); staticConstructor.Body.GetILProcessor().Append(Instruction.Create(OpCodes.Ret)); type.Methods.Add(staticConstructor); } VariableReference localTokenEx = null; int localTokenExIndex = 0; for (int i = 0; i < staticConstructor.Body.Variables.Count; i++) { var localVar = staticConstructor.Body.Variables[i]; if (localVar.VariableType.FullName == typeTokenInfoEx.FullName) { localTokenEx = localVar; localTokenExIndex = i; break; } } if (localTokenEx == null) { localTokenEx = new VariableDefinition(assembly.MainModule.ImportReference(typeTokenInfoEx)); staticConstructor.Body.Variables.Add((VariableDefinition)localTokenEx); localTokenExIndex = staticConstructor.Body.Variables.Count - 1; } var ilProcessor = staticConstructor.Body.GetILProcessor(); var returnInstruction = staticConstructor.Body.Instructions.Last(); var newReturnInstruction = Instruction.Create(returnInstruction.OpCode); newReturnInstruction.Operand = returnInstruction.Operand; returnInstruction.OpCode = OpCodes.Nop; returnInstruction.Operand = null; // Find PropertyInfo and store it in static field ilProcessor.Append(Instruction.Create(OpCodes.Ldtoken, type)); ilProcessor.Append(Instruction.Create(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod))); ilProcessor.Append(Instruction.Create(OpCodes.Call, assembly.MainModule.ImportReference(getTokenInfoExMethod))); ilProcessor.Append(LocationToStloc(ilProcessor, localTokenExIndex)); ilProcessor.Append(ilProcessor.Create(OpCodes.Ldloca_S, (byte)localTokenExIndex)); ilProcessor.Append(Instruction.Create(OpCodes.Ldstr, property.Name)); ilProcessor.Append(Instruction.Create(OpCodes.Call, assembly.MainModule.ImportReference(getPropertyMethod))); ilProcessor.Append(Instruction.Create(OpCodes.Stsfld, staticField)); ilProcessor.Append(newReturnInstruction); } { var ilProcessor = property.SetMethod.Body.GetILProcessor(); var returnInstruction = property.SetMethod.Body.Instructions.Last(); var firstInstruction = property.SetMethod.Body.Instructions[0]; if (property.SetMethod.Body.Instructions[0].OpCode != OpCodes.Nop) { ilProcessor.InsertBefore(property.SetMethod.Body.Instructions[0], Instruction.Create(OpCodes.Nop)); } var newReturnInstruction = Instruction.Create(returnInstruction.OpCode); newReturnInstruction.Operand = returnInstruction.Operand; returnInstruction.OpCode = OpCodes.Nop; returnInstruction.Operand = null; var propertyChangedVariable = new VariableDefinition(assembly.MainModule.ImportReference(propertyChangedFieldType)); property.SetMethod.Body.Variables.Add(propertyChangedVariable); var oldValueVariable = new VariableDefinition(objectType); property.SetMethod.Body.Variables.Add(oldValueVariable); Instruction jump1, jump2; // Prepend: // var propertyChanged = GetPropertyChanged(); // var oldValue = propertyChanged != null ? (object)Property : null; property.SetMethod.Body.SimplifyMacros(); ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Ldarg_0)); ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Call, getPropertyChangedMethod)); ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Stloc, propertyChangedVariable)); ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Ldloc, propertyChangedVariable)); ilProcessor.InsertBefore(firstInstruction, jump1 = Instruction.Create(OpCodes.Brtrue, Instruction.Create(OpCodes.Nop))); ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Ldnull)); ilProcessor.InsertBefore(firstInstruction, jump2 = Instruction.Create(OpCodes.Br, Instruction.Create(OpCodes.Nop))); ilProcessor.InsertBefore(firstInstruction, (Instruction)(jump1.Operand = Instruction.Create(OpCodes.Ldarg_0))); ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Call, propertyGetMethod)); if (property.PropertyType.IsValueType) { ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Box, property.PropertyType)); } ilProcessor.InsertBefore(firstInstruction, (Instruction)(jump2.Operand = Instruction.Create(OpCodes.Nop))); ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Stloc, oldValueVariable)); // Append: // if (propertyChanged != null) // propertyChanged(this, new PropertyChangedExtendedEventArgs("Property", oldValue, Property)); ilProcessor.Append(Instruction.Create(OpCodes.Ldloc, propertyChangedVariable)); ilProcessor.Append(Instruction.Create(OpCodes.Ldnull)); ilProcessor.Append(Instruction.Create(OpCodes.Ceq)); ilProcessor.Append(Instruction.Create(OpCodes.Brtrue, newReturnInstruction)); ilProcessor.Append(Instruction.Create(OpCodes.Ldloc, propertyChangedVariable)); ilProcessor.Append(Instruction.Create(OpCodes.Ldarg_0)); ilProcessor.Append(Instruction.Create(OpCodes.Ldsfld, staticField)); ilProcessor.Append(Instruction.Create(OpCodes.Ldloc, oldValueVariable)); ilProcessor.Append(Instruction.Create(OpCodes.Ldarg_0)); ilProcessor.Append(Instruction.Create(OpCodes.Call, propertyGetMethod)); if (property.PropertyType.IsValueType) { ilProcessor.Append(Instruction.Create(OpCodes.Box, property.PropertyType)); } ilProcessor.Append(Instruction.Create(OpCodes.Newobj, propertyChangedExtendedEventArgsConstructor)); ilProcessor.Append(Instruction.Create(OpCodes.Callvirt, propertyChangedInvoke)); ilProcessor.Append(Instruction.Create(OpCodes.Nop)); ilProcessor.Append(newReturnInstruction); property.SetMethod.Body.OptimizeMacros(); } } } return(modified); }