Пример #1
0
        private static IEnumerable <CodeInstruction> Transpiler(MethodBase __originalMethod, IEnumerable <CodeInstruction> codeInstructions)
        {
            var enumerable = codeInstructions.ToList();

            try
            {
                List <CodeInstruction> instructions = enumerable;

                for (int i = 0; i < instructions.Count; i++)
                {
                    if (!IsFunctionCall(instructions[i].opcode))
                    {
                        continue;
                    }
                    if (!(instructions[i].operand is MethodInfo meth))
                    {
                        continue;
                    }

                    // Check for constrained
                    if (i != 0 && instructions[i - 1].opcode == OpCodes.Constrained)
                    {
                        continue;
                    }
                    // Make sure it is not an analyzer profiling method
                    if (meth.DeclaringType.FullName.Contains("Analyzer.Profiling"))
                    {
                        continue;
                    }

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

                    var inst = MethodTransplanting.ReplaceMethodInstruction(
                        instructions[i],
                        key,
                        GUIController.types[__originalMethod.DeclaringType + ":" + __originalMethod.Name + "-int"],
                        index);

                    instructions[i] = inst;
                }

                return(instructions);
            }
            catch (Exception e)
            {
#if DEBUG
                ThreadSafeLogger.Error($"Failed to patch the internal method {__originalMethod.DeclaringType.FullName}:{__originalMethod.Name}, failed with the error " + e.Message);
#else
                if (Settings.verboseLogging)
                {
                    ThreadSafeLogger.Warning($"Failed to patch the internal method {__originalMethod.DeclaringType.FullName}:{__originalMethod.Name}, failed with the error " + e.Message);
                }
#endif

                return(enumerable);
            }
        }
        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);
        }
Пример #3
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);
        }
        private static IEnumerable <CodeInstruction> Transpiler(MethodBase __originalMethod, IEnumerable <CodeInstruction> codeInstructions)
        {
            try
            {
                var instructions = codeInstructions.ToList();

                for (int i = 0; i < instructions.Count; i++)
                {
                    if (!Utility.ValidCallInstruction(instructions[i], i == 0 ? null : instructions[i - 1], out var meth, out var key))
                    {
                        continue;
                    }

                    var index = MethodInfoCache.AddMethod(key, meth);

                    var inst = MethodTransplanting.ReplaceMethodInstruction(
                        instructions[i],
                        key,
                        GUIController.types[__originalMethod.DeclaringType + ":" + __originalMethod.Name + "-int"],
                        index);

                    instructions[i] = inst;
                }

                return(instructions);
            }
            catch (Exception e)
            {
#if DEBUG
                ThreadSafeLogger.ReportException(e, $"Failed to patch the methods inside {Utility.GetSignature(__originalMethod, false)}");
#else
                if (Settings.verboseLogging)
                {
                    ThreadSafeLogger.ReportException(e, $"Failed to patch the methods inside {Utility.GetSignature(__originalMethod, false)}");
                }
#endif

                return(codeInstructions);
            }
        }
