.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
 
 
 
 

188 lines
5.4 KiB

// 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 Decompiler;
using ICSharpCode.Decompiler.Disassembler;
using Mono.Cecil;
namespace ICSharpCode.Decompiler
{
public enum DecompiledLanguages
{
IL,
CSharp
}
/// <summary>
/// Maps the source code to IL.
/// </summary>
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.To;
return result;
}
}
/// <summary>
/// Stores the method information and its source code mappings.
/// </summary>
public sealed class MethodMapping
{
public string TypeName { get; set; }
public uint MetadataToken { get; set; }
public List<SourceCodeMapping> 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<string, List<MethodMapping>> GetStorage(DecompiledLanguages language)
{
ConcurrentDictionary<string, List<MethodMapping>> 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;
}
/// <summary>
/// Create code mapping for a method.
/// </summary>
/// <param name="method">Method to create the mapping for.</param>
/// <param name="sourceCodeMappings">Source code mapping storage.</param>
public static MethodMapping CreateCodeMapping(
this MethodDefinition method,
ConcurrentDictionary<string, List<MethodMapping>> sourceCodeMappings)
{
// create IL 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(),
TypeName = method.DeclaringType.FullName,
MethodCodeMappings = new List<SourceCodeMapping>()
};
mapping.Add(currentMethodMapping);
}
}
return currentMethodMapping;
}
/// <summary>
/// Gets source code mapping and metadata token based on type name and line number.
/// </summary>
/// <param name="codeMappings">Code mappings storage.</param>
/// <param name="typeName">Type name.</param>
/// <param name="lineNumber">Line number.</param>
/// <param name="metadataToken">Metadata token.</param>
/// <returns></returns>
public static SourceCodeMapping GetInstructionByTypeAndLine(
this ConcurrentDictionary<string, List<MethodMapping>> 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;
}
/// <summary>
/// Gets the source code and type name from metadata token and offset.
/// </summary>
/// <param name="codeMappings">Code mappings storage.</param>
/// <param name="token">Metadata token.</param>
/// <param name="ilOffset">IL offset.</param>
/// <param name="typeName">Type name.</param>
/// <param name="line">Line number.</param>
public static void GetSourceCodeFromMetadataTokenAndOffset(
this ConcurrentDictionary<string, List<MethodMapping>> codeMappings,
uint token,
int ilOffset,
out string typeName,
out int line)
{
typeName = 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) || // for CSharp
(cm.ILInstructionOffset.From == ilOffset && ilOffset == cm.ILInstructionOffset.To)); // for IL
if (codeMapping == null) {
codeMapping = mapping.MethodCodeMappings.Find(cm => (cm.ILInstructionOffset.From >= ilOffset));
if (codeMapping == null)
continue;
}
typeName = typename;
line = codeMapping.SourceCodeLine;
break;
}
}
}
}