/********* ** Public methods *********/ /// <summary>Construct an instance.</summary> /// <param name="targetPlatform">The current game platform.</param> /// <param name="framework">The game framework running the game.</param> /// <param name="monitor">Encapsulates monitoring and logging.</param> /// <param name="paranoidMode">Whether to detect paranoid mode issues.</param> /// <param name="rewriteMods">Whether to rewrite mods for compatibility.</param> public AssemblyLoader(Platform targetPlatform, GameFramework framework, IMonitor monitor, bool paranoidMode, bool rewriteMods) { this.Monitor = monitor; this.ParanoidMode = paranoidMode; this.RewriteMods = rewriteMods; this.AssemblyMap = this.TrackForDisposal(Constants.GetAssemblyMap(targetPlatform, framework)); // init resolver this.AssemblyDefinitionResolver = this.TrackForDisposal(new AssemblyDefinitionResolver()); this.AssemblyDefinitionResolver.AddSearchDirectory(Constants.ExecutionPath); this.AssemblyDefinitionResolver.AddSearchDirectory(Constants.InternalFilesPath); // generate type => assembly lookup for types which should be rewritten this.TypeAssemblies = new Dictionary <string, Assembly>(); foreach (Assembly assembly in this.AssemblyMap.Targets) { ModuleDefinition module = this.AssemblyMap.TargetModules[assembly]; foreach (TypeDefinition type in module.GetTypes()) { if (!type.IsPublic) { continue; // no need to rewrite } if (type.Namespace.Contains("<")) { continue; // ignore assembly metadata } this.TypeAssemblies[type.FullName] = assembly; } } }
/********* ** Public methods *********/ /// <summary>Construct an instance.</summary> /// <param name="assemblyMap">Metadata for mapping assemblies to the current <see cref="Platform"/>.</param> /// <param name="monitor">Encapsulates monitoring and logging.</param> public AssemblyTypeRewriter(PlatformAssemblyMap assemblyMap, IMonitor monitor) { // save config this.AssemblyMap = assemblyMap; this.Monitor = monitor; // collect type => assembly lookup this.TypeAssemblies = new Dictionary <string, Assembly>(); foreach (Assembly assembly in assemblyMap.Targets) { ModuleDefinition module = this.AssemblyMap.TargetModules[assembly]; foreach (TypeDefinition type in module.GetTypes()) { if (!type.IsPublic) { continue; // no need to rewrite } if (type.Namespace.Contains("<")) { continue; // ignore assembly metadata } this.TypeAssemblies[type.FullName] = assembly; } } }
/********* ** Public methods *********/ /// <summary>Construct an instance.</summary> /// <param name="cacheDirName">The name of the directory containing a mod's cached data.</param> /// <param name="targetPlatform">The current game platform.</param> /// <param name="monitor">Encapsulates monitoring and logging.</param> public ModAssemblyLoader(string cacheDirName, Platform targetPlatform, IMonitor monitor) { this.CacheDirName = cacheDirName; this.Monitor = monitor; this.AssemblyMap = Constants.GetAssemblyMap(targetPlatform); this.AssemblyTypeRewriter = new AssemblyTypeRewriter(this.AssemblyMap, monitor); }
/********* ** Public methods *********/ /// <summary>Construct an instance.</summary> /// <param name="targetPlatform">The current game platform.</param> /// <param name="monitor">Encapsulates monitoring and logging.</param> public AssemblyLoader(Platform targetPlatform, IMonitor monitor) { this.Monitor = monitor; this.AssemblyMap = Constants.GetAssemblyMap(targetPlatform); // generate type => assembly lookup for types which should be rewritten this.TypeAssemblies = new Dictionary <string, Assembly>(); foreach (Assembly assembly in this.AssemblyMap.Targets) { ModuleDefinition module = this.AssemblyMap.TargetModules[assembly]; foreach (TypeDefinition type in module.GetTypes()) { if (!type.IsPublic) { continue; // no need to rewrite } if (type.Namespace.Contains("<")) { continue; // ignore assembly metadata } this.TypeAssemblies[type.FullName] = assembly; } } }
/// <summary>Perform the predefined logic for an instruction if applicable.</summary> /// <param name="module">The assembly module containing the instruction.</param> /// <param name="cil">The CIL processor.</param> /// <param name="instruction">The instruction to handle.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> public virtual InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { // field reference FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction); if (fieldRef != null && this.ShouldValidate(fieldRef.DeclaringType)) { FieldDefinition target = fieldRef.DeclaringType.Resolve()?.Fields.FirstOrDefault(p => p.Name == fieldRef.Name); if (target == null) { this.NounPhrase = $"reference to {fieldRef.DeclaringType.FullName}.{fieldRef.Name} (no such field)"; return(InstructionHandleResult.NotCompatible); } } // method reference MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); if (methodRef != null && this.ShouldValidate(methodRef.DeclaringType) && !this.IsUnsupported(methodRef)) { MethodDefinition target = methodRef.Resolve(); if (target == null) { if (this.IsProperty(methodRef)) { this.NounPhrase = $"reference to {methodRef.DeclaringType.FullName}.{methodRef.Name.Substring(4)} (no such property)"; } else if (methodRef.Name == ".ctor") { this.NounPhrase = $"reference to {methodRef.DeclaringType.FullName}.{methodRef.Name} (no matching constructor)"; } else { this.NounPhrase = $"reference to {methodRef.DeclaringType.FullName}.{methodRef.Name} (no such method)"; } return(InstructionHandleResult.NotCompatible); } } return(InstructionHandleResult.None); }
/// <summary>Rewrite a CIL instruction for compatibility.</summary> /// <param name="module">The module being rewritten.</param> /// <param name="cil">The CIL rewriter.</param> /// <param name="instruction">The instruction to rewrite.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> /// <returns>Returns whether the instruction was rewritten.</returns> /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception> public bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { if (!this.IsMatch(instruction, platformChanged)) { return(false); } MethodReference methodRef = (MethodReference)instruction.Operand; methodRef.DeclaringType = module.Import(this.ToType); return(true); }
/// <summary>Perform the predefined logic for a method if applicable.</summary> /// <param name="module">The assembly module containing the instruction.</param> /// <param name="method">The method definition containing the instruction.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> public override InstructionHandleResult Handle(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged) { bool rewritten = false; // return type if (this.IsMatch(method.ReturnType)) { this.RewriteIfNeeded(module, method.ReturnType, newType => method.ReturnType = newType); rewritten = true; } // parameters foreach (ParameterDefinition parameter in method.Parameters) { if (this.IsMatch(parameter.ParameterType)) { this.RewriteIfNeeded(module, parameter.ParameterType, newType => parameter.ParameterType = newType); rewritten = true; } } // generic parameters for (int i = 0; i < method.GenericParameters.Count; i++) { var parameter = method.GenericParameters[i]; if (this.IsMatch(parameter)) { this.RewriteIfNeeded(module, parameter, newType => method.GenericParameters[i] = new GenericParameter(parameter.Name, newType)); rewritten = true; } } // local variables foreach (VariableDefinition variable in method.Body.Variables) { if (this.IsMatch(variable.VariableType)) { this.RewriteIfNeeded(module, variable.VariableType, newType => variable.VariableType = newType); rewritten = true; } } return(rewritten ? InstructionHandleResult.Rewritten : InstructionHandleResult.None); }
/// <summary>Perform the predefined logic for a method if applicable.</summary> /// <param name="module">The assembly module containing the instruction.</param> /// <param name="method">The method definition containing the instruction.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> public virtual InstructionHandleResult Handle(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged) { return(InstructionHandleResult.None); }
/// <summary>Perform the predefined logic for an instruction if applicable.</summary> /// <param name="module">The assembly module containing the instruction.</param> /// <param name="cil">The CIL processor.</param> /// <param name="instruction">The instruction to handle.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> public virtual InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { return(this.IsMatch(instruction) ? this.Result : InstructionHandleResult.None); }
/// <summary>Perform the predefined logic for an instruction if applicable.</summary> /// <param name="module">The assembly module containing the instruction.</param> /// <param name="cil">The CIL processor.</param> /// <param name="instruction">The instruction to handle.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> public override InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { if (!this.IsMatch(instruction)) { return(InstructionHandleResult.None); } string methodPrefix = instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld ? "get" : "set"; MethodReference propertyRef = module.Import(this.Type.GetMethod($"{methodPrefix}_{this.PropertyName}")); cil.Replace(instruction, cil.Create(OpCodes.Call, propertyRef)); return(InstructionHandleResult.Rewritten); }
/// <summary>Rewrite a CIL instruction for compatibility.</summary> /// <param name="module">The module being rewritten.</param> /// <param name="cil">The CIL rewriter.</param> /// <param name="instruction">The instruction to rewrite.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> /// <returns>Returns whether the instruction was rewritten.</returns> /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception> public override bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { if (!this.IsMatch(instruction) && !instruction.ToString().Contains(this.FromTypeName)) { return(false); } // field reference FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction); if (fieldRef != null) { fieldRef.DeclaringType = this.RewriteIfNeeded(module, fieldRef.DeclaringType); fieldRef.FieldType = this.RewriteIfNeeded(module, fieldRef.FieldType); } // method reference MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); if (methodRef != null) { methodRef.DeclaringType = this.RewriteIfNeeded(module, methodRef.DeclaringType); methodRef.ReturnType = this.RewriteIfNeeded(module, methodRef.ReturnType); foreach (var parameter in methodRef.Parameters) { parameter.ParameterType = this.RewriteIfNeeded(module, parameter.ParameterType); } } // type reference if (instruction.Operand is TypeReference typeRef) { TypeReference newRef = this.RewriteIfNeeded(module, typeRef); if (typeRef != newRef) { cil.Replace(instruction, cil.Create(instruction.OpCode, newRef)); } } return(true); }
/// <summary>Perform the predefined logic for an instruction if applicable.</summary> /// <param name="module">The assembly module containing the instruction.</param> /// <param name="cil">The CIL processor.</param> /// <param name="instruction">The instruction to handle.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> public override InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { if (!this.IsMatch(instruction)) { return(InstructionHandleResult.None); } //Instruction: IL_0025: ldsfld StardewValley.GameLocation StardewValley.Game1::currentLocation string methodPrefix = instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld ? "get" : "set"; try { //MethodReference propertyRef = module.ImportReference(this.ToType.GetMethod($"{methodPrefix}_{this.PropertyName}")); MethodReference method = module.ImportReference(this.ToType.GetMethod($"{methodPrefix}_{this.PropertyName}")); this.Monitor.Log("Method Ref: " + method.ToString()); cil.Replace(instruction, cil.Create(OpCodes.Call, method)); } catch (Exception e) { this.Monitor.Log(e.Message); } return(InstructionHandleResult.Rewritten); }
/// <summary>Perform the predefined logic for an instruction if applicable.</summary> /// <param name="module">The assembly module containing the instruction.</param> /// <param name="cil">The CIL processor.</param> /// <param name="instruction">The instruction to handle.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> public override InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { if (!this.IsMatch(instruction)) { return(InstructionHandleResult.None); } cil.Replace(instruction, this.CreateConstantInstruction(cil, this.Value)); return(InstructionHandleResult.Rewritten); }
/// <summary>Rewrite a method definition for compatibility.</summary> /// <param name="module">The module being rewritten.</param> /// <param name="method">The method definition to rewrite.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> /// <returns>Returns whether the instruction was rewritten.</returns> /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception> public virtual bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged) { if (!this.IsMatch(method)) { return(false); } throw new IncompatibleInstructionException(this.NounPhrase); }
/// <summary>Perform the predefined logic for an instruction if applicable.</summary> /// <param name="module">The assembly module containing the instruction.</param> /// <param name="cil">The CIL processor.</param> /// <param name="instruction">The instruction to handle.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> public override InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { if (!this.IsMatch(instruction)) { return(InstructionHandleResult.None); } // field reference FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction); if (fieldRef != null) { this.RewriteIfNeeded(module, fieldRef.DeclaringType, newType => fieldRef.DeclaringType = newType); this.RewriteIfNeeded(module, fieldRef.FieldType, newType => fieldRef.FieldType = newType); } // method reference MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); if (methodRef != null) { this.RewriteIfNeeded(module, methodRef.DeclaringType, newType => methodRef.DeclaringType = newType); this.RewriteIfNeeded(module, methodRef.ReturnType, newType => methodRef.ReturnType = newType); foreach (var parameter in methodRef.Parameters) { this.RewriteIfNeeded(module, parameter.ParameterType, newType => parameter.ParameterType = newType); } } // type reference if (instruction.Operand is TypeReference typeRef) { this.RewriteIfNeeded(module, typeRef, newType => cil.Replace(instruction, cil.Create(instruction.OpCode, newType))); } return(InstructionHandleResult.Rewritten); }
/// <summary>Rewrite a method definition for compatibility.</summary> /// <param name="module">The module being rewritten.</param> /// <param name="method">The method definition to rewrite.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> /// <returns>Returns whether the instruction was rewritten.</returns> /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception> public override bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged) { bool rewritten = false; // return type if (this.IsMatch(method.ReturnType)) { method.ReturnType = this.RewriteIfNeeded(module, method.ReturnType); rewritten = true; } // parameters foreach (ParameterDefinition parameter in method.Parameters) { if (this.IsMatch(parameter.ParameterType)) { parameter.ParameterType = this.RewriteIfNeeded(module, parameter.ParameterType); rewritten = true; } } // generic parameters for (int i = 0; i < method.GenericParameters.Count; i++) { var parameter = method.GenericParameters[i]; if (this.IsMatch(parameter)) { TypeReference newType = this.RewriteIfNeeded(module, parameter); if (newType != parameter) { method.GenericParameters[i] = new GenericParameter(parameter.Name, newType); } rewritten = true; } } // local variables foreach (VariableDefinition variable in method.Body.Variables) { if (this.IsMatch(variable.VariableType)) { variable.VariableType = this.RewriteIfNeeded(module, variable.VariableType); rewritten = true; } } return(rewritten); }
/// <summary>Rewrite a method for compatibility.</summary> /// <param name="module">The module being rewritten.</param> /// <param name="cil">The CIL rewriter.</param> /// <param name="callOp">The instruction which calls the method.</param> /// <param name="methodRef">The method reference invoked by the <paramref name="callOp"/>.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> public abstract void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction callOp, MethodReference methodRef, PlatformAssemblyMap assemblyMap);
/// <summary>Perform the predefined logic for an instruction if applicable.</summary> /// <param name="module">The assembly module containing the instruction.</param> /// <param name="cil">The CIL processor.</param> /// <param name="instruction">The instruction to handle.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> public override InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { if (!this.IsMatch(instruction)) { return(InstructionHandleResult.None); } FieldReference newRef = module.ImportReference(this.ToField); cil.Replace(instruction, cil.Create(instruction.OpCode, newRef)); return(InstructionHandleResult.Rewritten); }
/// <summary>Perform the predefined logic for an instruction if applicable.</summary> /// <param name="module">The assembly module containing the instruction.</param> /// <param name="cil">The CIL processor.</param> /// <param name="instruction">The instruction to handle.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> public InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { if (!this.IsMatch(instruction, platformChanged)) { return(InstructionHandleResult.None); } MethodReference methodRef = (MethodReference)instruction.Operand; methodRef.DeclaringType = module.ImportReference(this.ToType); return(InstructionHandleResult.Rewritten); }
/// <summary>Perform the predefined logic for an instruction if applicable.</summary> /// <param name="module">The assembly module containing the instruction.</param> /// <param name="cil">The CIL processor.</param> /// <param name="instruction">The instruction to handle.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> public virtual InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { // field reference FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction); if (fieldRef != null && this.ShouldValidate(fieldRef.DeclaringType)) { // get target field FieldDefinition targetField = fieldRef.DeclaringType.Resolve()?.Fields.FirstOrDefault(p => p.Name == fieldRef.Name); if (targetField == null) { return(InstructionHandleResult.None); } // validate return type if (!RewriteHelper.LooksLikeSameType(fieldRef.FieldType, targetField.FieldType)) { this.NounPhrase = $"reference to {fieldRef.DeclaringType.FullName}.{fieldRef.Name} (field returns {this.GetFriendlyTypeName(targetField.FieldType)}, not {this.GetFriendlyTypeName(fieldRef.FieldType)})"; return(InstructionHandleResult.NotCompatible); } } // method reference MethodReference methodReference = RewriteHelper.AsMethodReference(instruction); if (methodReference != null && !this.IsUnsupported(methodReference) && this.ShouldValidate(methodReference.DeclaringType)) { // get potential targets MethodDefinition[] candidateMethods = methodReference.DeclaringType.Resolve()?.Methods.Where(found => found.Name == methodReference.Name).ToArray(); if (candidateMethods == null || !candidateMethods.Any()) { return(InstructionHandleResult.None); } // compare return types MethodDefinition methodDef = methodReference.Resolve(); if (methodDef == null) { this.NounPhrase = $"reference to {methodReference.DeclaringType.FullName}.{methodReference.Name} (no such method)"; return(InstructionHandleResult.NotCompatible); } if (candidateMethods.All(method => !RewriteHelper.LooksLikeSameType(method.ReturnType, methodDef.ReturnType))) { this.NounPhrase = $"reference to {methodDef.DeclaringType.FullName}.{methodDef.Name} (no such method returns {this.GetFriendlyTypeName(methodDef.ReturnType)})"; return(InstructionHandleResult.NotCompatible); } } return(InstructionHandleResult.None); }
/// <summary>Rewrite a CIL instruction for compatibility.</summary> /// <param name="module">The module being rewritten.</param> /// <param name="cil">The CIL rewriter.</param> /// <param name="instruction">The instruction to rewrite.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> /// <returns>Returns whether the instruction was rewritten.</returns> /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception> public virtual bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { if (!this.IsMatch(instruction)) { return(false); } throw new IncompatibleInstructionException(this.NounPhrase); }
/// <summary>Rewrite a method for compatibility.</summary> /// <param name="module">The module being rewritten.</param> /// <param name="cil">The CIL rewriter.</param> /// <param name="callOp">The instruction which calls the method.</param> /// <param name="methodRef">The method reference invoked by the <paramref name="callOp"/>.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> public override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction callOp, MethodReference methodRef, PlatformAssemblyMap assemblyMap) { methodRef.DeclaringType = module.Import(typeof(CompatibleSpriteBatch)); }
/// <summary>Rewrite a method definition for compatibility.</summary> /// <param name="module">The module being rewritten.</param> /// <param name="method">The method definition to rewrite.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> /// <returns>Returns whether the instruction was rewritten.</returns> /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception> public bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged) { return(false); }
/// <summary>Perform the predefined logic for an instruction if applicable.</summary> /// <param name="module">The assembly module containing the instruction.</param> /// <param name="cil">The CIL processor.</param> /// <param name="instruction">The instruction to handle.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> public virtual InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { // field reference FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction); if (fieldRef != null && this.ShouldValidate(fieldRef.DeclaringType)) { // can't compare generic type parameters between definition and reference if (fieldRef.FieldType.IsGenericInstance || fieldRef.FieldType.IsGenericParameter) { return(InstructionHandleResult.None); } // get target field FieldDefinition targetField = fieldRef.DeclaringType.Resolve()?.Fields.FirstOrDefault(p => p.Name == fieldRef.Name); if (targetField == null) { return(InstructionHandleResult.None); } // validate return type string actualReturnTypeID = this.GetComparableTypeID(targetField.FieldType); string expectedReturnTypeID = this.GetComparableTypeID(fieldRef.FieldType); if (actualReturnTypeID != expectedReturnTypeID) { this.NounPhrase = $"reference to {fieldRef.DeclaringType.FullName}.{fieldRef.Name} (field returns {this.GetFriendlyTypeName(targetField.FieldType, actualReturnTypeID)}, not {this.GetFriendlyTypeName(fieldRef.FieldType, expectedReturnTypeID)})"; return(InstructionHandleResult.NotCompatible); } } // method reference MethodReference methodReference = RewriteHelper.AsMethodReference(instruction); if (methodReference != null && this.ShouldValidate(methodReference.DeclaringType)) { // can't compare generic type parameters between definition and reference if (methodReference.ReturnType.IsGenericInstance || methodReference.ReturnType.IsGenericParameter) { return(InstructionHandleResult.None); } // get potential targets MethodDefinition[] candidateMethods = methodReference.DeclaringType.Resolve()?.Methods.Where(found => found.Name == methodReference.Name).ToArray(); if (candidateMethods == null || !candidateMethods.Any()) { return(InstructionHandleResult.None); } // compare return types MethodDefinition methodDef = methodReference.Resolve(); if (methodDef == null) { this.NounPhrase = $"reference to {methodReference.DeclaringType.FullName}.{methodReference.Name} (no such method)"; return(InstructionHandleResult.NotCompatible); } string expectedReturnType = this.GetComparableTypeID(methodDef.ReturnType); if (candidateMethods.All(method => this.GetComparableTypeID(method.ReturnType) != expectedReturnType)) { this.NounPhrase = $"reference to {methodDef.DeclaringType.FullName}.{methodDef.Name} (no such method returns {this.GetFriendlyTypeName(methodDef.ReturnType, expectedReturnType)})"; return(InstructionHandleResult.NotCompatible); } } return(InstructionHandleResult.None); }
/// <summary>Perform the predefined logic for an instruction if applicable.</summary> /// <param name="module">The assembly module containing the instruction.</param> /// <param name="cil">The CIL processor.</param> /// <param name="instruction">The instruction to handle.</param> /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param> /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param> public InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { if (!this.IsMatch(instruction)) { return(InstructionHandleResult.None); } // get instructions comprising method call int index = cil.Body.Instructions.IndexOf(instruction); Instruction loadArg0 = cil.Body.Instructions[index - 2]; Instruction loadArg1 = cil.Body.Instructions[index - 1]; if (loadArg0.OpCode != OpCodes.Ldarg_0) { throw new InvalidOperationException($"Unexpected instruction sequence while removing virtual {this.ToType.Name}.{this.MethodName} call: found {loadArg0.OpCode.Name} instead of {OpCodes.Ldarg_0.Name}"); } if (loadArg1.OpCode != OpCodes.Ldarg_1) { throw new InvalidOperationException($"Unexpected instruction sequence while removing virtual {this.ToType.Name}.{this.MethodName} call: found {loadArg1.OpCode.Name} instead of {OpCodes.Ldarg_1.Name}"); } // remove method call cil.Remove(loadArg0); cil.Remove(loadArg1); cil.Remove(instruction); return(InstructionHandleResult.Rewritten); }