// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.Ast; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.ILAst; using ICSharpCode.NRefactory; using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; namespace ICSharpCode.Decompiler { /// /// Maps the source code to IL. /// public sealed class SourceCodeMapping { /// /// Gets or sets the start location of the instruction. /// public TextLocation StartLocation { get; set; } /// /// Gets or sets the end location of the instruction. /// public TextLocation EndLocation { get; set; } /// /// Gets or sets IL Range offset for the source code line. E.g.: 13-19 <-> 135. /// public ILRange ILInstructionOffset { get; set; } /// /// Gets or sets the member mapping this source code mapping belongs to. /// public MemberMapping MemberMapping { get; set; } /// /// Retrieves the array that contains the IL range and the missing gaps between ranges. /// /// The array representation of the step aranges. public int[] ToArray(bool isMatch) { var currentList = new List(); // add list for the current source code line currentList.AddRange(ILRange.OrderAndJoint(MemberMapping.MemberCodeMappings .FindAll(m => m.StartLocation.Line == this.StartLocation.Line) .ConvertAll(m => m.ILInstructionOffset))); if (!isMatch) { // add inverted currentList.AddRange(MemberMapping.InvertedList); } else { // if the current list contains the last mapping, add also the last gap var lastInverted = MemberMapping.InvertedList.LastOrDefault(); if (lastInverted != null && lastInverted.From == currentList[currentList.Count - 1].To) currentList.Add(lastInverted); } // set the output var resultList = new List(); foreach (var element in ILRange.OrderAndJoint(currentList)) { resultList.Add(element.From); resultList.Add(element.To); } return resultList.ToArray(); } } /// /// Stores the member information and its source code mappings. /// public sealed class MemberMapping { IEnumerable invertedList; internal MemberMapping() { } public MemberMapping(MethodDefinition method) { this.MetadataToken = method.MetadataToken.ToInt32(); this.MemberCodeMappings = new List(); this.MemberReference = method; this.CodeSize = method.Body.CodeSize; } /// /// Gets or sets the type of the mapping. /// public MemberReference MemberReference { get; internal set; } /// /// Metadata token of the member. /// public int MetadataToken { get; internal set; } /// /// Gets or sets the code size for the member mapping. /// public int CodeSize { get; internal set; } /// /// Gets or sets the source code mappings. /// public List MemberCodeMappings { get; internal set; } /// /// Gets or sets the local variables. /// public IEnumerable LocalVariables { get; internal set; } /// /// Gets the inverted IL Ranges.
/// E.g.: for (0-9, 11-14, 14-18, 21-25) => (9-11,18-21). ///
/// IL Range inverted list. public IEnumerable InvertedList { get { if (invertedList == null) { var list = MemberCodeMappings.ConvertAll( s => new ILRange { From = s.ILInstructionOffset.From, To = s.ILInstructionOffset.To }); invertedList = ILRange.OrderAndJoint(ILRange.Invert(list, CodeSize)); } return invertedList; } } } /// /// Code mappings helper class. /// public static class CodeMappings { /// /// Gets source code mapping and metadata token based on type name and line number. /// /// Code mappings storage. /// Member reference name. /// Line number. /// Metadata token. /// public static SourceCodeMapping GetInstructionByLineNumber( this MemberMapping codeMapping, int lineNumber, out int metadataToken) { if (codeMapping == null) throw new ArgumentException("CodeMappings storage must be valid!"); var map = codeMapping.MemberCodeMappings.Find(m => m.StartLocation.Line == lineNumber); if (map != null) { metadataToken = codeMapping.MetadataToken; return map; } metadataToken = 0; return null; } /// /// Gets a mapping given a type, a token and an IL offset. /// /// Code mappings storage. /// Token. /// IL offset. /// True, if perfect match. /// A code mapping. public static SourceCodeMapping GetInstructionByTokenAndOffset( this MemberMapping codeMapping, int 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.ILInstructionOffset.From <= ilOffset && ilOffset < m.ILInstructionOffset.To); if (map == null) { // get the immediate next one map = codeMapping.MemberCodeMappings.Find(m => m.ILInstructionOffset.From > ilOffset); isMatch = false; if (map == null) map = codeMapping.MemberCodeMappings.LastOrDefault(); // get the last return map; } isMatch = true; return map; } /// /// Gets the source code and type name from metadata token and offset. /// /// Code mapping storage. /// Metadata token. /// IL offset. /// Type definition. /// Line number. /// It is possible to exist to different types from different assemblies with the same metadata token. public static bool GetInstructionByTokenAndOffset( this MemberMapping mapping, int ilOffset, out MemberReference member, out int line) { member = null; line = 0; if (mapping == null) throw new ArgumentException("CodeMappings storage must be valid!"); var codeMapping = mapping.MemberCodeMappings.Find( cm => cm.ILInstructionOffset.From <= ilOffset && ilOffset <= cm.ILInstructionOffset.To - 1); if (codeMapping == null) { codeMapping = mapping.MemberCodeMappings.Find(cm => cm.ILInstructionOffset.From > ilOffset); if (codeMapping == null) { codeMapping = mapping.MemberCodeMappings.LastOrDefault(); if (codeMapping == null) return false; } } member = mapping.MemberReference; line = codeMapping.StartLocation.Line; return true; } } }