HashSet<ILVariable> localVariablesToDefine = new HashSet<ILVariable>(); // local variables that are missing a definition /// <summary> /// Creates the body for the method definition. /// </summary> /// <param name="methodDef">Method definition to decompile.</param> /// <param name="context">Decompilation context.</param> /// <param name="parameters">Parameter declarations of the method being decompiled. /// These are used to update the parameter names when the decompiler generates names for the parameters.</param> /// <returns>Block for the method body</returns> public static BlockStatement CreateMethodBody(MethodDef methodDef, DecompilerContext context, IEnumerable<ParameterDeclaration> parameters, out MemberMapping mm) { MethodDef oldCurrentMethod = context.CurrentMethod; Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef); context.CurrentMethod = methodDef; context.CurrentMethodIsAsync = false; try { AstMethodBodyBuilder builder = new AstMethodBodyBuilder(); builder.methodDef = methodDef; builder.context = context; builder.corLib = methodDef.Module.CorLibTypes; if (Debugger.IsAttached) { return builder.CreateMethodBody(parameters, out mm); } else { try { return builder.CreateMethodBody(parameters, out mm); } catch (OperationCanceledException) { throw; } catch (Exception ex) { throw new ICSharpCode.Decompiler.DecompilerException(methodDef, ex); } } } finally { context.CurrentMethod = oldCurrentMethod; } }
BlockStatement CreateMethodBody(IEnumerable<ParameterDeclaration> parameters, out MemberMapping mm) { if (methodDef.Body == null) { mm = null; return null; } context.CancellationToken.ThrowIfCancellationRequested(); ILBlock ilMethod = new ILBlock(); ILAstBuilder astBuilder = new ILAstBuilder(); ilMethod.Body = astBuilder.Build(methodDef, true, context); context.CancellationToken.ThrowIfCancellationRequested(); ILAstOptimizer bodyGraph = new ILAstOptimizer(); bodyGraph.Optimize(context, ilMethod); context.CancellationToken.ThrowIfCancellationRequested(); var localVariables = ilMethod.GetSelfAndChildrenRecursive<ILExpression>().Select(e => e.Operand as ILVariable) .Where(v => v != null && !v.IsParameter).Distinct(); Debug.Assert(context.CurrentMethod == methodDef); NameVariables.AssignNamesToVariables(context, astBuilder.Parameters, localVariables, ilMethod); if (parameters != null) { foreach (var pair in (from p in parameters join v in astBuilder.Parameters on p.Annotation<Parameter>() equals v.OriginalParameter select new { p, v.Name })) { pair.p.NameToken = Identifier.Create(pair.Name).WithAnnotation(TextTokenKind.Parameter); } } context.CancellationToken.ThrowIfCancellationRequested(); Ast.BlockStatement astBlock = TransformBlock(ilMethod); CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments Statement insertionPoint = astBlock.Statements.FirstOrDefault(); foreach (ILVariable v in localVariablesToDefine) { AstType type; if (v.Type.ContainsAnonymousType()) type = new SimpleType("var").WithAnnotation(TextTokenKind.Keyword); else type = AstBuilder.ConvertType(v.Type); var newVarDecl = new VariableDeclarationStatement(v.IsParameter ? TextTokenKind.Parameter : TextTokenKind.Local, type, v.Name); newVarDecl.Variables.Single().AddAnnotation(v); astBlock.Statements.InsertBefore(insertionPoint, newVarDecl); } mm = new MemberMapping(methodDef, localVariables.ToList()); return astBlock; }
public void StartNode(AstNode node) { // var ranges = node.Annotation<List<ILRange>>(); // if (ranges != null && ranges.Count > 0) // { // // find the ancestor that has method mapping as annotation // if (node.Ancestors != null && node.Ancestors.Count() > 0) // { // var n = node.Ancestors.FirstOrDefault(a => a.Annotation<MemberMapping>() != null); // if (n != null) { // MemberMapping mapping = n.Annotation<MemberMapping>(); // // // add all ranges // foreach (var range in ranges) { // mapping.MemberCodeMappings.Add(new SourceCodeMapping { // ILInstructionOffset = range, // SourceCodeLine = output.CurrentLine, // MemberMapping = mapping // }); // } // } // } // } nodeStack.Push(node); MemberMapping mapping = node.Annotation<MemberMapping>(); if (mapping != null) { parentMemberMappings.Push(currentMemberMapping); currentMemberMapping = mapping; } // For ctor/cctor field initializers var mms = node.Annotation<List<Tuple<MemberMapping, List<ILRange>>>>(); if (mms != null) { Debug.Assert(multiMappings == null); multiMappings = mms; } }
/// <summary> /// Gets a mapping given a type, a token and an IL offset. /// </summary> /// <param name="codeMappings">Code mappings storage.</param> /// <param name="ilOffset">IL offset.</param> /// <param name="isMatch">True, if perfect match.</param> /// <returns>A code mapping.</returns> public static SourceCodeMapping GetInstructionByOffset( this MemberMapping codeMapping, uint ilOffset, out bool isMatch) { isMatch = false; if (codeMapping == null) { throw new ArgumentNullException("CodeMappings storage must be valid!"); } // try find an exact match var map = codeMapping.MemberCodeMappings.Find(m => m.ILRange.From <= ilOffset && ilOffset < m.ILRange.To); isMatch = map != null; if (map == null) { // get the immediate next one map = codeMapping.MemberCodeMappings.Find(m => m.ILRange.From > ilOffset); } return(map); }
public SourceCodeMapping(ILRange ilRange, TextPosition start, TextPosition end, MemberMapping mapping) { this.ilRange = ilRange; this.startPos = start; this.endPos = end; this.memberMapping = mapping; }
void ITextOutput.AddDebugSymbols(MemberMapping methodDebugSymbols) { }
void ITextOutput.AddDebugSymbols(MemberMapping methodDebugSymbols) { }
public void AddDebugSymbols(MemberMapping methodDebugSymbols) { DebuggerMemberMappings.Add(methodDebugSymbols); }
public override void EndNode(AstNode node) { if (nodeStack.Pop() != node) throw new InvalidOperationException(); if (node.Annotation<MemberMapping>() != null) { output.AddDebugSymbols(currentMemberMapping); currentMemberMapping = parentMemberMappings.Pop(); } var mms = node.Annotation<List<Tuple<MemberMapping, List<ILRange>>>>(); if (mms != null) { Debug.Assert(mms == multiMappings); if (mms == multiMappings) { foreach (var mm in mms) output.AddDebugSymbols(mm.Item1); multiMappings = null; } } }
public override void StartNode(AstNode node) { if (nodeStack.Count == 0) { if (IsUsingDeclaration(node)) { firstUsingDeclaration = !IsUsingDeclaration(node.PrevSibling); lastUsingDeclaration = !IsUsingDeclaration(node.NextSibling); } else { firstUsingDeclaration = false; lastUsingDeclaration = false; } } nodeStack.Push(node); MemberMapping mapping = node.Annotation<MemberMapping>(); if (mapping != null) { parentMemberMappings.Push(currentMemberMapping); currentMemberMapping = mapping; } // For ctor/cctor field initializers var mms = node.Annotation<List<Tuple<MemberMapping, List<ILRange>>>>(); if (mms != null) { Debug.Assert(multiMappings == null); multiMappings = mms; } }
public SourceCodeMapping(ILRange ilRange, TextPosition start, TextPosition end, MemberMapping mapping) { this.ilRange = ilRange; this.startPos = start; this.endPos = end; this.memberMapping = mapping; }
public void Disassemble(MethodDef method, MemberMapping debugSymbols) { // start writing IL code CilBody body = method.Body; uint codeSize = (uint)body.GetCodeSize(); uint rva = (uint)method.RVA; long offset = method.Module.ToFileOffset(rva); if (options.ShowTokenAndRvaComments) { output.WriteLine(string.Format("// Header Size: {0} {1}", method.Body.HeaderSize, method.Body.HeaderSize == 1 ? "byte" : "bytes"), TextTokenKind.Comment); output.WriteLine(string.Format("// Code Size: {0} (0x{0:X}) {1}", codeSize, codeSize == 1 ? "byte" : "bytes"), TextTokenKind.Comment); if (body.LocalVarSigTok != 0) { output.Write("// LocalVarSig Token: ", TextTokenKind.Comment); output.WriteReference(string.Format("0x{0:X8}", body.LocalVarSigTok), new TokenReference(method.Module, body.LocalVarSigTok), TextTokenKind.Comment, false); output.Write(string.Format(" RID: {0}", body.LocalVarSigTok & 0xFFFFFF), TextTokenKind.Comment); output.WriteLine(); } } output.Write(".maxstack", TextTokenKind.ILDirective); output.WriteSpace(); output.WriteLine(string.Format("{0}", body.MaxStack), TextTokenKind.Number); if (method.DeclaringType.Module.EntryPoint == method) output.WriteLine (".entrypoint", TextTokenKind.ILDirective); if (method.Body.HasVariables) { output.Write(".locals", TextTokenKind.ILDirective); output.WriteSpace(); if (method.Body.InitLocals) { output.Write("init", TextTokenKind.Keyword); output.WriteSpace(); } output.WriteLine("(", TextTokenKind.Operator); output.Indent(); foreach (var v in method.Body.Variables) { output.Write("[", TextTokenKind.Operator); output.WriteDefinition(v.Index.ToString(), v, TextTokenKind.Number); output.Write("]", TextTokenKind.Operator); output.WriteSpace(); v.Type.WriteTo(output); if (!string.IsNullOrEmpty(v.Name)) { output.WriteSpace(); output.Write(DisassemblerHelpers.Escape(v.Name), TextTokenKind.Local); } if (v.Index + 1 < method.Body.Variables.Count) output.Write(",", TextTokenKind.Operator); output.WriteLine(); } output.Unindent(); output.WriteLine(")", TextTokenKind.Operator); } output.WriteLine(); uint baseRva = rva == 0 ? 0 : rva + method.Body.HeaderSize; long baseOffs = baseRva == 0 ? 0 : method.Module.ToFileOffset(baseRva); using (var byteReader = !options.ShowILBytes || options.CreateInstructionBytesReader == null ? null : options.CreateInstructionBytesReader(method)) { if (detectControlStructure && body.Instructions.Count > 0) { int index = 0; HashSet<uint> branchTargets = GetBranchTargets(body.Instructions); WriteStructureBody(body, new ILStructure(body), branchTargets, ref index, debugSymbols, method.Body.GetCodeSize(), baseRva, baseOffs, byteReader, method); } else { var instructions = method.Body.Instructions; for (int i = 0; i < instructions.Count; i++) { var inst = instructions[i]; var startLocation = output.Location; inst.WriteTo(output, options, baseRva, baseOffs, byteReader, method); if (debugSymbols != null) { // add IL code mappings - used in debugger var next = i + 1 < instructions.Count ? instructions[i + 1] : null; debugSymbols.MemberCodeMappings.Add(new SourceCodeMapping(new ILRange(inst.Offset, next == null ? (uint)method.Body.GetCodeSize() : next.Offset), output.Location, output.Location, debugSymbols)); } output.WriteLine(); } if (method.Body.HasExceptionHandlers) { output.WriteLine(); foreach (var eh in method.Body.ExceptionHandlers) { eh.WriteTo(output, method); output.WriteLine(); } } } } }
void WriteStructureBody(CilBody body, ILStructure s, HashSet<uint> branchTargets, ref int index, MemberMapping debugSymbols, int codeSize, uint baseRva, long baseOffs, IInstructionBytesReader byteReader, MethodDef method) { bool isFirstInstructionInStructure = true; bool prevInstructionWasBranch = false; int childIndex = 0; var instructions = body.Instructions; while (index < instructions.Count) { Instruction inst = instructions[index]; if (inst.Offset >= s.EndOffset) break; uint offset = inst.Offset; if (childIndex < s.Children.Count && s.Children[childIndex].StartOffset <= offset && offset < s.Children[childIndex].EndOffset) { ILStructure child = s.Children[childIndex++]; WriteStructureHeader(child); WriteStructureBody(body, child, branchTargets, ref index, debugSymbols, codeSize, baseRva, baseOffs, byteReader, method); WriteStructureFooter(child); } else { if (!isFirstInstructionInStructure && (prevInstructionWasBranch || branchTargets.Contains(offset))) { output.WriteLine(); // put an empty line after branches, and in front of branch targets } var startLocation = output.Location; inst.WriteTo(output, options, baseRva, baseOffs, byteReader, method); // add IL code mappings - used in debugger if (debugSymbols != null) { var next = index + 1 < instructions.Count ? instructions[index + 1] : null; debugSymbols.MemberCodeMappings.Add(new SourceCodeMapping(new ILRange(inst.Offset, next == null ? (uint)codeSize : next.Offset), startLocation, output.Location, debugSymbols)); } output.WriteLine(); prevInstructionWasBranch = inst.OpCode.FlowControl == FlowControl.Branch || inst.OpCode.FlowControl == FlowControl.Cond_Branch || inst.OpCode.FlowControl == FlowControl.Return || inst.OpCode.FlowControl == FlowControl.Throw; index++; } isFirstInstructionInStructure = false; } }