Пример #5
0
        // This transpiler basically replicates ProfileController.Start, but in IL, and inside the method it is patching, to reduce as much overhead as
        // possible, its quite simple, just long and hard to read.
        public static IEnumerable <CodeInstruction> Transpiler(MethodBase __originalMethod, IEnumerable <CodeInstruction> instructions, ILGenerator ilGen)
        {
            var profLocal   = ilGen.DeclareLocal(typeof(Profiler));
            var keyLocal    = ilGen.DeclareLocal(typeof(string));
            var beginLabel  = ilGen.DefineLabel();
            var noProfLabel = ilGen.DefineLabel();

            var curType      = typeInfo[__originalMethod];
            var curLabelMeth = curType.GetMethod("GetLabel", BindingFlags.Public | BindingFlags.Static);
            var curNamerMeth = curType.GetMethod("GetName", BindingFlags.Public | BindingFlags.Static);
            var curTypeMeth  = curType.GetMethod("GetType", BindingFlags.Public | BindingFlags.Static);


            var key       = Utility.GetMethodKey(__originalMethod as MethodInfo); // This translates our method into a human-legible key, I.e. Namespace.Type<Generic>:Method
            var methodKey = MethodInfoCache.AddMethod(key, __originalMethod as MethodInfo);


            // Active Check
            {
                // if(active && (Analyzer.CurrentlyProfiling))
                yield return(new CodeInstruction(OpCodes.Ldsfld, curType.GetField("Active", BindingFlags.Public | BindingFlags.Static)));

                yield return(new CodeInstruction(OpCodes.Ldsfld, Analyzer_CurrentlyProfiling)); // profiling

                yield return(new CodeInstruction(OpCodes.Ldsfld, Analyzer_CurrentyPaused));     // !paused

                yield return(new CodeInstruction(OpCodes.Not));

                yield return(new CodeInstruction(OpCodes.And));

                yield return(new CodeInstruction(OpCodes.And));

                yield return(new CodeInstruction(OpCodes.Brfalse_S, beginLabel));
            }


            { // Custom Namer
                if (curNamerMeth != null)
                {
                    foreach (var codeInst in GetLoadArgsForMethodParams(__originalMethod as MethodInfo, curNamerMeth.GetParameters()))
                    {
                        yield return(codeInst);
                    }

                    yield return(new CodeInstruction(OpCodes.Call, curNamerMeth));
                }
                else
                {
                    yield return(new CodeInstruction(OpCodes.Ldstr, key));
                }
                yield return(new CodeInstruction(OpCodes.Stloc, keyLocal));
            }

            { // if(Profilers.TryGetValue(key, out var prof))
                yield return(new CodeInstruction(OpCodes.Ldsfld, ProfilerController_Profiles));

                yield return(new CodeInstruction(OpCodes.Ldloc, keyLocal));

                yield return(new CodeInstruction(OpCodes.Ldloca_S, profLocal));

                yield return(new CodeInstruction(OpCodes.Callvirt, Dict_TryGetValue));

                yield return(new CodeInstruction(OpCodes.Brfalse_S, noProfLabel));
            }

            { // If we found a profiler - Start it, and skip to the start of execution of the method
                yield return(new CodeInstruction(OpCodes.Ldloc, profLocal));

                yield return(new CodeInstruction(OpCodes.Call, Profiler_Start));

                yield return(new CodeInstruction(OpCodes.Pop)); // Profiler.Start returns itself so we pop it off the stack

                yield return(new CodeInstruction(OpCodes.Br, beginLabel));
            }

            { // if not, we need to make one
                yield return(new CodeInstruction(OpCodes.Ldloc, keyLocal).WithLabels(noProfLabel));

                { // Custom Labelling
                    if (curLabelMeth != null)
                    {
                        foreach (var codeInst in GetLoadArgsForMethodParams(__originalMethod as MethodInfo, curLabelMeth.GetParameters()))
                        {
                            yield return(codeInst);
                        }

                        yield return(new CodeInstruction(OpCodes.Call, curLabelMeth));
                    }
                    else
                    {
                        yield return(new CodeInstruction(OpCodes.Dup)); // duplicate the key on the stack so the key is both the key and the label in ProfileController.Start
                    }
                }
                { // Custom Typing
                    if (curTypeMeth != null)
                    {
                        foreach (var codeInst in GetLoadArgsForMethodParams(__originalMethod as MethodInfo, curTypeMeth.GetParameters()))
                        {
                            yield return(codeInst);
                        }

                        yield return(new CodeInstruction(OpCodes.Call, curTypeMeth));
                    }
                    else
                    {
                        yield return(new CodeInstruction(OpCodes.Ldnull)); // duplicate the key on the stack so the key is both the key and the label in ProfileController.Start
                    }
                }

                yield return(new CodeInstruction(OpCodes.Ldnull));

                yield return(new CodeInstruction(OpCodes.Ldnull));

                { // get our methodinfo from the metadata
                    foreach (var inst in MethodInfoCache.GetInlineIL(methodKey))
                    {
                        yield return(inst);
                    }
                }

                yield return(new CodeInstruction(OpCodes.Newobj, ProfilerCtor)); // new Profiler();

                yield return(new CodeInstruction(OpCodes.Dup));

                yield return(new CodeInstruction(OpCodes.Stloc, profLocal));
            }

            yield return(new CodeInstruction(OpCodes.Call, Profiler_Start));

            yield return(new CodeInstruction(OpCodes.Pop)); // profiler.Start returns itself

            {                                               // Add to the Profilers dictionary, so we cache creation.
                yield return(new CodeInstruction(OpCodes.Ldsfld, ProfilerController_Profiles));

                yield return(new CodeInstruction(OpCodes.Ldloc, keyLocal));

                yield return(new CodeInstruction(OpCodes.Ldloc, profLocal));

                yield return(new CodeInstruction(OpCodes.Callvirt, Dict_Add));
            }

            instructions.ElementAt(0).WithLabels(beginLabel);

            // For each instruction which exits this function, append our finishing touches (I.e.)
            // if(profiler != null)
            // {
            //      profiler.Stop();
            // }
            // return; // any labels here are moved to the start of the `if`
            foreach (var inst in instructions)
            {
                if (inst.opcode == OpCodes.Ret)
                {
                    Label endLabel = ilGen.DefineLabel();

                    // localProf?.Stop();
                    yield return(new CodeInstruction(OpCodes.Ldloc, profLocal).MoveLabelsFrom(inst));

                    yield return(new CodeInstruction(OpCodes.Brfalse_S, endLabel));

                    yield return(new CodeInstruction(OpCodes.Ldloc, profLocal));

                    yield return(new CodeInstruction(OpCodes.Call, Profiler_Stop));

                    yield return(inst.WithLabels(endLabel));
                }
                else
                {
                    yield return(inst);
                }
            }
        }