示例#1
0
        public void Test_CanGetInstructionsWithNoILGenerator()
        {
            var method       = typeof(Class12).GetMethod(nameof(Class12.FizzBuzz));
            var newGenerator = PatchProcessor.CreateILGenerator(method);

            var instrsNoGen  = MethodBodyReader.GetInstructions(generator: null, method);
            var instrsHasGen = MethodBodyReader.GetInstructions(newGenerator, method);

            Assert.AreEqual(instrsNoGen.Count, instrsHasGen.Count);
            for (var i = 0; i < instrsNoGen.Count; i++)
            {
                var instrNoGen  = instrsNoGen[i];
                var instrHasGen = instrsHasGen[i];
                Assert.AreEqual(instrNoGen.offset, instrHasGen.offset, "offset @ {0} ({1})", i, instrNoGen);
                Assert.AreEqual(instrNoGen.opcode, instrHasGen.opcode, "opcode @ {0} ({1})", i, instrNoGen);
                AssertAreEqual(instrNoGen.operand, instrHasGen.operand, "operand", i, instrNoGen);
                CollectionAssert.AreEqual(instrNoGen.labels, instrHasGen.labels, "labels @ {0}", i);
                CollectionAssert.AreEqual(instrNoGen.blocks, instrHasGen.blocks, "blocks @ {0}", i);
                AssertAreEqual(instrNoGen.argument, instrHasGen.argument, "argument", i, instrNoGen);

                // The only difference between w/o gen and w/ gen is this:
                var operandType = instrNoGen.opcode.OperandType;
                if ((operandType == OperandType.ShortInlineVar || operandType == OperandType.InlineVar) && instrNoGen.argument is object)
                {
#if NETCOREAPP3_0 || NETCOREAPP3_1 || NET5_0
                    Assert.AreEqual("System.Reflection.RuntimeLocalVariableInfo", instrNoGen.argument.GetType().FullName, "w/o generator argument type @ {0} ({1})", i, instrNoGen);
#else
                    Assert.AreEqual("System.Reflection.LocalVariableInfo", instrNoGen.argument.GetType().FullName, "w/o generator argument type @ {0} ({1})", i, instrNoGen);
#endif
                    Assert.AreEqual(typeof(LocalBuilder), instrHasGen.argument.GetType(), "w/ generator argument type @ {0} ({1})", i, instrNoGen);
                }
            }
        }
示例#2
0
        /// <summary>
        /// Compiles a method into IL instructions exactly the same as the Harmony transpiler.
        /// </summary>
        /// <param name="method">Method to compile.</param>
        /// <returns>
        /// List of ILInstructions.
        /// </returns>
        /// <remarks>
        /// Utilizes harmony library.
        /// </remarks>
        public static List <CodeInstruction> MethodToILInstructions(MethodBase method)
        {
            // Get il generator
            ILGenerator il_gen = PatchProcessor.CreateILGenerator(method);

            List <Label>      labels      = new List <Label>();
            List <MethodInfo> transpilers = new List <MethodInfo>();
            bool hasReturn = false;

            // internal Emitter(ILGenerator il, bool debug)
            object emitter = Activator.CreateInstance(
                t_Emitter,
                BindingFlags.NonPublic | BindingFlags.Instance,
                null,
                new object[] { il_gen, true },
                null);

            // internal MethodBodyReader(MethodBase method, ILGenerator generator)
            object methodBodyReader = Activator.CreateInstance(
                t_MethodBodyReader,
                BindingFlags.NonPublic | BindingFlags.Instance,
                null,
                new object[] { method, il_gen },
                null);

            // internal void DeclareVariables(LocalBuilder[] existingVariables)
            t_MethodBodyReader.GetMethod("DeclareVariables", BindingFlags.NonPublic | BindingFlags.Instance)
            .Invoke(methodBodyReader, new object[] { null });

            // internal void ReadInstructions()
            t_MethodBodyReader.GetMethod("ReadInstructions", BindingFlags.NonPublic | BindingFlags.Instance)
            .Invoke(methodBodyReader, new object[0]);

            // internal List<CodeInstruction> FinalizeILCodes(Emitter emitter, List<MethodInfo> transpilers, List<Label> endLabels, out bool hasReturnCode)
            List <CodeInstruction> generated_instructions = (List <CodeInstruction>)t_MethodBodyReader
                                                            .GetMethod("FinalizeILCodes", BindingFlags.NonPublic | BindingFlags.Instance)
                                                            .Invoke(methodBodyReader, new object[] { emitter, transpilers, labels, hasReturn });

            // Add final return statement as harmony adds it manually after???
            generated_instructions.Add(new CodeInstruction(OpCodes.Ret, null));

            return(generated_instructions);
        }