223 lines
6.0 KiB
C#
223 lines
6.0 KiB
C#
|
//
|
||
|
// MdbReader.cs
|
||
|
//
|
||
|
// Author:
|
||
|
// Jb Evain (jbevain@gmail.com)
|
||
|
//
|
||
|
// Copyright (c) 2008 - 2011 Jb Evain
|
||
|
//
|
||
|
// 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.Generic;
|
||
|
using System.IO;
|
||
|
|
||
|
using Mono.Cecil.Cil;
|
||
|
using Mono.Collections.Generic;
|
||
|
using Mono.CompilerServices.SymbolWriter;
|
||
|
|
||
|
namespace Mono.Cecil.Mdb {
|
||
|
|
||
|
public class MdbReaderProvider : ISymbolReaderProvider {
|
||
|
|
||
|
public ISymbolReader GetSymbolReader (ModuleDefinition module, string fileName)
|
||
|
{
|
||
|
return new MdbReader (module, MonoSymbolFile.ReadSymbolFile (module, fileName));
|
||
|
}
|
||
|
|
||
|
public ISymbolReader GetSymbolReader (ModuleDefinition module, Stream symbolStream)
|
||
|
{
|
||
|
throw new NotImplementedException ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class MdbReader : ISymbolReader {
|
||
|
|
||
|
readonly ModuleDefinition module;
|
||
|
readonly MonoSymbolFile symbol_file;
|
||
|
readonly Dictionary<string, Document> documents;
|
||
|
|
||
|
public MdbReader (ModuleDefinition module, MonoSymbolFile symFile)
|
||
|
{
|
||
|
this.module = module;
|
||
|
this.symbol_file = symFile;
|
||
|
this.documents = new Dictionary<string, Document> ();
|
||
|
}
|
||
|
|
||
|
public bool ProcessDebugHeader (ImageDebugDirectory directory, byte [] header)
|
||
|
{
|
||
|
return symbol_file.Guid == module.Mvid;
|
||
|
}
|
||
|
|
||
|
public void Read (MethodBody body, InstructionMapper mapper)
|
||
|
{
|
||
|
var method_token = body.Method.MetadataToken;
|
||
|
var entry = symbol_file.GetMethodByToken (method_token.ToInt32 ());
|
||
|
if (entry == null)
|
||
|
return;
|
||
|
|
||
|
var scopes = ReadScopes (entry, body, mapper);
|
||
|
ReadLineNumbers (entry, mapper);
|
||
|
ReadLocalVariables (entry, body, scopes);
|
||
|
}
|
||
|
|
||
|
static void ReadLocalVariables (MethodEntry entry, MethodBody body, Scope [] scopes)
|
||
|
{
|
||
|
var locals = entry.GetLocals ();
|
||
|
|
||
|
foreach (var local in locals) {
|
||
|
if (local.Index < 0 || local.Index >= body.Variables.Count) // Mono 2.6 emits wrong local infos for iterators
|
||
|
continue;
|
||
|
|
||
|
var variable = body.Variables [local.Index];
|
||
|
variable.Name = local.Name;
|
||
|
|
||
|
var index = local.BlockIndex;
|
||
|
if (index < 0 || index >= scopes.Length)
|
||
|
continue;
|
||
|
|
||
|
var scope = scopes [index];
|
||
|
if (scope == null)
|
||
|
continue;
|
||
|
|
||
|
scope.Variables.Add (variable);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ReadLineNumbers (MethodEntry entry, InstructionMapper mapper)
|
||
|
{
|
||
|
Document document = null;
|
||
|
var table = entry.GetLineNumberTable ();
|
||
|
|
||
|
foreach (var line in table.LineNumbers) {
|
||
|
var instruction = mapper (line.Offset);
|
||
|
if (instruction == null)
|
||
|
continue;
|
||
|
|
||
|
if (document == null)
|
||
|
document = GetDocument (entry.CompileUnit.SourceFile);
|
||
|
|
||
|
instruction.SequencePoint = new SequencePoint (document) {
|
||
|
StartLine = line.Row,
|
||
|
EndLine = line.Row,
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Document GetDocument (SourceFileEntry file)
|
||
|
{
|
||
|
var file_name = file.FileName;
|
||
|
|
||
|
Document document;
|
||
|
if (documents.TryGetValue (file_name, out document))
|
||
|
return document;
|
||
|
|
||
|
document = new Document (file_name);
|
||
|
documents.Add (file_name, document);
|
||
|
|
||
|
return document;
|
||
|
}
|
||
|
|
||
|
static Scope [] ReadScopes (MethodEntry entry, MethodBody body, InstructionMapper mapper)
|
||
|
{
|
||
|
var blocks = entry.GetCodeBlocks ();
|
||
|
var scopes = new Scope [blocks.Length];
|
||
|
|
||
|
foreach (var block in blocks) {
|
||
|
if (block.BlockType != CodeBlockEntry.Type.Lexical)
|
||
|
continue;
|
||
|
|
||
|
var scope = new Scope ();
|
||
|
scope.Start = mapper (block.StartOffset);
|
||
|
scope.End = mapper (block.EndOffset);
|
||
|
|
||
|
scopes [block.Index] = scope;
|
||
|
|
||
|
if (body.Scope == null)
|
||
|
body.Scope = scope;
|
||
|
|
||
|
if (!AddScope (body.Scope, scope))
|
||
|
body.Scope = scope;
|
||
|
}
|
||
|
|
||
|
return scopes;
|
||
|
}
|
||
|
|
||
|
static bool AddScope (Scope provider, Scope scope)
|
||
|
{
|
||
|
foreach (var sub_scope in provider.Scopes) {
|
||
|
if (AddScope (sub_scope, scope))
|
||
|
return true;
|
||
|
|
||
|
if (scope.Start.Offset >= sub_scope.Start.Offset && scope.End.Offset <= sub_scope.End.Offset) {
|
||
|
sub_scope.Scopes.Add (scope);
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public void Read (MethodSymbols symbols)
|
||
|
{
|
||
|
var entry = symbol_file.GetMethodByToken (symbols.MethodToken.ToInt32 ());
|
||
|
if (entry == null)
|
||
|
return;
|
||
|
|
||
|
ReadLineNumbers (entry, symbols);
|
||
|
ReadLocalVariables (entry, symbols);
|
||
|
}
|
||
|
|
||
|
void ReadLineNumbers (MethodEntry entry, MethodSymbols symbols)
|
||
|
{
|
||
|
var table = entry.GetLineNumberTable ();
|
||
|
var lines = table.LineNumbers;
|
||
|
|
||
|
var instructions = symbols.instructions = new Collection<InstructionSymbol> (lines.Length);
|
||
|
|
||
|
for (int i = 0; i < lines.Length; i++) {
|
||
|
var line = lines [i];
|
||
|
|
||
|
instructions.Add (new InstructionSymbol (line.Offset, new SequencePoint (GetDocument (entry.CompileUnit.SourceFile)) {
|
||
|
StartLine = line.Row,
|
||
|
EndLine = line.Row,
|
||
|
}));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void ReadLocalVariables (MethodEntry entry, MethodSymbols symbols)
|
||
|
{
|
||
|
foreach (var local in entry.GetLocals ()) {
|
||
|
if (local.Index < 0 || local.Index >= symbols.Variables.Count) // Mono 2.6 emits wrong local infos for iterators
|
||
|
continue;
|
||
|
|
||
|
var variable = symbols.Variables [local.Index];
|
||
|
variable.Name = local.Name;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Dispose ()
|
||
|
{
|
||
|
symbol_file.Dispose ();
|
||
|
}
|
||
|
}
|
||
|
}
|