示例#1
0
        public void GenerateFor(MethodDefinition method)
        {
            string nameEscaped = PathVerifyRegex.Replace(method.Name, "");

            CurrentPath.Push(nameEscaped + ".il");
            int pathCollision = 0;

            while (File.Exists(FullPath))
            {
                pathCollision++;
                CurrentPath.Pop();
                CurrentPath.Push(nameEscaped + "." + pathCollision + ".il");
            }

            method.NoInlining     = true;
            method.NoOptimization = true;

            using (StreamWriter writer = new StreamWriter(FullPath)) {
                Line = 1;
                writer.WriteLine("// MonoMod DebugILGenerator"); Line++;
                writer.Write("// Method: (");
                writer.Write(method.Attributes);
                writer.Write(") ");
                writer.WriteLine(method.GetFindableID()); Line++;
                writer.WriteLine(); Line++;

                // TODO: [DbgILGen] Other method metadata?

                writer.WriteLine("// Body:"); Line++;
                if (!method.HasBody)
                {
                    writer.WriteLine("// No body found."); Line++;
                }
                else
                {
                    // TODO: [DbgILGen] Method body metadata?

                    writer.Write(".maxstack ");
                    writer.WriteLine(method.Body.MaxStackSize); Line++;

                    // Always assure a debug scope exists!
                    method.DebugInformation.GetOrAddScope().Variables.Clear();
                    if (method.Body.HasVariables)
                    {
                        if (method.Body.InitLocals)
                        {
                            writer.WriteLine(".locals init (");
                        }
                        else
                        {
                            writer.WriteLine(".locals (");
                        }
                        Line++;
                        for (int i = 0; i < method.Body.Variables.Count; i++)
                        {
                            VariableDefinition @var = method.Body.Variables[i];
                            writer.Write("    [");
                            writer.Write(i);
                            writer.Write("] ");
                            if ([email protected] && [email protected])
                            {
                                writer.Write("class ");
                            }
                            writer.Write(@var.VariableType.FullName);
                            string name = @var.GenerateVariableName(method, i);
                            method.DebugInformation.GetOrAddScope().Variables.Add(new VariableDebugInformation(@var, name));
                            writer.Write(" ");
                            writer.Write(name);
                            if (i < method.Body.Variables.Count - 1)
                            {
                                writer.WriteLine(",");
                            }
                            else
                            {
                                writer.WriteLine();
                            }
                            Line++;
                        }
                        writer.WriteLine(")"); Line++;
                    }

                    writer.WriteLine("// Code:"); Line++;
                    method.DebugInformation.SequencePoints.Clear();
                    Document symbolDoc = new Document(FullPath)
                    {
                        LanguageVendor = DocumentLanguageVendor.Microsoft,
                        Language       = DocumentLanguage.CSharp, // Even Visual Studio can't deal with Cil!
                        HashAlgorithm  = DocumentHashAlgorithm.None,
                        Type           = DocumentType.Text
                    };

                    ILProcessor il = method.Body.GetILProcessor();
                    for (int instri = 0; instri < method.Body.Instructions.Count; instri++)
                    {
                        Instruction instr    = method.Body.Instructions[instri];
                        string      instrStr = Relative ? instr.ToRelativeString() : instr.ToString();

                        method.DebugInformation.SequencePoints.Add(
                            new SequencePoint(instr, symbolDoc)
                        {
                            StartLine   = Line,
                            StartColumn = 1,
                            EndLine     = Line,
                            EndColumn   = instrStr.Length + 1
                        }
                            );

                        writer.WriteLine(instrStr); Line++;
                    }
                }

                writer.WriteLine();
            }

            CurrentPath.Pop();
        }
