// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Ast;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.ILSpyAddIn.LaunchILSpy;
using ICSharpCode.SharpDevelop.Debugging;
using Mono.Cecil;
namespace ICSharpCode.ILSpyAddIn
{
///
/// Stores the decompilation information.
///
public class DebuggerDecompilerService : IDebuggerDecompilerService
{
ILSpyAssemblyResolver resolver;
static DebuggerDecompilerService()
{
DebugInformation = new ConcurrentDictionary();
}
internal static IDebuggerDecompilerService Instance { get; private set; }
///
/// Gets or sets the external debug information.
/// This constains the code mappings and local variables.
///
internal static ConcurrentDictionary DebugInformation { get; private set; }
public DebuggerDecompilerService()
{
Instance = this;
}
public Tuple DebugStepInformation { get; set; }
public bool CheckMappings(int typeToken)
{
DecompileInformation data = null;
DebugInformation.TryGetValue(typeToken, out data);
DecompileInformation information = data as DecompileInformation;
if (information == null)
return false;
if (information.CodeMappings == null)
return false;
return true;
}
public void DecompileOnDemand(TypeDefinition type)
{
if (type == null)
return;
if (CheckMappings(type.MetadataToken.ToInt32()))
return;
try {
DecompilerContext context = new DecompilerContext(type.Module);
AstBuilder astBuilder = new AstBuilder(context);
astBuilder.AddType(type);
astBuilder.GenerateCode(new PlainTextOutput());
int token = type.MetadataToken.ToInt32();
var info = new DecompileInformation {
CodeMappings = astBuilder.CodeMappings,
LocalVariables = astBuilder.LocalVariables,
DecompiledMemberReferences = astBuilder.DecompiledMemberReferences
};
// save the data
DebugInformation.AddOrUpdate(token, info, (k, v) => info);
} catch {
return;
}
}
public bool GetILAndTokenByLineNumber(int typeToken, int lineNumber, out int[] ilRanges, out int memberToken)
{
ilRanges = null;
memberToken = -1;
if (!CheckMappings(typeToken))
return false;
var data = (DecompileInformation)DebugInformation[typeToken];
var mappings = data.CodeMappings;
foreach (var key in mappings.Keys) {
var list = mappings[key];
var instruction = list.GetInstructionByLineNumber(lineNumber, out memberToken);
if (instruction == null)
continue;
ilRanges = new [] { instruction.ILInstructionOffset.From, instruction.ILInstructionOffset.To };
memberToken = instruction.MemberMapping.MetadataToken;
return true;
}
return false;
}
public bool GetILAndLineNumber(int typeToken, int memberToken, int ilOffset, out int[] ilRange, out int line, out bool isMatch)
{
ilRange = null;
line = -1;
isMatch = false;
if (!CheckMappings(typeToken))
return false;
var data = (DecompileInformation)DebugInformation[typeToken];
var mappings = data.CodeMappings;
if (!mappings.ContainsKey(memberToken))
return false;
var map = mappings[memberToken].GetInstructionByTokenAndOffset(memberToken, ilOffset, out isMatch);
if (map != null) {
ilRange = map.ToArray(isMatch);
line = map.SourceCodeLine;
return true;
}
return false;
}
public IEnumerable GetLocalVariables(int typeToken, int memberToken)
{
if (DebugInformation == null || !DebugInformation.ContainsKey(typeToken))
yield break;
var externalData = DebugInformation[typeToken];
IEnumerable list;
if (externalData.LocalVariables.TryGetValue(memberToken, out list)) {
foreach (var local in list) {
if (local.IsParameter)
continue;
if (string.IsNullOrEmpty(local.Name))
continue;
yield return local.Name;
}
}
}
public object GetLocalVariableIndex(int typeToken, int memberToken, string name)
{
if (DebugInformation == null || !DebugInformation.ContainsKey(typeToken))
return null;
var externalData = DebugInformation[typeToken];
IEnumerable list;
if (externalData.LocalVariables.TryGetValue(memberToken, out list)) {
foreach (var local in list) {
if (local.IsParameter)
continue;
if (local.Name == name)
return new[] { local.OriginalVariable.Index };
}
}
return null;
}
public IAssemblyResolver GetAssemblyResolver(string assemblyFile)
{
if (string.IsNullOrEmpty(assemblyFile))
throw new ArgumentException("assemblyFile is null or empty");
string folderPath = Path.GetDirectoryName(assemblyFile);
if (resolver == null)
return (resolver = new ILSpyAssemblyResolver(folderPath));
if (string.Compare(folderPath, resolver.FolderPath, StringComparison.OrdinalIgnoreCase) != 0)
return (resolver = new ILSpyAssemblyResolver(folderPath));
return resolver;
}
}
}