/// <summary> /// Applies the patch described in the given PatchingManifest to the TargetAssembly. /// </summary> /// <param name="manifest">The PatchingManifest. Note that the instance will be populated with additional information (such as newly created types) during execution.</param> /// <param name="o"></param> public void PatchManifest(PatchingManifest manifest, IProgressMonitor o) { /* The point of this method is to introduce all the patched elements in the correct order. * * Standard order of business for order between modify/remove/update: * 1. Remove Xs * 2. Create new Xs * 3. Update existing Xs (+ those we just created) */ manifest.Specialize(TargetAssembly); o.TaskTitle = $"{manifest.PatchAssembly.Name.Name}"; o.Current = 0; o.Total = 11; AssemblyDefinition targetAssemblyBackup = null; if (UseBackup) { targetAssemblyBackup = TargetAssembly.Clone(); } try { CurrentMemberCache = manifest.CreateCache(); //+INTRODUCE NEW TYPES //Declare all the new types, in the order of their nesting level (.e.g topmost types, nested types, types nested in those, etc), //But don't set anything that references other things, like BaseType, interfaces, custom attributes, etc. //Type parameters *are* added at this stage, but no constraints are specified. //DEPENDENCIES: None o.TaskText = "Introducing Types"; IntroduceTypes(manifest.TypeActions); o.Current++; //+REMOVE EXISTING METHODS //Remove any methods that need removing. //DEPENDENCIES: Probably none o.TaskText = "Removing Methods"; RemoveMethods(manifest.MethodActions); o.Current++; //+INTRODUCE NEW METHOD AND METHOD TYPE DEFINITIONS //Declare all the new methods and their type parameters, but do not set nything that references other types, //including return type, parameter types, and type parameter constraints //This is because method type variables must already exist. //This is performed now because types can reference attribute constructors that don't exist until we create them, //Don't set custom attributes in this stage. //DEPENDENCIES: Type definitions (when introduced to new types) o.TaskText = "Introducing Methods"; IntroduceMethods(manifest.MethodActions); o.Current++; //+UPDATE METHOD DECLERATIONS //Update method declerations with parameters and return types //We do this separately, because a method's parameters and return types depend on that method's generic parameters //Don't set custom attributes in this stage though it's possible to do so. //This is to avoid code duplication. //DEPENDENCIES: Type definitions, method definitions o.TaskText = "Updating Type Declerations"; UpdateMethodDeclerations(manifest.MethodActions); o.Current++; //+IMPORT ASSEMBLY/MODULE CUSTOM ATTRIBUTES //Add any custom attributes for things that aren't part of the method/type hierarchy, such as assemblies. //DEPENDENCIES: Method definitions, type definitions. (for attribute types and attribute constructors) o.TaskText = "Importing Assembly/Module Attributes"; var patchingAssembly = manifest.PatchAssembly; //for assmebly: CopyCustomAttributesByImportAttribute(TargetAssembly, patchingAssembly, patchingAssembly.GetCustomAttribute <ImportCustomAttributesAttribute>()); //for module: CopyCustomAttributesByImportAttribute(TargetAssembly.MainModule, patchingAssembly.MainModule, patchingAssembly.MainModule.GetCustomAttribute <ImportCustomAttributesAttribute>()); o.Current++; //+UPDATE TYPE DEFINITIONS //Set the type information we didn't set in (1), according to the same order, including custom attributes. //When/if modifying type inheritance is allowed, this process will occur at this stage. //DEPENDENCIES: Type definitions (obviously), method definitions (for attribute constructors) o.TaskText = "Updating Type Definitions"; UpdateTypes(manifest.TypeActions); o.Current++; //+FIELDS //remove/define/modify fields. This is where most enum processing happens. Custom attributes are set at this stage. //DEPENDENCIES: Type definitions, method definitions (for attribute constructors) o.TaskText = "Processing Fields"; ClearReplacedTypes(manifest.TypeActions); RemoveFields(manifest.FieldActions); IntroduceFields(manifest.FieldActions); UpdateFields(manifest.FieldActions); o.Current++; //+PROPERTIES //Remove/define/modify properties. This doesn't change anything about methods. Custom attributes are set at this stage. //DEPENDENCIES: Type definitions, method definitions (attribute constructors, getter/setters) o.TaskText = "Processing Properties"; RemoveProperties(manifest.PropertyActions); IntroduceProperties(manifest.PropertyActions); UpdateProperties(manifest.PropertyActions); o.Current++; //+EVENTS //Remove/define/modify events. This doesn't change anything about methods. Custom attributes are set at this stage. //DEPENENCIES: Type definitions, method definitions (attribute constructors, add/remove/invoke handlers) o.TaskText = "Introducing Events"; RemoveEvents(manifest.EventActions); IntroduceEvents(manifest.EventActions); UpdateEvents(manifest.EventActions); o.Current++; //+FINALIZE METHODS, GENERATE METHOD BODIES //Fill/modify the bodies of all remaining methods, and also modify accessibility/attributes of existing ones. //Note that methods that are the source of DuplicatesBody will be resolved in their unmodified version, //so the modifications won't influence this functionality. //Custom attributes are set at this stage. //Also, explicit overrides (what in C# is explicit interface implementation) are specified here. //DEPENDENCIES: Type definitions, method definitions, method signature elements, field definitions o.TaskText = "Updating Method Bodies"; UpdateMethods(manifest.MethodActions); o.Current++; //+ADD PATCHING HISTORY TO ASSEMBLY o.TaskText = "Updating History"; if (EmbedHistory) { TargetAssembly.AddPatchedByAssemblyAttribute(manifest.PatchAssembly, _assemblyHistoryIndex++, OriginalAssemblyMetadata); } o.Current++; } catch { if (UseBackup) { TargetAssembly = targetAssemblyBackup; } throw; } }
/// <summary> /// Reads the specified assembly definition and creates a manifest. /// </summary> /// <param name="yourAssembly">The patch assembly to read.</param> /// <returns></returns> public PatchingManifest CreateManifest(AssemblyDefinition yourAssembly) { var multicast = yourAssembly.GetCustomAttributes <DisablePatchingByNameAttribute>(); var filter = CreateMemberFilter(multicast); var patchAssemblyAttr = yourAssembly.GetCustomAttribute <PatchAssemblyAttribute>(); if (patchAssemblyAttr == null) { throw Errors.No_patch_assembly_attribute(yourAssembly); } var patchInfoTypeRefs = yourAssembly.MainModule.Types.Where( x => x.CustomAttributes.Any(y => y.AttributeType.FullName == typeof(PatchInfoAttribute).FullName)) .ToList (); if (patchInfoTypeRefs.Count > 1) { throw Errors.More_than_one_PatchInfoAttribute(yourAssembly); } var infoTypeRef = patchInfoTypeRefs.FirstOrDefault(); PatchInfoProxy info = null; if (infoTypeRef != null) { var infoTypeDef = infoTypeRef.Resolve(); info = PatchInfoProxy.FromPatchAssembly(infoTypeDef.Module.FullyQualifiedName, infoTypeDef.Module.Assembly.Name.Name, infoTypeDef.FullName); Log.Information("PatchInfo class successfuly loaded."); } else { Log.Warning($"This assembly does not have a class decorated with {nameof(PatchInfoAttribute)} so it cannot participate in automatic patching."); } var allTypesInOrder = GetAllTypesInNestingOrder(yourAssembly.MainModule.Types).ToList(); //+ Organizing types by action attribute var typesByActionSeq = from type in allTypesInOrder let typeActionAttr = GetTypeActionAttribute(type) where typeActionAttr != null && Filter(type) && filter(type) let patchedTypeName = type.GetPatchedTypeFullName() group new TypeAction() { YourType = type, ActionAttribute = typeActionAttr, } by typeActionAttr.GetType(); var types = typesByActionSeq.ToSimpleTypeLookup(); var fields = OrganizeMembers(types, x => x.Fields, filter); var methods = OrganizeMembers(types, x => x.Methods, filter); var properties = OrganizeMembers(types, x => x.Properties, filter); var events = OrganizeMembers(types, x => x.Events, filter); foreach (var eventAction in events[typeof(NewMemberAttribute)]) { ImplicitlyAddNewMethods(methods, eventAction, vent => { return(new[] { vent.AddMethod, vent.RemoveMethod, vent.InvokeMethod, }.Concat(vent.OtherMethods).Where(method => method != null)); }); } foreach (var propAction in properties[typeof(NewMemberAttribute)]) { ImplicitlyAddNewMethods(methods, propAction, prop => { return(new[] { prop.GetMethod, prop.SetMethod, }.Concat(prop.OtherMethods).Where(method => method != null)); }); } var patchingManifest = new PatchingManifest() { EventActions = events, FieldActions = fields, PropertyActions = properties, MethodActions = methods, TypeActions = types, PatchAssembly = yourAssembly, PatchInfo = info }; return(patchingManifest); }