示例#2
0
        public void DumpMethod(string parent, int index, MethodDefinition method)
        {
            method.NoInlining     = true;
            method.NoOptimization = true;

            using (DebugILWriter writer = new DebugILWriter(parent, method.Name, index)) {
                writer.WriteLine("// MonoMod DebugILGenerator");
                writer.WriteLine($"// Method: ({method.Attributes}) {method.GetID()}");
                writer.WriteLine();

                // TODO: [DbgILGen] Other method metadata?

                writer.WriteLine("// Body:");
                if (!method.HasBody)
                {
                    writer.WriteLine("// No body found.");
                    writer.WriteLine();
                    return;
                }

                // TODO: [DbgILGen] Method body metadata?

                if (!SkipMaxStack)
                {
                    writer.WriteLine($".maxstack {method.Body.MaxStackSize}");
                }

                // Always assure a debug scope exists!
                method.DebugInformation.GetOrAddScope().Variables.Clear();
                if (method.Body.HasVariables)
                {
                    writer.WriteLine(method.Body.InitLocals ? ".locals init (" : ".locals (");

                    for (int i = 0; i < method.Body.Variables.Count; i++)
                    {
                        VariableDefinition vd   = method.Body.Variables[i];
                        string             name = vd.GenerateVariableName();
                        method.DebugInformation.GetOrAddScope().Variables.Add(new VariableDebugInformation(vd, name));
                        writer.WriteLine($"    [{i}] {(!vd.VariableType.IsPrimitive && !vd.VariableType.IsValueType ? "class " : "")}{vd.VariableType.FullName} {name}{(i < method.Body.Variables.Count - 1 ? "," : "")}");
                    }
                    writer.WriteLine(")");
                }

                writer.WriteLine("// Code:");
                method.DebugInformation.SequencePoints.Clear();
                Document symbolDoc = new Document(writer.FullPath)
                {
                    LanguageVendor = DocumentLanguageVendor.Microsoft,
                    Language       = DocumentLanguage.CSharp, // Even Visual Studio can't deal with Cil!
                    HashAlgorithm  = DocumentHashAlgorithm.None,
                    Type           = DocumentType.Text
                };

                // The exception block pretty printing is based off of
                // https://github.com/BepInEx/HarmonyX/blob/a570001c568629d745c88fbc46e70cc7d0c9becf/Harmony/Internal/Util/MethodBodyLogExtensions.cs#L44
                // Thanks to ghorsington (denikson) for allowing MonoMod to use it!

                // Cache exception blocks for pretty printing
                Dictionary <Instruction, List <ExceptionBlock> > handlerMap = new Dictionary <Instruction, List <ExceptionBlock> >();

                ExceptionBlock AddBlock(Instruction instr, ExceptionBlockType t)
                {
                    if (instr == null)
                    {
                        return(new ExceptionBlock());
                    }

                    if (!handlerMap.TryGetValue(instr, out List <ExceptionBlock> list))
                    {
                        handlerMap[instr] = list = new List <ExceptionBlock>();
                    }

                    ExceptionBlock block = new ExceptionBlock()
                    {
                        BlockType = t
                    };

                    list.Add(block);
                    return(block);
                }

                foreach (ExceptionHandler handler in method.Body.ExceptionHandlers)
                {
                    AddBlock(handler.TryStart, ExceptionBlockType.BeginExceptionBlock);
                    AddBlock(handler.TryEnd, ExceptionBlockType.EndExceptionBlock);
                    AddBlock(handler.HandlerEnd, ExceptionBlockType.EndExceptionBlock);
                    switch (handler.HandlerType)
                    {
                    case ExceptionHandlerType.Catch:
                        AddBlock(handler.HandlerStart, ExceptionBlockType.BeginCatchBlock).CatchType =
                            handler.CatchType ?? Modder.Module.TypeSystem.Object;
                        break;

                    case ExceptionHandlerType.Filter:
                        AddBlock(handler.FilterStart, ExceptionBlockType.BeginExceptFilterBlock);
                        break;

                    case ExceptionHandlerType.Finally:
                        AddBlock(handler.HandlerStart, ExceptionBlockType.BeginFinallyBlock);
                        break;

                    case ExceptionHandlerType.Fault:
                        AddBlock(handler.HandlerStart, ExceptionBlockType.BeginFaultBlock);
                        break;

                    default:
                        throw new NotSupportedException($"Unsupported exception handler type ${handler.HandlerType}");
                    }
                }

                var handlerStack = new Stack <string>();

                for (int instri = 0; instri < method.Body.Instructions.Count; instri++)
                {
                    Instruction instr = method.Body.Instructions[instri];

                    if (handlerMap.TryGetValue(instr, out List <ExceptionBlock> blocks))
                    {
                        // Force exception close to the start for correct output
                        blocks.Sort((a, b) => a.BlockType == ExceptionBlockType.EndExceptionBlock ? -1 : 0);

                        foreach (ExceptionBlock block in blocks)
                        {
                            switch (block.BlockType)
                            {
                            case ExceptionBlockType.BeginExceptionBlock:
                                writer.WriteLine(".try {");
                                handlerStack.Push(".try");
                                break;

                            case ExceptionBlockType.BeginCatchBlock:
                                writer.WriteLine($"catch {block.CatchType.FullName} {{");
                                handlerStack.Push("handler (catch)");
                                break;

                            case ExceptionBlockType.BeginExceptFilterBlock:
                                writer.WriteLine("filter {");
                                handlerStack.Push("handler (filter)");
                                break;

                            case ExceptionBlockType.BeginFaultBlock:
                                writer.WriteLine("fault {");
                                handlerStack.Push("handler (fault)");
                                break;

                            case ExceptionBlockType.BeginFinallyBlock:
                                writer.WriteLine("finally {");
                                handlerStack.Push("handler (finally)");
                                break;

                            case ExceptionBlockType.EndExceptionBlock:
                                writer.WriteLine($"}} // end {handlerStack.Pop()}");
                                break;

                            default:
                                throw new NotSupportedException($"Unsupported exception handler type ${block.BlockType}");
                            }
                        }
                    }

                    string instrStr = Relative ? instr.ToRelativeString() : instr.ToString();

                    method.DebugInformation.SequencePoints.Add(
                        new SequencePoint(instr, symbolDoc)
                    {
                        StartLine   = writer.Line,
                        StartColumn = 1,
                        EndLine     = writer.Line,
                        EndColumn   = instrStr.Length + 1
                    }
                        );

                    writer.WriteLine(instrStr);
                }

                writer.WriteLine();
            }
        }