// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using Decompiler; using ICSharpCode.Decompiler.Disassembler; using Mono.Cecil; namespace ICSharpCode.Decompiler { public enum DecompiledLanguages { IL, CSharp } /// /// Maps the source code to IL. /// public class SourceCodeMapping { public int SourceCodeLine { get; set; } public ILRange ILInstructionOffset { get; set; } public int[] ToArray() { int[] result = new int[2]; result[0] = ILInstructionOffset.From; result[1] = ILInstructionOffset.From + 1 == ILInstructionOffset.To ? ILInstructionOffset.To : ILInstructionOffset.To + 1; return result; } } /// /// Stores the method information and its source code mappings. /// public sealed class MethodMapping { public TypeDefinition Type { get; set; } public uint MetadataToken { get; set; } public List MethodCodeMappings { get; set; } public int[] ToArray() { int[] result = new int[MethodCodeMappings.Count * 2]; int i = 0; foreach (var element in MethodCodeMappings) { result[i] = element.ILInstructionOffset.From; result[i+1] = element.ILInstructionOffset.To; i+=2; } //result[MethodCodeMappings.Count] = MethodCodeMappings[MethodCodeMappings.Count - 1].ILInstructionOffset.To; return result; } } public static class CodeMappings { public static ConcurrentDictionary> GetStorage(DecompiledLanguages language) { ConcurrentDictionary> storage = null; switch (language) { case DecompiledLanguages.IL: storage = ILCodeMapping.SourceCodeMappings; break; case DecompiledLanguages.CSharp: storage = CSharpCodeMapping.SourceCodeMappings; break; default: throw new System.Exception("Invalid value for DecompiledLanguages"); } return storage; } /// /// Create code mapping for a method. /// /// Method to create the mapping for. /// Source code mapping storage. public static MethodMapping CreateCodeMapping( this MethodDefinition method, ConcurrentDictionary> sourceCodeMappings) { // create IL/CSharp code mappings - used in debugger MethodMapping currentMethodMapping = null; if (sourceCodeMappings.ContainsKey(method.DeclaringType.FullName)) { var mapping = sourceCodeMappings[method.DeclaringType.FullName]; if (mapping.Find(map => (int)map.MetadataToken == method.MetadataToken.ToInt32()) == null) { currentMethodMapping = new MethodMapping() { MetadataToken = (uint)method.MetadataToken.ToInt32(), Type = method.DeclaringType, MethodCodeMappings = new List() }; mapping.Add(currentMethodMapping); } } return currentMethodMapping; } /// /// Gets source code mapping and metadata token based on type name and line number. /// /// Code mappings storage. /// Type name. /// Line number. /// Metadata token. /// public static SourceCodeMapping GetInstructionByTypeAndLine( this ConcurrentDictionary> codeMappings, string typeName, int lineNumber, out uint metadataToken) { if (!codeMappings.ContainsKey(typeName)) { metadataToken = 0; return null; } if (lineNumber <= 0) { metadataToken = 0; return null; } var methodMappings = codeMappings[typeName]; foreach (var maping in methodMappings) { var map = maping.MethodCodeMappings.Find(m => m.SourceCodeLine == lineNumber); if (map != null) { metadataToken = maping.MetadataToken; return map; } } metadataToken = 0; return null; } /// /// Gets the source code and type name from metadata token and offset. /// /// Code mappings storage. /// Metadata token. /// IL offset. /// Type definition. /// Line number. public static bool GetSourceCodeFromMetadataTokenAndOffset( this ConcurrentDictionary> codeMappings, uint token, int ilOffset, out TypeDefinition type, out int line) { type = null; line = 0; foreach (var typename in codeMappings.Keys) { var mapping = codeMappings[typename].Find(m => m.MetadataToken == token); if (mapping == null) continue; var codeMapping = mapping.MethodCodeMappings.Find( cm => cm.ILInstructionOffset.From <= ilOffset && ilOffset <= cm.ILInstructionOffset.To - 1); if (codeMapping == null) { codeMapping = mapping.MethodCodeMappings.Find(cm => (cm.ILInstructionOffset.From >= ilOffset)); if (codeMapping == null) { codeMapping = mapping.MethodCodeMappings.LastOrDefault(); if (codeMapping == null) continue; } } type = mapping.Type; line = codeMapping.SourceCodeLine; return true; } return false; } } }