public virtual bool SearchMethod(MethodInfo searchMethod, List <MethodInfo> targets, out List <ItemPos <MethodInfo> > Results)
        {
            Results = new List <ItemPos <MethodInfo> >();
            List <CodeInstruction> instructionList;

            try { instructionList = PatchProcessor.GetCurrentInstructions(searchMethod); }
            catch { instructionList = PatchProcessor.GetOriginalInstructions(searchMethod); }
            bool foundResult = false;

            for (int i = 0; i < instructionList.Count; i++)
            {
                CodeInstruction instruction = instructionList[i];
                if ((instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) &&
                    instruction.operand is MethodInfo calledMethod && targets.Exists(t => IsBaseMethod(t, calledMethod)))
                {
                    foundResult = true;
                    Results.Add((ItemPos <MethodInfo>)(i, calledMethod));
                    if (!FindAll)
                    {
                        return(true);
                    }
                }
            }
            return(foundResult);
        }
        public virtual bool SearchMethod(MethodInfo searchMethod, out List <ItemPos <JobDef> > Results)
        {
            Results = new List <ItemPos <JobDef> >();
            bool foundResult = false;
            List <CodeInstruction> instructionList;

            try { instructionList = PatchProcessor.GetCurrentInstructions(searchMethod); }
            catch { instructionList = PatchProcessor.GetOriginalInstructions(searchMethod); }
            for (int i = 0; i < instructionList.Count; i++)
            {
                CodeInstruction instruction = instructionList[i];
                // Ambiguous method call
                if ((instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) &&
                    instruction.operand is MethodInfo calledMethod)
                {
                    if (calledMethod.ReturnType == typeof(JobDef))
                    {
                        foundResult = true;
                        Results.Add((ItemPos <JobDef>)(i, null));
                    }
                }
                // StatDefOf call
                if (instruction.opcode == OpCodes.Ldsfld && instruction.operand is FieldInfo jobField &&
                    jobField.FieldType == typeof(JobDef) && JobDefOfFieldInfo.Contains(jobField))
                {
                    Results.Add((ItemPos <JobDef>)(i, FieldToJob[jobField]));
                    foundResult = true;
                }
                if (!FindAll && foundResult)
                {
                    return(foundResult);
                }
            }
            return(foundResult);
        }
        private static IEnumerable <CodeInstruction> Transpiler(MethodBase __originalMethod,
                                                                IEnumerable <CodeInstruction> instructions)
        {
            var inst        = PatchProcessor.GetOriginalInstructions(__originalMethod);
            var modInstList = instructions.ToList();

            var insts = new Myers <CodeInstruction>(inst.ToArray(), modInstList.ToArray(), methComparer);

            insts.Compute();

            var key   = Utility.GetMethodKey(__originalMethod as MethodInfo);
            var index = MethodInfoCache.AddMethod(key, __originalMethod as MethodInfo);

            foreach (var thing in insts.changeSet)
            {
                // We only want added methods
                if (thing.change != ChangeType.Added)
                {
                    continue;
                }
                if (!InternalMethodUtility.IsFunctionCall(thing.value.opcode) ||
                    !(thing.value.operand is MethodInfo meth))
                {
                    continue;
                }

                // swap our instruction
                var replaceInstruction = MethodTransplanting.ReplaceMethodInstruction(
                    thing.value,
                    key,
                    typeof(H_HarmonyTranspilersInternalMethods),
                    index);

                // Find the place it was in our method, and replace the instruction (Optimisation Opportunity to improve this)
                for (var i = 0; i < modInstList.Count; i++)
                {
                    var instruction = modInstList[i];
                    if (!InternalMethodUtility.IsFunctionCall(instruction.opcode))
                    {
                        continue;
                    }
                    if (!(instruction.operand is MethodInfo info) || info.Name != meth.Name)
                    {
                        continue;
                    }


                    if (instruction != replaceInstruction)
                    {
                        modInstList[i] = replaceInstruction;
                    }
                    break;
                }
            }

            return(modInstList);
        }
        /* The idea here is a little convoluted, but in short
         * 1. Grab the 'original' instructions of the method, and grab all the 'Call'/'CallVirt' instructions
         * 2. Grab all the 'Call'/'CallVirt' instructions in the modified version
         */

        public static IEnumerable <CodeInstruction> Transpiler(MethodBase __originalMethod, IEnumerable <CodeInstruction> instructions, ILGenerator ilGen)
        {
            var inst        = PatchProcessor.GetOriginalInstructions(__originalMethod);
            var modInstList = instructions.ToList();

            var origCalls = inst.Where(i => InternalMethodUtility.IsFunctionCall(i.opcode)).ToList();
            var modCalls  = modInstList.Where(i => InternalMethodUtility.IsFunctionCall(i.opcode)).ToList();

            return(instructions);
        }
        public void Test_Read_WeirdMethodWithGoto()
        {
            var method = SymbolExtensions.GetMethodInfo(() => WeirdMethodWithGoto());

            Assert.NotNull(method);
            var instructions = PatchProcessor.GetOriginalInstructions(method);

            Assert.NotNull(instructions);
            Assert.Greater(instructions.Count, 0);
        }
