/// <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(nameof(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(nameof(method));
            }
            if (translation == null)
            {
                throw new ArgumentNullException(nameof(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(nameof(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));
 }