/// <summary> /// Transpiles a method to replace instances of one constant value with another. /// /// Note that values of type byte, short, char, and bool are also represented with "i4" /// constants which can be targeted by this method. /// </summary> /// <param name="method">The method to patch.</param> /// <param name="oldValue">The old constant to remove.</param> /// <param name="newValue">The new constant to replace.</param> /// <param name="all">true to replace all instances, or false to replace the first /// instance (default).</param> /// <returns>A transpiled version of that method which replaces instances of the first /// constant with that of the second.</returns> public static TranspiledMethod ReplaceConstant(TranspiledMethod method, int oldValue, int newValue, bool all = false) { int replaced = 0; bool quickCode = oldValue >= -1 && oldValue <= 8; var qc = OpCodes.Nop; if (method == null) { throw new ArgumentNullException("method"); } // Quick test for the opcode on the shorthand forms if (quickCode) { qc = PTranspilerTools.LOAD_INT[oldValue + 1]; } foreach (var inst in method) { var instruction = inst; var opcode = instruction.opcode; object operand = instruction.operand; if ((opcode == OpCodes.Ldc_I4 && (operand is int ival) && ival == oldValue) || (opcode == OpCodes.Ldc_I4_S && (operand is byte bval) && bval == oldValue) || (quickCode && qc == opcode)) { // Replace instruction if first instance, or all to be replaced if (all || replaced == 0) { PTranspilerTools.ModifyLoadI4(instruction, newValue); } replaced++; } yield return(instruction); } }
/// <summary> /// Transpiles a method to replace calls to the specified victim methods with /// replacement methods, altering the call type if necessary. /// /// Each key to value pair must meet the criteria defined in /// ReplaceMethodCall(TranspiledMethod, MethodInfo, MethodInfo). /// </summary> /// <param name="method">The method to patch.</param> /// <param name="translation">A mapping from the old method calls to replace, to the /// new method calls to use instead.</param> /// <returns>A transpiled version of that method that replaces or removes all calls /// to the specified methods.</returns> /// <exception cref="ArgumentException">If any of the new methods' argument types do /// not exactly match the old methods' argument types.</exception> public static TranspiledMethod ReplaceMethodCall(TranspiledMethod method, IDictionary <MethodInfo, MethodInfo> translation) { if (method == null) { throw new ArgumentNullException("method"); } if (translation == null) { throw new ArgumentNullException("translation"); } // Sanity check arguments int replaced = 0; foreach (var pair in translation) { var victim = pair.Key; var newMethod = pair.Value; if (victim == null) { throw new ArgumentNullException("victim"); } if (newMethod != null) { PTranspilerTools.CompareMethodParams(victim, victim.GetParameterTypes(), newMethod); } else if (victim.ReturnType != typeof(void)) { throw new ArgumentException("Cannot remove method {0} with a return value". F(victim.Name)); } } foreach (var instruction in method) { var opcode = instruction.opcode; MethodInfo target; if ((opcode == OpCodes.Call || opcode == OpCodes.Calli || opcode == OpCodes. Callvirt) && translation.TryGetValue(target = instruction.operand as MethodInfo, out MethodInfo newMethod)) { if (newMethod != null) { // Replace with new method instruction.opcode = newMethod.IsStatic ? OpCodes.Call : OpCodes.Callvirt; instruction.operand = newMethod; yield return(instruction); } else { // Pop "this" if needed int n = target.GetParameters().Length; if (!target.IsStatic) { n++; } // Pop the arguments off the stack instruction.opcode = (n == 0) ? OpCodes.Nop : OpCodes.Pop; instruction.operand = null; yield return(instruction); for (int i = 0; i < n - 1; i++) { yield return(new CodeInstruction(OpCodes.Pop)); } } replaced++; } else { yield return(instruction); } } #if DEBUG if (replaced == 0) { if (translation.Count == 1) { // Diagnose the method that could not be replaced var items = new KeyValuePair <MethodInfo, MethodInfo> [1]; translation.CopyTo(items, 0); MethodInfo from = items[0].Key, to = items[0].Value; PUtil.LogWarning("No method calls replaced: {0}.{1} to {2}.{3}".F( from.DeclaringType.FullName, from.Name, to.DeclaringType.FullName, to.Name)); } else { PUtil.LogWarning("No method calls replaced (multiple replacements)"); } } #endif }
public static void LogAllExceptions() { PUtil.LogWarning("PLib in mod " + Assembly.GetCallingAssembly().GetName()?.Name + " is logging ALL unhandled exceptions!"); PTranspilerTools.LogAllExceptions(); }
public static void LogAllFailedAsserts() { PUtil.LogWarning("PLib in mod " + Assembly.GetCallingAssembly().GetName()?.Name + " is logging ALL failed assertions!"); PTranspilerTools.LogAllFailedAsserts(); }
/// <summary> /// Checks to see if an instruction opcode is a branch instruction. /// </summary> /// <param name="opcode">The opcode to check.</param> /// <returns>true if it is a branch, or false otherwise.</returns> public static bool IsConditionalBranchInstruction(this OpCode opcode) { return(PTranspilerTools.IsConditionalBranchInstruction(opcode)); }