Exemple #6
0
        internal static MethodBase TargetMethod()
        {
            var shwGetter = ReflectionHelper.Settings.Type.GetProperty("ShowHaxchiWarning").GetGetMethod();

            return((from method in ReflectionHelper.NusGrabberForm.Type.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
                    where method.GetParameters().Length == 1 &&
                    method.GetParameters()[0].ParameterType.IsAbstract
                    let instructions = PatchProcessor.GetOriginalInstructions(method, out _)
                                       where instructions.Any(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand) == shwGetter)
                                       select method).FirstOrDefault());
        }
Exemple #7
0
        static string GetMethodHash(MethodBase method)
        {
            var toHash = new List <byte>();

            foreach (var ins in PatchProcessor.GetOriginalInstructions(method))
            {
                toHash.Add(Encoding.UTF8.GetBytes(ins.opcode.Name + ins.operand));
            }

            using (var sha256 = System.Security.Cryptography.SHA256.Create()) {
                return(Convert.ToBase64String(sha256.ComputeHash(toHash.ToArray())));
            }
        }
Exemple #8
0
        public static CodeInstructionDifference CodeInstructionDifferencesBetweenOriginalAndTranspiledMethod(
            IPatch patch,
            MethodInfo patchedMethod,
            MethodInfo transpilerMethod)
        {
            List <CodeInstruction> originalInstructions = PatchProcessor.GetOriginalInstructions(patchedMethod);
            List <CodeInstruction> codeInstructionList;

            patch.Apply();
            codeInstructionList = (List <CodeInstruction>)transpilerMethod.Invoke(null, new object[] { originalInstructions });

            return(new CodeInstructionDifference(originalInstructions, codeInstructionList));
        }
Exemple #9
0
        private static FieldInfo GetFieldInfo(MethodInfo getterMethod)
        {
            List <CodeInstruction> instructionList;

            try { instructionList = PatchProcessor.GetCurrentInstructions(getterMethod); }
            catch { instructionList = PatchProcessor.GetOriginalInstructions(getterMethod); }
            var length = instructionList.Count;

            for (int i = 0; i < length; i++)
            {
                CodeInstruction instruction = instructionList[length - 1 - i];
                if (instruction.opcode == OpCodes.Ldfld && instructionList[length - 2 - i].IsLdarg(0))
                {
                    return(instruction.operand as FieldInfo);
                }
            }
            return(null);
        }
