2014-08-13 10:39:27 +01:00
|
|
|
//
|
|
|
|
// Author:
|
|
|
|
// Jb Evain (jbevain@gmail.com)
|
|
|
|
//
|
2016-11-10 13:04:39 +00:00
|
|
|
// Copyright (c) 2008 - 2015 Jb Evain
|
|
|
|
// Copyright (c) 2008 - 2011 Novell, Inc.
|
2014-08-13 10:39:27 +01:00
|
|
|
//
|
2016-11-10 13:04:39 +00:00
|
|
|
// Licensed under the MIT/X11 license.
|
2014-08-13 10:39:27 +01:00
|
|
|
//
|
|
|
|
|
|
|
|
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 {
|
|
|
|
|
|
|
|
#if !READ_ONLY
|
2016-11-10 13:04:39 +00:00
|
|
|
public sealed class MdbWriterProvider : ISymbolWriterProvider {
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
public ISymbolWriter GetSymbolWriter (ModuleDefinition module, string fileName)
|
|
|
|
{
|
2016-11-10 13:04:39 +00:00
|
|
|
Mixin.CheckModule (module);
|
|
|
|
Mixin.CheckFileName (fileName);
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
return new MdbWriter (module.Mvid, fileName);
|
|
|
|
}
|
|
|
|
|
|
|
|
public ISymbolWriter GetSymbolWriter (ModuleDefinition module, Stream symbolStream)
|
|
|
|
{
|
|
|
|
throw new NotImplementedException ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
public sealed class MdbWriter : ISymbolWriter {
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
readonly Guid mvid;
|
|
|
|
readonly MonoSymbolWriter writer;
|
|
|
|
readonly Dictionary<string, SourceFile> source_files;
|
|
|
|
|
|
|
|
public MdbWriter (Guid mvid, string assembly)
|
|
|
|
{
|
|
|
|
this.mvid = mvid;
|
|
|
|
this.writer = new MonoSymbolWriter (assembly);
|
|
|
|
this.source_files = new Dictionary<string, SourceFile> ();
|
|
|
|
}
|
|
|
|
|
2017-06-07 13:16:24 +00:00
|
|
|
public ISymbolReaderProvider GetReaderProvider ()
|
|
|
|
{
|
|
|
|
return new MdbReaderProvider ();
|
|
|
|
}
|
|
|
|
|
2014-08-13 10:39:27 +01:00
|
|
|
SourceFile GetSourceFile (Document document)
|
|
|
|
{
|
|
|
|
var url = document.Url;
|
|
|
|
|
|
|
|
SourceFile source_file;
|
|
|
|
if (source_files.TryGetValue (url, out source_file))
|
|
|
|
return source_file;
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
var entry = writer.DefineDocument (url, null, document.Hash != null && document.Hash.Length == 16 ? document.Hash : null);
|
2014-08-13 10:39:27 +01:00
|
|
|
var compile_unit = writer.DefineCompilationUnit (entry);
|
|
|
|
|
|
|
|
source_file = new SourceFile (compile_unit, entry);
|
|
|
|
source_files.Add (url, source_file);
|
|
|
|
return source_file;
|
|
|
|
}
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
void Populate (Collection<SequencePoint> sequencePoints, int [] offsets,
|
|
|
|
int [] startRows, int [] endRows, int [] startCols, int [] endCols, out SourceFile file)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
|
|
|
SourceFile source_file = null;
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
for (int i = 0; i < sequencePoints.Count; i++) {
|
|
|
|
var sequence_point = sequencePoints [i];
|
|
|
|
offsets [i] = sequence_point.Offset;
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
if (source_file == null)
|
|
|
|
source_file = GetSourceFile (sequence_point.Document);
|
|
|
|
|
|
|
|
startRows [i] = sequence_point.StartLine;
|
2016-11-10 13:04:39 +00:00
|
|
|
endRows [i] = sequence_point.EndLine;
|
2014-08-13 10:39:27 +01:00
|
|
|
startCols [i] = sequence_point.StartColumn;
|
2016-11-10 13:04:39 +00:00
|
|
|
endCols [i] = sequence_point.EndColumn;
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
file = source_file;
|
|
|
|
}
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
public void Write (MethodDebugInformation info)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
2016-11-10 13:04:39 +00:00
|
|
|
var method = new SourceMethod (info.method);
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
var sequence_points = info.SequencePoints;
|
|
|
|
int count = sequence_points.Count;
|
2014-08-13 10:39:27 +01:00
|
|
|
if (count == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var offsets = new int [count];
|
|
|
|
var start_rows = new int [count];
|
2016-11-10 13:04:39 +00:00
|
|
|
var end_rows = new int [count];
|
2014-08-13 10:39:27 +01:00
|
|
|
var start_cols = new int [count];
|
2016-11-10 13:04:39 +00:00
|
|
|
var end_cols = new int [count];
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
SourceFile file;
|
2016-11-10 13:04:39 +00:00
|
|
|
Populate (sequence_points, offsets, start_rows, end_rows, start_cols, end_cols, out file);
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
var builder = writer.OpenMethod (file.CompilationUnit, 0, method);
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
for (int i = 0; i < count; i++) {
|
2014-08-13 10:39:27 +01:00
|
|
|
builder.MarkSequencePoint (
|
|
|
|
offsets [i],
|
|
|
|
file.CompilationUnit.SourceFile,
|
|
|
|
start_rows [i],
|
|
|
|
start_cols [i],
|
2016-11-10 13:04:39 +00:00
|
|
|
end_rows [i],
|
|
|
|
end_cols [i],
|
2014-08-13 10:39:27 +01:00
|
|
|
false);
|
2016-11-10 13:04:39 +00:00
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
if (info.scope != null)
|
|
|
|
WriteRootScope (info.scope, info);
|
2014-08-13 10:39:27 +01:00
|
|
|
|
|
|
|
writer.CloseMethod ();
|
|
|
|
}
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
void WriteRootScope (ScopeDebugInformation scope, MethodDebugInformation info)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
2016-11-10 13:04:39 +00:00
|
|
|
WriteScopeVariables (scope);
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
if (scope.HasScopes)
|
|
|
|
WriteScopes (scope.Scopes, info);
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
void WriteScope (ScopeDebugInformation scope, MethodDebugInformation info)
|
2014-08-13 10:39:27 +01:00
|
|
|
{
|
2016-11-10 13:04:39 +00:00
|
|
|
writer.OpenScope (scope.Start.Offset);
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
WriteScopeVariables (scope);
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
if (scope.HasScopes)
|
|
|
|
WriteScopes (scope.Scopes, info);
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
writer.CloseScope (scope.End.IsEndOfMethod ? info.code_size : scope.End.Offset);
|
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
void WriteScopes (Collection<ScopeDebugInformation> scopes, MethodDebugInformation info)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < scopes.Count; i++)
|
|
|
|
WriteScope (scopes [i], info);
|
|
|
|
}
|
2014-08-13 10:39:27 +01:00
|
|
|
|
2016-11-10 13:04:39 +00:00
|
|
|
void WriteScopeVariables (ScopeDebugInformation scope)
|
|
|
|
{
|
|
|
|
if (!scope.HasVariables)
|
|
|
|
return;
|
|
|
|
|
|
|
|
foreach (var variable in scope.variables)
|
|
|
|
if (!string.IsNullOrEmpty (variable.Name))
|
|
|
|
writer.DefineLocalVariable (variable.Index, variable.Name);
|
|
|
|
}
|
|
|
|
|
2017-06-07 13:16:24 +00:00
|
|
|
public ImageDebugHeader GetDebugHeader ()
|
2016-11-10 13:04:39 +00:00
|
|
|
{
|
2017-06-07 13:16:24 +00:00
|
|
|
return new ImageDebugHeader ();
|
2014-08-13 10:39:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public void Dispose ()
|
|
|
|
{
|
|
|
|
writer.WriteSymbolFile (mvid);
|
|
|
|
}
|
|
|
|
|
|
|
|
class SourceFile : ISourceFile {
|
|
|
|
|
|
|
|
readonly CompileUnitEntry compilation_unit;
|
|
|
|
readonly SourceFileEntry entry;
|
|
|
|
|
|
|
|
public SourceFileEntry Entry {
|
|
|
|
get { return entry; }
|
|
|
|
}
|
|
|
|
|
|
|
|
public CompileUnitEntry CompilationUnit {
|
|
|
|
get { return compilation_unit; }
|
|
|
|
}
|
|
|
|
|
|
|
|
public SourceFile (CompileUnitEntry comp_unit, SourceFileEntry entry)
|
|
|
|
{
|
|
|
|
this.compilation_unit = comp_unit;
|
|
|
|
this.entry = entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class SourceMethod : IMethodDef {
|
|
|
|
|
|
|
|
readonly MethodDefinition method;
|
|
|
|
|
|
|
|
public string Name {
|
|
|
|
get { return method.Name; }
|
|
|
|
}
|
|
|
|
|
|
|
|
public int Token {
|
|
|
|
get { return method.MetadataToken.ToInt32 (); }
|
|
|
|
}
|
|
|
|
|
|
|
|
public SourceMethod (MethodDefinition method)
|
|
|
|
{
|
|
|
|
this.method = method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|