public void DumpMetadata(string path) { using (DebugILWriter writer = new DebugILWriter(path, "__AssemblyInfo__")) { writer.WriteLine("// MonoMod DebugILGenerator"); writer.WriteLine($"// MonoMod Version: {MonoModder.Version}"); writer.WriteLine(); writer.WriteLine("// Input assembly:"); writer.WriteLine($"// {Modder.Module.Assembly.Name.FullName}"); writer.WriteLine($"// {Modder.InputPath}"); writer.WriteLine(); writer.WriteLine("// Assembly references:"); foreach (AssemblyNameReference dep in Modder.Module.AssemblyReferences) { writer.WriteLine($"// {dep.FullName}"); } writer.WriteLine(); // TODO: [DbgILGen] Other assembly metadata? } }
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(); } }
public void DumpType(string path, TypeDefinition type) { using (DebugILWriter writer = new DebugILWriter(path, "__TypeInfo__")) { writer.WriteLine("// MonoMod DebugILGenerator"); writer.WriteLine($"// Type: ({type.Attributes.ToString()}) {type.FullName}"); writer.WriteLine(); writer.WriteLine("// Fields:"); foreach (FieldDefinition member in type.Fields) { writer.WriteLine($"// ({member.Attributes}) {member.FieldType.FullName} {member.Name}"); } writer.WriteLine(); writer.WriteLine("// Properties:"); foreach (PropertyDefinition member in type.Properties) { writer.WriteLine($"// ({member.Attributes}) {member.PropertyType.FullName} {member.Name}"); } writer.WriteLine(); writer.WriteLine("// Events:"); foreach (EventDefinition member in type.Events) { writer.WriteLine($"// ({member.Attributes}) {member.EventType.FullName} {member.Name}"); } writer.WriteLine(); // TODO: [DbgILGen] Other type metadata? } }