Exemple #10
0
        // Based on https://github.com/dotnet/roslyn/blob/main/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameKind.cs
        // and https://github.com/dotnet/roslyn/blob/main/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs
        public static int GetMethodDebugId(this MethodBase method)
        {
            string cur = null;

            try
            {
                // Try extract the debug id from the method body
                foreach (var inst in PatchProcessor.GetOriginalInstructions(method))
                {
                    // Example class names: <>c__DisplayClass10_0 or <CompGetGizmosExtra>d__7
                    if (inst.opcode == OpCodes.Newobj &&
                        inst.operand is MethodBase m &&
                        (cur = m.DeclaringType.Name) != null)
                    {
                        if (cur.StartsWith(DisplayClassPrefix))
                        {
                            return(int.Parse(cur.Substring(DisplayClassPrefix.Length).Until('_')));
                        }
                        else if (cur.Contains(EnumerableStateMachineInfix))
                        {
                            return(int.Parse(cur.After('>').Substring(EnumerableStateMachineInfix.Length)));
                        }
                    }
                    // Example method names: <FillTab>b__10_0 or <DoWindowContents>g__Start|55_1
                    else if (
                        (inst.opcode == OpCodes.Ldftn || inst.opcode == OpCodes.Call) &&
                        inst.operand is MethodBase f &&
                        (cur = f.Name) != null &&
                        cur.StartsWith("<") &&
                        cur.After('>').CharacterCount('_') == 3)
                    {
                        if (cur.Contains(LambdaMethodInfix))
                        {
                            return(int.Parse(cur.After('>').Substring(LambdaMethodInfix.Length).Until('_')));
                        }
                        else if (cur.Contains(LocalFunctionInfix))
                        {
                            return(int.Parse(cur.After('|').Until('_')));
                        }
                    }
                }
Exemple #11
0
        private static IEnumerable <CodeInstruction> Transpiler(MethodBase __originalMethod, IEnumerable <CodeInstruction> instructions)
        {
            var inst        = PatchProcessor.GetOriginalInstructions(__originalMethod);
            var modInstList = instructions.ToList();

            var insts = new Myers <CodeInstruction>(inst.ToArray(), modInstList.ToArray(), methComparer);

            insts.Compute();

            var key   = Utility.GetMethodKey(__originalMethod);
            var index = MethodInfoCache.AddMethod(key, __originalMethod);

            foreach (var thing in insts.changeSet)
            {
                // We only want added methods
                if (thing.change != ChangeType.Added)
                {
                    continue;
                }

                if (!Utility.ValidCallInstruction(thing.value, null, out var meth, out _))
                {
                    continue;
                }
                if (!(meth is MethodInfo))
                {
                    continue;
                }

                // swap our instruction
                var replaceInstruction = MethodTransplanting.ReplaceMethodInstruction(
                    thing.value,
                    key,
                    typeof(H_HarmonyTranspilersInternalMethods),
                    index);

                modInstList[thing.rIndex] = replaceInstruction;
            }

            return(modInstList);
        }
Exemple #12
0
        private bool SearchToilGenerator(MethodInfo searchMethod, List <MethodInfo> ActionList, out List <MethodInfo> ActionsFound)
        {
            ActionsFound = new List <MethodInfo>();
            bool foundResult = false;
            List <CodeInstruction> instructionList;

            try { instructionList = PatchProcessor.GetCurrentInstructions(searchMethod); }
            catch { instructionList = PatchProcessor.GetOriginalInstructions(searchMethod); }
            foreach (var instruction in instructionList)
            {
                if ((instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt || instruction.opcode == OpCodes.Ldftn) &&
                    instruction.operand is MethodInfo calledMethod && ActionList.Any(t => IsBaseMethod(t, calledMethod)))
                {
                    foundResult = true;
                    ActionsFound.Add(calledMethod);
                    if (!FindAll)
                    {
                        return(true);
                    }
                }
            }
            return(foundResult);
        }
Exemple #13
0
        public virtual bool SearchMethod(MethodInfo searchMethod, out List <ItemPos <StatDef> > Results, out List <CodeInstruction> PawnIns)
        {
            Results = new List <ItemPos <StatDef> >();
            PawnIns = new List <CodeInstruction>();
            bool foundResult = false;
            bool foundPawn   = false;
            List <CodeInstruction> instructionList;

            try { instructionList = PatchProcessor.GetCurrentInstructions(searchMethod); }
            catch { instructionList = PatchProcessor.GetOriginalInstructions(searchMethod); }
            List <int?>         ParamList = searchMethod.GetParameters()?.Where(t => t.ParameterType.IsAssignableFrom(typeof(Pawn)))?.Select(t => new int?(t.Position))?.ToList() ?? new List <int?>();
            List <LocalBuilder> LocalList = new List <LocalBuilder>();

            if (!ParamList.NullOrEmpty())
            {
                foundPawn = true;
            }
            for (int i = 0; i < instructionList.Count; i++)
            {
                CodeInstruction instruction = instructionList[i];
                if (instruction.opcode == OpCodes.Ldfld && instruction.operand is FieldInfo pawnField &&
                    pawnField.FieldType.IsAssignableFrom(typeof(Pawn)))
                {
                    foundPawn = true;
                    for (int j = 1; j < i; j++)
                    {
                        if (instructionList[i - j].IsLdarg(0))
                        {
                            PawnIns = new List <CodeInstruction>(instructionList.GetRange(i - j, j + 1));
                            break;
                        }
                    }
                }
                if (ParamList.Any(t => instruction.IsLdarg(t)))
                {
                    PawnIns = new List <CodeInstruction>()
                    {
                        instruction
                    }
                }
                ;
                if (instruction.IsStloc() && instruction.operand is LocalBuilder local && !LocalList.Contains(local) && local.LocalType.IsAssignableFrom(typeof(Pawn)))
                {
                    LocalList.Add(local);
                }
                if (LocalList.Any(t => instruction.IsLdloc(t)))
                {
                    PawnIns = new List <CodeInstruction>()
                    {
                        instruction
                    }
                }
                ;
                // Ambiguous method call
                if ((instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) &&
                    instruction.operand is MethodInfo calledMethod)
                {
                    if (calledMethod.ReturnType == typeof(StatDef))
                    {
                        foundResult = true;
                        Results.Add((ItemPos <StatDef>)(i, null));
                    }
                    else if (calledMethod.ReturnType.IsAssignableFrom(typeof(Pawn)))
                    {
                        foundPawn = true;
                        if (calledMethod.IsStatic)
                        {
                            PawnIns = new List <CodeInstruction>()
                            {
                                instruction
                            }
                        }
                        ;
                        else
                        {
                            for (int j = 1; j < i; j++)
                            {
                                if (instructionList[i - j].IsLdarg(0))
                                {
                                    PawnIns = new List <CodeInstruction>(instructionList.GetRange(i - j, j + 1));
                                    break;
                                }
                            }
                        }
                    }
                }
                // StatDefOf call
                if (instruction.opcode == OpCodes.Ldsfld && instruction.operand is FieldInfo statField &&
                    statField.FieldType == typeof(StatDef) && StatDefOfFieldInfo.Contains(statField))
                {
                    Results.Add((ItemPos <StatDef>)(i, FieldToStat[statField]));
                    foundResult = true;
                }
                if (!FindAll && foundResult && (!findPawn || foundPawn))
                {
                    return(foundResult);
                }
            }
            return(foundResult);
        }
    }
}