private TypeReference ChangeGenericArguments(AssemblyProcessorContext context, TypeReference type, TypeReference relativeType) { var genericInstance = type as GenericInstanceType; if (genericInstance == null) { return(type); } var genericArguments = new List <TypeReference>(); foreach (var genericArgument in genericInstance.GenericArguments) { if (genericArgument.IsGenericParameter) { var genericParameter = GetGenericParameterForArgument(relativeType, genericArgument); if (genericParameter != null) { genericArguments.Add(genericParameter); } } else { var newGenericArgument = ChangeGenericArguments(context, genericArgument, relativeType); genericArguments.Add(newGenericArgument); } } if (genericArguments.Count != genericInstance.GenericArguments.Count) { throw new InvalidOperationException("Could not resolve generic arguments"); } return(context.Assembly.MainModule.ImportReference(genericInstance.Resolve()).MakeGenericInstanceType(genericArguments.ToArray())); }
public bool Process(AssemblyProcessorContext context) { this.assembly = context.Assembly; asyncBridgeAssembly = context.AssemblyResolver.Resolve(new AssemblyNameReference("AsyncBridge", null)); assembly.MainModule.AssemblyReferences.Add(asyncBridgeAssembly.Name); foreach (var type in assembly.MainModule.Types) { ProcessType(type); } return(true); }
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); }
public bool Process(AssemblyProcessorContext context) { var assembly = context.Assembly; var moduleInitializers = new List <KeyValuePair <int, MethodReference> >(); // Generate a module initializer for all types, including nested types foreach (var type in assembly.MainModule.GetAllTypes()) { foreach (var method in type.Methods) { var moduleInitializerAttribute = method.CustomAttributes.FirstOrDefault(x => x.AttributeType.FullName == "Xenko.Core.ModuleInitializerAttribute"); if (moduleInitializerAttribute != null) { var order = moduleInitializerAttribute.HasConstructorArguments ? (int)moduleInitializerAttribute.ConstructorArguments[0].Value : 0; moduleInitializers.Add(new KeyValuePair <int, MethodReference>(order, method)); } } } if (moduleInitializers.Count == 0) { return(false); } // Sort by Order property moduleInitializers = moduleInitializers.OrderBy(x => x.Key).ToList(); // Get or create module static constructor Instruction returnInstruction; var staticConstructor = OpenModuleConstructor(assembly, out returnInstruction); var il = staticConstructor.Body.GetILProcessor(); var newReturnInstruction = Instruction.Create(returnInstruction.OpCode); newReturnInstruction.Operand = returnInstruction.Operand; returnInstruction.OpCode = OpCodes.Nop; returnInstruction.Operand = null; staticConstructor.Body.SimplifyMacros(); foreach (var moduleInitializer in moduleInitializers) { il.Append(Instruction.Create(OpCodes.Call, moduleInitializer.Value)); } il.Append(newReturnInstruction); staticConstructor.Body.OptimizeMacros(); return(true); }
public bool Process(AssemblyProcessorContext context) { var registry = new ComplexSerializerRegistry(context.Platform, context.Assembly, context.Log); // Register default serialization profile (to help AOT generic instantiation of serializers) RegisterDefaultSerializationProfile(context.AssemblyResolver, context.Assembly, registry, context.Log); // Generate serializer code // Create the serializer code generator //var serializerGenerator = new ComplexSerializerCodeGenerator(registry); //sourceCodeRegisterAction(serializerGenerator.TransformText(), "DataSerializers"); GenerateSerializerCode(registry); return(true); }
private void EnsureInitialized(AssemblyProcessorContext context) { if (isInitialized) { return; } var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(context.Assembly); xenkoCoreModule = context.Assembly.GetXenkoCoreModule(); pooledClosureType = context.Assembly.MainModule.ImportReference(xenkoCoreModule.GetType("Xenko.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 = xenkoCoreModule.GetType("Xenko.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; }
public bool Process(AssemblyProcessorContext context) { this.assembly = context.Assembly; // Import void* and int32 from assembly using mscorlib specific version (2.0 or 4.0 depending on assembly) voidPointerType = new PointerType(assembly.MainModule.TypeSystem.Void); intType = assembly.MainModule.TypeSystem.Int32; context.Log.WriteLine($"Patch for assembly [{assembly.FullName}]"); foreach (var type in assembly.MainModule.Types) { PatchType(type); } // Remove All Interop classes foreach (var type in classToRemoveList) { assembly.MainModule.Types.Remove(type); } return(true); }
public bool Process(AssemblyProcessorContext context) { var assembly = context.Assembly; // Only process if there is a public key if (!assembly.Name.HasPublicKey) { return(false); } // Check if already strong signed if ((assembly.MainModule.Attributes & ModuleAttributes.StrongNameSigned) == ModuleAttributes.StrongNameSigned) { return(false); } // We have a delay signed assembly that is not strong name signed yet. // Let's strong sign it now (a.k.a. OSS, OpenSourceSign) // Note: Maybe we should make sure it's actually Xenko key? assembly.MainModule.Attributes |= ModuleAttributes.StrongNameSigned; return(true); }
public bool Process(AssemblyProcessorContext context) { bool changed = false; foreach (var type in context.Assembly.MainModule.GetAllTypes()) { foreach (var method in type.Methods) { if (method.CustomAttributes.Any(x => x.AttributeType.FullName == "Xenko.Core.IL.RemoveInitLocalsAttribute")) { if (method.Body == null) { throw new InvalidOperationException($"Trying to remove initlocals from method {method.FullName} without body."); } method.Body.InitLocals = false; changed = true; } } } return(changed); }
public bool Run(ref AssemblyDefinition assemblyDefinition, ref bool readWriteSymbols, out bool modified) { modified = false; try { var assemblyResolver = (CustomAssemblyResolver)assemblyDefinition.MainModule.AssemblyResolver; // Register self assemblyResolver.Register(assemblyDefinition); var processors = new List <IAssemblyDefinitionProcessor>(); // We are no longer using it so we are deactivating it for now to avoid processing //if (AutoNotifyProperty) //{ // processors.Add(new NotifyPropertyProcessor()); //} processors.Add(new AddReferenceProcessor(ReferencesToAdd)); if (ParameterKey) { processors.Add(new ParameterKeyProcessor()); } if (NewAssemblyName != null) { processors.Add(new RenameAssemblyProcessor(NewAssemblyName)); } //processors.Add(new AsyncBridgeProcessor()); // Always applies the interop processor processors.Add(new InteropProcessor()); processors.Add(new MonoFixedProcessor()); 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()); processors.Add(new OpenSourceSignProcessor()); // Check if there is already a AssemblyProcessedAttribute (in which case we can skip processing, it has already been done). // 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.CustomAttributes.Any( x => x.AttributeType.FullName == "Xenko.Core.AssemblyProcessedAttribute")) { OnInfoAction($"Assembly [{assemblyDefinition.Name}] has already been processed, skip it."); return(true); } // Register references so that our assembly resolver can use them foreach (var reference in References) { assemblyResolver.RegisterReference(reference); } var assemblyProcessorContext = new AssemblyProcessorContext(assemblyResolver, assemblyDefinition, Platform, 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; if (modified) { // In case assembly has been modified, // add AssemblyProcessedAttribute to assembly so that it doesn't get processed again var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assemblyDefinition); if (mscorlibAssembly == 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("Xenko.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 e) { OnErrorAction(null, e); return(false); } finally { } return(true); }
public bool Process(AssemblyProcessorContext context) { var basePath = Path.Combine(Path.GetDirectoryName(inputFile) ?? "", Path.GetFileNameWithoutExtension(inputFile) ?? ""); var xmlFile = basePath + ".xml"; var targetFile = basePath + ".usrdoc"; // No xml documentation file available, stop here. if (!File.Exists(xmlFile)) { return(false); } var result = new Dictionary <string, string>(); var document = XElement.Load(xmlFile); foreach (var member in document.Descendants("member")) { var nameAttribute = member.Attribute("name"); if (nameAttribute == null) { continue; } foreach (var userdocElement in member.Descendants("userdoc")) { string userdoc = null; var key = nameAttribute.Value; var docOverride = userdocElement.Attribute("override"); if (docOverride != null && key.StartsWith("T")) // if on top of the class we have some overrides we must process them now { key = "P" + key.Substring(1) + "." + docOverride.Value; //replace T with M } if (result.ContainsKey(key)) { context.Log.WriteLine($"Warning: the member {key} has multiple userdoc, only the first one will be used."); continue; } if (userdocElement.Descendants().Any()) { context.Log.WriteLine($"Warning: the userdoc of member {key} has descendant nodes, which is not supported."); continue; } userdoc = userdocElement.Value; userdoc = userdoc.Replace('\t', ' ').Replace('\r', ' ').Replace('\n', ' ').Trim(); // Removes double space. var regex = new Regex(@"[ ]{2,}", RegexOptions.None); userdoc = regex.Replace(userdoc, @" "); result.Add(key, userdoc); } } using (var writer = new StreamWriter(targetFile)) { foreach (var entry in result) { writer.WriteLine("{0}={1}", entry.Key, entry.Value); } } 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 xenkoCoreModule = assembly.GetXenkoCoreModule(); var assemblyRegistryType = xenkoCoreModule.GetType("Xenko.Core.Reflection.AssemblyRegistry"); // Generate code var assemblyScanType = new TypeDefinition("Xenko.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); }
private ClosureInfo ProcessClosure(AssemblyProcessorContext context, TypeDefinition closureType, TypeReference[] genericParameters) { ClosureInfo closure; if (closures.TryGetValue(closureType, out closure)) { return(closure); } var closureTypeConstructor = closureType.Methods.FirstOrDefault(x => x.Name == ".ctor"); var closureGenericType = closureType.MakeGenericType(genericParameters); // Create factory method for pool items var factoryMethod = new MethodDefinition("<Factory>", MethodAttributes.HideBySig | MethodAttributes.Private | MethodAttributes.Static, closureGenericType); closureType.Methods.Add(factoryMethod); factoryMethod.Body.InitLocals = true; factoryMethod.Body.Variables.Add(new VariableDefinition(closureGenericType)); var factoryMethodProcessor = factoryMethod.Body.GetILProcessor(); // Create and store closure factoryMethodProcessor.Emit(OpCodes.Newobj, closureTypeConstructor.MakeGeneric(genericParameters)); factoryMethodProcessor.Emit(OpCodes.Stloc_0); //// Return closure factoryMethodProcessor.Emit(OpCodes.Ldloc_0); factoryMethodProcessor.Emit(OpCodes.Ret); // Create pool field var poolField = new FieldDefinition("<pool>", FieldAttributes.Public | FieldAttributes.Static, poolType.MakeGenericType(closureGenericType)); closureType.Fields.Add(poolField); var poolFieldReference = poolField.MakeGeneric(genericParameters); // Initialize pool var cctor = GetOrCreateClassConstructor(closureType); var ilProcessor2 = cctor.Body.GetILProcessor(); var retInstruction = cctor.Body.Instructions.FirstOrDefault(x => x.OpCode == OpCodes.Ret); ilProcessor2.InsertBefore(retInstruction, ilProcessor2.Create(OpCodes.Ldnull)); ilProcessor2.InsertBefore(retInstruction, ilProcessor2.Create(OpCodes.Ldftn, factoryMethod.MakeGeneric(genericParameters))); ilProcessor2.InsertBefore(retInstruction, ilProcessor2.Create(OpCodes.Newobj, funcConstructor.MakeGeneric(closureGenericType))); ilProcessor2.InsertBefore(retInstruction, ilProcessor2.Create(OpCodes.Newobj, poolConstructor.MakeGeneric(closureGenericType))); ilProcessor2.InsertBefore(retInstruction, ilProcessor2.Create(OpCodes.Stsfld, poolFieldReference)); // Implement IPooledClosure closureType.Interfaces.Add(new InterfaceImplementation(pooledClosureType)); // Create reference count field var countField = new FieldDefinition("<referenceCount>", FieldAttributes.Public, context.Assembly.MainModule.TypeSystem.Int32); closureType.Fields.Add(countField); var oountFieldReference = countField.MakeGeneric(genericParameters); // Create AddReference method var addReferenceMethod = new MethodDefinition("AddReference", MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.NewSlot, context.Assembly.MainModule.TypeSystem.Void); var ilProcessor4 = addReferenceMethod.Body.GetILProcessor(); ilProcessor4.Emit(OpCodes.Ldarg_0); ilProcessor4.Emit(OpCodes.Ldflda, oountFieldReference); ilProcessor4.Emit(OpCodes.Call, interlockedIncrementMethod); ilProcessor4.Emit(OpCodes.Pop); ilProcessor4.Emit(OpCodes.Ret); closureType.Methods.Add(addReferenceMethod); // Create Release method var releaseMethod = new MethodDefinition("Release", MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.NewSlot, context.Assembly.MainModule.TypeSystem.Void); ilProcessor4 = releaseMethod.Body.GetILProcessor(); retInstruction = ilProcessor4.Create(OpCodes.Ret); // Check decremented reference count ilProcessor4.Emit(OpCodes.Ldarg_0); ilProcessor4.Emit(OpCodes.Ldflda, oountFieldReference); ilProcessor4.Emit(OpCodes.Call, interlockedDecrementMethod); ilProcessor4.Emit(OpCodes.Ldc_I4_0); ilProcessor4.Emit(OpCodes.Ceq); ilProcessor4.Emit(OpCodes.Brfalse_S, retInstruction); // Clear fields foreach (var field in closureType.Fields) { if (field.IsStatic || field.FieldType.IsPrimitive || field == countField) { continue; } ilProcessor4.Emit(OpCodes.Ldarg_0); if (field.FieldType.IsValueType) { ilProcessor4.Emit(OpCodes.Ldflda, field.MakeGeneric(genericParameters)); ilProcessor4.Emit(OpCodes.Initobj, field.FieldType); } else { ilProcessor4.Emit(OpCodes.Ldnull); ilProcessor4.Emit(OpCodes.Stfld, field.MakeGeneric(genericParameters)); } } // Release this to pool ilProcessor4.Emit(OpCodes.Ldsfld, poolFieldReference); ilProcessor4.Emit(OpCodes.Ldarg_0); ilProcessor4.Emit(OpCodes.Callvirt, poolReleaseMethod.MakeGeneric(closureGenericType)); ilProcessor4.Append(retInstruction); closureType.Methods.Add(releaseMethod); closures.Add(closureType, closure = new ClosureInfo { FactoryMethod = factoryMethod, AddReferenceMethod = addReferenceMethod, ReleaseMethod = releaseMethod, PoolField = poolField }); return(closure); }
private bool ProcessDelegateAllocation(AssemblyProcessorContext context, MethodDefinition method, Instruction delegateAllocationInstruction) { // The instruction must be a delegate allocation // If not, this might be a static delegate, or some unsupported construct if (delegateAllocationInstruction.OpCode != OpCodes.Newobj) { return(false); } var delegateInstanceConstructor = (MethodReference)delegateAllocationInstruction.Operand; var delegateInstanceType = delegateInstanceConstructor.DeclaringType; // The previous instruction pushes the delegate method onto the stack var functionPointerInstruction = delegateAllocationInstruction.Previous; if (functionPointerInstruction.OpCode != OpCodes.Ldftn) { return(false); } var delegateMethod = (MethodReference)functionPointerInstruction.Operand; // The previous instruction pushes the target onto the stack // If it's the this-parameter, we can create an instance field, and reuse the same delegate var loadClosureInstruction = functionPointerInstruction.Previous; if (loadClosureInstruction.OpCode == OpCodes.Ldarg_0 && !method.IsStatic) { // TODO: Handle generic methods/delegates // TODO: Handle multiple constructors propertly var constructor = method.DeclaringType.Methods.FirstOrDefault(x => x.Name == ".ctor" && !x.HasParameters); var retInstruction3 = constructor?.Body.Instructions.FirstOrDefault(x => x.OpCode == OpCodes.Ret); if (retInstruction3 == null) { return(false); } // Create an instance field for the shared delegate var sharedDelegateField = new FieldDefinition($"<delegate>{delegateMethod.Name}", FieldAttributes.Private, delegateInstanceType); method.DeclaringType.Fields.Add(sharedDelegateField); // Create and store the delegate in constructor var ilProcessor5 = constructor.Body.GetILProcessor(); ilProcessor5.InsertBefore(retInstruction3, ilProcessor5.Create(OpCodes.Ldarg_0)); ilProcessor5.InsertBefore(retInstruction3, ilProcessor5.Create(OpCodes.Ldarg_0)); ilProcessor5.InsertBefore(retInstruction3, ilProcessor5.Create(OpCodes.Ldftn, delegateMethod)); ilProcessor5.InsertBefore(retInstruction3, ilProcessor5.Create(OpCodes.Newobj, delegateInstanceConstructor)); ilProcessor5.InsertBefore(retInstruction3, ilProcessor5.Create(OpCodes.Stfld, sharedDelegateField)); // Load from field instead of allocating var ilProcessor4 = method.Body.GetILProcessor(); ilProcessor4.Remove(functionPointerInstruction); ilProcessor4.Replace(delegateAllocationInstruction, ilProcessor4.Create(OpCodes.Ldfld, sharedDelegateField)); return(true); } // If the target is a compiler generated closure, we only handle local variable load instructions int variableIndex; OpCode storeOpCode; if (!TryGetStoreOpcode(loadClosureInstruction, out storeOpCode, out variableIndex)) { return(false); } // Find the instruction that stores the closure variable var storeClosureInstruction = loadClosureInstruction.Previous; VariableReference closureVarible = null; while (storeClosureInstruction != null) { closureVarible = storeClosureInstruction.Operand as VariableReference; if (storeClosureInstruction.OpCode == storeOpCode && (closureVarible == null || variableIndex == closureVarible.Index)) { break; } storeClosureInstruction = storeClosureInstruction.Previous; } if (storeClosureInstruction == null) { return(false); } var closureInstanceType = method.Body.Variables[variableIndex].VariableType; var closureType = closureInstanceType.Resolve(); var genericParameters = closureType.GenericParameters.Cast <TypeReference>().ToArray(); // Patch closure var closure = ProcessClosure(context, closureType, genericParameters); // Create delegate field var delegateFieldType = ChangeGenericArguments(context, delegateInstanceType, closureInstanceType); var delegateField = new FieldDefinition($"<delegate>{delegateMethod.Name}", FieldAttributes.Public, delegateFieldType); closureType.Fields.Add(delegateField); var localDelegateFieldInstance = delegateField.MakeGeneric(genericParameters); // Initialize delegate field (the closure instance (local 0) is already on the stack) var delegateConstructorInstance = (MethodReference)delegateAllocationInstruction.Operand; var delegateGenericArguments = (delegateFieldType as GenericInstanceType)?.GenericArguments.ToArray() ?? new TypeReference[0]; var genericDelegateConstructor = context.Assembly.MainModule.ImportReference(delegateConstructorInstance.Resolve()).MakeGeneric(delegateGenericArguments); var methodInstance = (MethodReference)functionPointerInstruction.Operand; var genericMethod = methodInstance.Resolve().MakeGeneric(closureType.GenericParameters.ToArray()); if (methodInstance is GenericInstanceMethod) { throw new NotImplementedException(); } var ilProcessor3 = closure.FactoryMethod.Body.GetILProcessor(); var returnInstruction = ilProcessor3.Body.Instructions.FirstOrDefault(x => x.OpCode == OpCodes.Ret); ilProcessor3.InsertBefore(returnInstruction, ilProcessor3.Create(OpCodes.Ldloc_0)); ilProcessor3.InsertBefore(returnInstruction, ilProcessor3.Create(OpCodes.Ldftn, genericMethod)); ilProcessor3.InsertBefore(returnInstruction, ilProcessor3.Create(OpCodes.Newobj, genericDelegateConstructor)); ilProcessor3.InsertBefore(returnInstruction, ilProcessor3.Create(OpCodes.Stfld, localDelegateFieldInstance)); ilProcessor3.InsertBefore(returnInstruction, ilProcessor3.Create(OpCodes.Ldloc_0)); var ilProcessor = method.Body.GetILProcessor(); // Retrieve from pool var closureGenericArguments = (closureInstanceType as GenericInstanceType)?.GenericArguments.ToArray() ?? new TypeReference[0]; var closureAllocation = storeClosureInstruction.Previous; if (closureAllocation.OpCode == OpCodes.Newobj) { // Retrieve closure from pool, instead of allocating var acquireClosure = ilProcessor.Create(OpCodes.Callvirt, poolAcquireMethod.MakeGeneric(closureInstanceType)); ilProcessor.InsertAfter(closureAllocation, acquireClosure); ilProcessor.InsertAfter(closureAllocation, ilProcessor.Create(OpCodes.Ldsfld, closure.PoolField.MakeGeneric(closureGenericArguments))); closureAllocation.OpCode = OpCodes.Nop; // Change to Nop instead of removing it, as this instruction might be reference somewhere? closureAllocation.Operand = null; // Add a reference ilProcessor.InsertAfter(storeClosureInstruction, ilProcessor.Create(OpCodes.Callvirt, closure.AddReferenceMethod.MakeGeneric(closureGenericArguments))); ilProcessor.InsertAfter(storeClosureInstruction, closureVarible == null ? ilProcessor.Create(loadClosureInstruction.OpCode) : ilProcessor.Create(loadClosureInstruction.OpCode, closureVarible.Resolve())); // TODO: Multiple returns + try/finally // Release reference var retInstructions = method.Body.Instructions.Where(x => x.OpCode == OpCodes.Ret).ToArray(); Instruction beforeReturn = closureVarible == null?ilProcessor.Create(loadClosureInstruction.OpCode) : ilProcessor.Create(loadClosureInstruction.OpCode, closureVarible.Resolve()); Instruction newReturnInstruction = ilProcessor.Create(OpCodes.Ret); ilProcessor.Append(beforeReturn); ilProcessor.Append(ilProcessor.Create(OpCodes.Ldnull)); ilProcessor.Append(ilProcessor.Create(OpCodes.Beq, newReturnInstruction)); ilProcessor.Append(closureVarible == null ? ilProcessor.Create(loadClosureInstruction.OpCode) : ilProcessor.Create(loadClosureInstruction.OpCode, closureVarible.Resolve())); ilProcessor.Append(ilProcessor.Create(OpCodes.Callvirt, closure.ReleaseMethod.MakeGeneric(closureGenericArguments))); ilProcessor.Append(newReturnInstruction); foreach (var retInstruction2 in retInstructions) { retInstruction2.OpCode = OpCodes.Br; retInstruction2.Operand = beforeReturn; } } // Get delegate from closure, instead of allocating ilProcessor.Remove(functionPointerInstruction); ilProcessor.Replace(delegateAllocationInstruction, ilProcessor.Create(OpCodes.Ldfld, delegateField.MakeGeneric(closureGenericArguments))); // Closure object is already on the stack 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 == "Xenko.Rendering.ParameterKey") { break; } // TODO: Get PropertyKey.PropertyType instead var genericInstance = fieldBaseType as GenericInstanceType; if (genericInstance != null && genericInstance.ElementType.FullName == "Xenko.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 Xenko methods/attributes if (parameterKeysMergeMethod == null) { AssemblyDefinition xenkoEngineAssembly; try { xenkoEngineAssembly = assembly.Name.Name == "Xenko" ? assembly : context.AssemblyResolver.Resolve(new AssemblyNameReference("Xenko", null)); } catch (Exception) { context.Log.WriteLine("Error, cannot find [Xenko] assembly for processing ParameterKeyProcessor"); // We can't generate an exception, so we are just returning. It means that Xenko has not been generated so far. return(true); } var parameterKeysType = xenkoEngineAssembly.MainModule.GetTypes().First(x => x.Name == "ParameterKeys"); parameterKeysMergeMethod = parameterKeysType.Methods.First(x => x.Name == "Merge"); assemblyEffectKeysAttributeType = xenkoEngineAssembly.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 xenkoAssembly = assembly.Name.Name == "Xenko" ? assembly : assembly.MainModule.AssemblyResolver.Resolve(new AssemblyNameReference("Xenko", null)); var parameterCollectionType = xenkoAssembly.MainModule.GetTypeResolved("Xenko.Rendering.ParameterCollection"); var internalValueArrayType = parameterCollectionType.NestedTypes.First(x => x.Name == "InternalValueArray`1"); var constructor = internalValueArrayType.GetConstructors().First(); var internalValueArrayConstructor = xenkoAssembly.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) { isInitialized = false; bool changed = false; foreach (var type in context.Assembly.MainModule.GetAllTypes().ToArray()) { foreach (var method in type.Methods.ToArray()) { if (method.Body == null) { continue; } foreach (var instruction in method.Body.Instructions.ToArray()) { if (instruction.OpCode != OpCodes.Call && instruction.OpCode != OpCodes.Callvirt) { continue; } var calledMethod = ((MethodReference)instruction.Operand).Resolve(); if (calledMethod == null) { continue; } for (int parameterIndex = 0; parameterIndex < calledMethod.Parameters.Count; parameterIndex++) { var parameter = calledMethod.Parameters[parameterIndex]; // Parameter must be decorated with PooledAttribute if (parameter.CustomAttributes.All(x => x.AttributeType.FullName != "Xenko.Core.Threading.PooledAttribute")) { continue; } // Parameter must be a delegate if (parameter.ParameterType.Resolve().BaseType.FullName != typeof(MulticastDelegate).FullName) { continue; } // Find the instruction that pushes the parameter on the stack // Non-trivial control flow is not supported var pushParameterInstruction = WalkStack(instruction, calledMethod.Parameters.Count - parameterIndex); // Try to replace delegate and closure allocations { EnsureInitialized(context); changed |= ProcessDelegateAllocation(context, method, pushParameterInstruction); } } } } } return(changed); }
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 xenkoCoreModule; try { xenkoCoreModule = assembly.GetXenkoCoreModule(); } catch (Exception) { return(true); } propertyChangedExtendedEventArgsType = xenkoCoreModule.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); }
public bool Process(AssemblyProcessorContext context) { if (context.Platform != Core.PlatformType.Android && context.Platform != Core.PlatformType.iOS) { return(false); } bool changed = false; foreach (var type in context.Assembly.MainModule.GetAllTypes()) { foreach (var method in type.Methods) { if (method.Body == null) { continue; } for (var index = 0; index < method.Body.Instructions.Count; index++) { var instruction = method.Body.Instructions[index]; if (instruction.OpCode == OpCodes.Conv_U) { VariableDefinition variable = null; // Check if next store is in a pointer variable switch (instruction.Next.OpCode.Code) { case Code.Stloc_0: variable = method.Body.Variables[0]; break; case Code.Stloc_1: variable = method.Body.Variables[1]; break; case Code.Stloc_2: variable = method.Body.Variables[2]; break; case Code.Stloc_3: variable = method.Body.Variables[3]; break; case Code.Stloc: variable = (VariableDefinition)instruction.Operand; break; } if (variable != null && variable.VariableType.IsPointer) { // We are in a fixed instruction, let's fix it instruction.OpCode = OpCodes.Conv_I; changed = true; } } } } } return(changed); }