Merge pull request #50 from jkotas/PDB
Add back PDB symbols for CppCodeGen
This commit is contained in:
Коммит
c1586e674f
|
@ -20,11 +20,12 @@ namespace ILToNative
|
|||
public struct CompilationOptions
|
||||
{
|
||||
public bool IsCppCodeGen;
|
||||
public bool NoLineNumbers;
|
||||
}
|
||||
|
||||
public partial class Compilation
|
||||
{
|
||||
readonly TypeSystemContext _typeSystemContext;
|
||||
readonly CompilerTypeSystemContext _typeSystemContext;
|
||||
readonly CompilationOptions _options;
|
||||
|
||||
Dictionary<TypeDesc, RegisteredType> _registeredTypes = new Dictionary<TypeDesc, RegisteredType>();
|
||||
|
@ -36,7 +37,7 @@ namespace ILToNative
|
|||
|
||||
ILToNative.CppCodeGen.CppWriter _cppWriter = null;
|
||||
|
||||
public Compilation(TypeSystemContext typeSystemContext, CompilationOptions options)
|
||||
public Compilation(CompilerTypeSystemContext typeSystemContext, CompilationOptions options)
|
||||
{
|
||||
_typeSystemContext = typeSystemContext;
|
||||
_options = options;
|
||||
|
@ -44,7 +45,7 @@ namespace ILToNative
|
|||
_nameMangler = new NameMangler(this);
|
||||
}
|
||||
|
||||
public TypeSystemContext TypeSystemContext
|
||||
public CompilerTypeSystemContext TypeSystemContext
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -90,6 +91,14 @@ namespace ILToNative
|
|||
}
|
||||
}
|
||||
|
||||
internal CompilationOptions Options
|
||||
{
|
||||
get
|
||||
{
|
||||
return _options;
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<RegisteredType> RegisteredTypes
|
||||
{
|
||||
get
|
||||
|
|
|
@ -3,13 +3,16 @@
|
|||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
using System.Reflection.PortableExecutable;
|
||||
|
||||
using Internal.TypeSystem;
|
||||
using Internal.TypeSystem.Ecma;
|
||||
using Internal.IL;
|
||||
|
||||
namespace ILToNative
|
||||
{
|
||||
|
@ -55,8 +58,7 @@ namespace ILToNative
|
|||
class ModuleData
|
||||
{
|
||||
public string Path;
|
||||
// public ISymbolReader SymbolReader;
|
||||
|
||||
public Microsoft.DiaSymReader.ISymUnmanagedReader PdbReader;
|
||||
}
|
||||
Dictionary<EcmaModule, ModuleData> _moduleData = new Dictionary<EcmaModule, ModuleData>();
|
||||
|
||||
|
@ -130,10 +132,75 @@ namespace ILToNative
|
|||
|
||||
_modules.Add(simpleName, module);
|
||||
|
||||
ModuleData moduleData = new ModuleData() { Path = filePath };
|
||||
ModuleData moduleData = new ModuleData() {
|
||||
Path = filePath
|
||||
};
|
||||
|
||||
InitializeSymbolReader(moduleData);
|
||||
|
||||
_moduleData.Add(module, moduleData);
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
//
|
||||
// Symbols
|
||||
//
|
||||
|
||||
PdbSymbolProvider _pdbSymbolProvider;
|
||||
|
||||
private void InitializeSymbolReader(ModuleData moduleData)
|
||||
{
|
||||
if (_pdbSymbolProvider == null)
|
||||
_pdbSymbolProvider = new PdbSymbolProvider();
|
||||
|
||||
moduleData.PdbReader = _pdbSymbolProvider.GetSymbolReaderForFile(moduleData.Path);
|
||||
}
|
||||
|
||||
public IEnumerable<ILSequencePoint> GetSequencePointsForMethod(MethodDesc method)
|
||||
{
|
||||
EcmaMethod ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod;
|
||||
if (ecmaMethod == null)
|
||||
return null;
|
||||
|
||||
ModuleData moduleData = _moduleData[ecmaMethod.Module];
|
||||
if (moduleData.PdbReader == null)
|
||||
return null;
|
||||
|
||||
return _pdbSymbolProvider.GetSequencePointsForMethod(moduleData.PdbReader, MetadataTokens.GetToken(ecmaMethod.Handle));
|
||||
}
|
||||
|
||||
public IEnumerable<LocalVariable> GetLocalVariableNamesForMethod(MethodDesc method)
|
||||
{
|
||||
EcmaMethod ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod;
|
||||
if (ecmaMethod == null)
|
||||
return null;
|
||||
|
||||
ModuleData moduleData = _moduleData[ecmaMethod.Module];
|
||||
if (moduleData.PdbReader == null)
|
||||
return null;
|
||||
|
||||
return _pdbSymbolProvider.GetLocalVariableNamesForMethod(moduleData.PdbReader, MetadataTokens.GetToken(ecmaMethod.Handle));
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetParameterNamesForMethod(MethodDesc method)
|
||||
{
|
||||
EcmaMethod ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod;
|
||||
if (ecmaMethod == null)
|
||||
yield break;
|
||||
|
||||
ParameterHandleCollection parameters = ecmaMethod.MetadataReader.GetMethodDefinition(ecmaMethod.Handle).GetParameters();
|
||||
|
||||
if (!ecmaMethod.Signature.IsStatic)
|
||||
{
|
||||
yield return "_this";
|
||||
}
|
||||
|
||||
foreach (var parameterHandle in parameters)
|
||||
{
|
||||
Parameter p = ecmaMethod.MetadataReader.GetParameter(parameterHandle);
|
||||
yield return ecmaMethod.MetadataReader.GetString(p.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,228 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Internal.IL;
|
||||
|
||||
using Microsoft.DiaSymReader;
|
||||
|
||||
namespace ILToNative
|
||||
{
|
||||
// For now, open PDB files using legacy desktop SymBinder
|
||||
|
||||
class PdbSymbolProvider
|
||||
{
|
||||
[Guid("809c652e-7396-11d2-9771-00a0c9b4d50c")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[ComVisible(true)]
|
||||
private interface IMetaDataDispenser
|
||||
{
|
||||
// We need to be able to call OpenScope, which is the 2nd vtable slot.
|
||||
// Thus we need this one placeholder here to occupy the first slot..
|
||||
void DefineScope_Placeholder();
|
||||
|
||||
[PreserveSig]
|
||||
int OpenScope([In, MarshalAs(UnmanagedType.LPWStr)] String szScope, [In] Int32 dwOpenFlags, [In] ref Guid riid, [Out, MarshalAs(UnmanagedType.IUnknown)] out Object punk);
|
||||
|
||||
// Don't need any other methods.
|
||||
}
|
||||
|
||||
// Since we're just blindly passing this interface through managed code to the Symbinder, we don't care about actually
|
||||
// importing the specific methods.
|
||||
// This needs to be public so that we can call Marshal.GetComInterfaceForObject() on it to get the
|
||||
// underlying metadata pointer.
|
||||
[Guid("7DAC8207-D3AE-4c75-9B67-92801A497D44")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[ComVisible(true)]
|
||||
public interface IMetadataImport
|
||||
{
|
||||
// Just need a single placeholder method so that it doesn't complain about an empty interface.
|
||||
void Placeholder();
|
||||
}
|
||||
|
||||
[DllImport("clr.dll")]
|
||||
private static extern int MetaDataGetDispenser([In] ref Guid rclsid,
|
||||
[In] ref Guid riid,
|
||||
[Out, MarshalAs(UnmanagedType.Interface)] out Object ppv);
|
||||
|
||||
[DllImport("ole32.dll")]
|
||||
static extern int CoCreateInstance(ref Guid rclsid, IntPtr pUnkOuter,
|
||||
Int32 dwClsContext,
|
||||
ref Guid riid,
|
||||
[MarshalAs(UnmanagedType.Interface)] out object ppv);
|
||||
|
||||
void ThrowExceptionForHR(int hr)
|
||||
{
|
||||
Marshal.ThrowExceptionForHR(hr, new IntPtr(-1));
|
||||
}
|
||||
|
||||
IMetaDataDispenser _metadataDispenser;
|
||||
|
||||
ISymUnmanagedBinder _symBinder;
|
||||
|
||||
public PdbSymbolProvider()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create a COM Metadata dispenser
|
||||
Guid dispenserClassID = new Guid(0xe5cb7a31, 0x7512, 0x11d2, 0x89, 0xce, 0x00, 0x80, 0xc7, 0x92, 0xe5, 0xd8); // CLSID_CorMetaDataDispenser
|
||||
Guid dispenserIID = new Guid(0x809c652e, 0x7396, 0x11d2, 0x97, 0x71, 0x00, 0xa0, 0xc9, 0xb4, 0xd5, 0x0c); // IID_IMetaDataDispenser
|
||||
object objDispenser;
|
||||
if (MetaDataGetDispenser(ref dispenserClassID, ref dispenserIID, out objDispenser) < 0)
|
||||
return;
|
||||
_metadataDispenser = (IMetaDataDispenser)objDispenser;
|
||||
|
||||
Guid symBinderClassID = new Guid(0x0A29FF9E, 0x7F9C, 0x4437, 0x8B, 0x11, 0xF4, 0x24, 0x49, 0x1E, 0x39, 0x31); // CLSID_CorSymBinder
|
||||
Guid symBinderIID = new Guid(0xAA544d42, 0x28CB, 0x11d3, 0xbd, 0x22, 0x00, 0x00, 0xf8, 0x08, 0x49, 0xbd); // IID_ISymUnmanagedBinder
|
||||
object objBinder;
|
||||
if (CoCreateInstance(ref symBinderClassID,
|
||||
IntPtr.Zero, // pUnkOuter
|
||||
1, // CLSCTX_INPROC_SERVER
|
||||
ref symBinderIID,
|
||||
out objBinder) < 0)
|
||||
return;
|
||||
_symBinder = (ISymUnmanagedBinder)objBinder;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public ISymUnmanagedReader GetSymbolReaderForFile(string metadataFileName)
|
||||
{
|
||||
if (!File.Exists(Path.ChangeExtension(metadataFileName, ".pdb")))
|
||||
return null;
|
||||
|
||||
if (_metadataDispenser == null || _symBinder == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
Guid importerIID = new Guid(0x7dac8207, 0xd3ae, 0x4c75, 0x9b, 0x67, 0x92, 0x80, 0x1a, 0x49, 0x7d, 0x44); // IID_IMetaDataImport
|
||||
|
||||
// Open an metadata importer on the given filename. We'll end up passing this importer straight
|
||||
// through to the Binder.
|
||||
object objImporter;
|
||||
if (_metadataDispenser.OpenScope(metadataFileName, 0x00000010 /* read only */, ref importerIID, out objImporter) < 0)
|
||||
return null;
|
||||
|
||||
ISymUnmanagedReader reader;
|
||||
if (_symBinder.GetReaderForFile(objImporter, metadataFileName, "", out reader) < 0)
|
||||
return null;
|
||||
return reader;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<ISymUnmanagedDocument, string> _urlCache = new Dictionary<ISymUnmanagedDocument, string>();
|
||||
|
||||
private string GetUrl(ISymUnmanagedDocument doc)
|
||||
{
|
||||
string url;
|
||||
if (_urlCache.TryGetValue(doc, out url))
|
||||
return url;
|
||||
|
||||
int urlLength;
|
||||
ThrowExceptionForHR(doc.GetUrl(0, out urlLength, null));
|
||||
|
||||
char[] urlBuffer = new char[urlLength];
|
||||
ThrowExceptionForHR(doc.GetUrl(urlLength, out urlLength, urlBuffer));
|
||||
|
||||
url = new string(urlBuffer, 0, urlLength);
|
||||
_urlCache.Add(doc, url);
|
||||
return url;
|
||||
}
|
||||
|
||||
public IEnumerable<ILSequencePoint> GetSequencePointsForMethod(ISymUnmanagedReader reader, int methodToken)
|
||||
{
|
||||
ISymUnmanagedMethod symbolMethod;
|
||||
if (reader.GetMethod(methodToken, out symbolMethod) < 0)
|
||||
yield break;
|
||||
|
||||
int count;
|
||||
ThrowExceptionForHR(symbolMethod.GetSequencePointCount(out count));
|
||||
|
||||
ISymUnmanagedDocument[] docs = new ISymUnmanagedDocument[count];
|
||||
int[] lineNumbers = new int[count];
|
||||
int[] ilOffsets = new int[count];
|
||||
|
||||
ThrowExceptionForHR(symbolMethod.GetSequencePoints(count, out count, ilOffsets, docs, lineNumbers, null, null, null));
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (lineNumbers[i] == 0xFEEFEE)
|
||||
continue;
|
||||
|
||||
yield return new ILSequencePoint() { Document = GetUrl(docs[i]), LineNumber = lineNumbers[i], Offset = ilOffsets[i] };
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Gather the local details in a scope and then recurse to child scopes
|
||||
//
|
||||
private void ProbeScopeForLocals(List<LocalVariable> variables, ISymUnmanagedScope scope)
|
||||
{
|
||||
int localCount;
|
||||
ThrowExceptionForHR(scope.GetLocalCount(out localCount));
|
||||
|
||||
ISymUnmanagedVariable[] locals = new ISymUnmanagedVariable[localCount];
|
||||
ThrowExceptionForHR(scope.GetLocals(localCount, out localCount, locals));
|
||||
|
||||
for (int i = 0; i < localCount; i++)
|
||||
{
|
||||
var local = locals[i];
|
||||
|
||||
int slot;
|
||||
ThrowExceptionForHR(local.GetAddressField1(out slot));
|
||||
|
||||
int nameLength;
|
||||
ThrowExceptionForHR(local.GetName(0, out nameLength, null));
|
||||
|
||||
char[] nameBuffer = new char[nameLength];
|
||||
ThrowExceptionForHR(local.GetName(nameLength, out nameLength, nameBuffer));
|
||||
|
||||
int attributes;
|
||||
ThrowExceptionForHR(local.GetAttributes(out attributes));
|
||||
|
||||
variables.Add(new LocalVariable() { Slot = slot, Name = new String(nameBuffer, 0, nameLength), CompilerGenerated = (attributes & 0x1) != 0 });
|
||||
}
|
||||
|
||||
int childrenCount;
|
||||
ThrowExceptionForHR(scope.GetChildren(0, out childrenCount, null));
|
||||
|
||||
ISymUnmanagedScope[] children = new ISymUnmanagedScope[childrenCount];
|
||||
ThrowExceptionForHR(scope.GetChildren(childrenCount, out childrenCount, children));
|
||||
|
||||
for (int i = 0; i < childrenCount; i++)
|
||||
{
|
||||
ProbeScopeForLocals(variables, children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Recursively scan the scopes for a method stored in a PDB and gather the local slots
|
||||
// and names for all of them. This assumes a CSC-like compiler that doesn't re-use
|
||||
// local slots in the same method across scopes.
|
||||
//
|
||||
public IEnumerable<LocalVariable> GetLocalVariableNamesForMethod(ISymUnmanagedReader reader, int methodToken)
|
||||
{
|
||||
ISymUnmanagedMethod symbolMethod;
|
||||
if (reader.GetMethod(methodToken, out symbolMethod) < 0)
|
||||
return null;
|
||||
|
||||
ISymUnmanagedScope rootScope;
|
||||
ThrowExceptionForHR(symbolMethod.GetRootScope(out rootScope));
|
||||
|
||||
var variables = new List<LocalVariable>();
|
||||
ProbeScopeForLocals(variables, rootScope);
|
||||
return variables;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -118,24 +118,19 @@ namespace ILToNative.CppCodeGen
|
|||
argCount++;
|
||||
|
||||
List<string> parameterNames = null;
|
||||
#if TODO // PDBs
|
||||
if (ParameterNamesCallback != null)
|
||||
IEnumerable<string> parameters = _compilation.TypeSystemContext.GetParameterNamesForMethod(method);
|
||||
if (parameters != null)
|
||||
{
|
||||
IEnumerable<string> parameters = ParameterNamesCallback(method);
|
||||
if (parameters != null)
|
||||
parameterNames = new List<string>(parameters);
|
||||
if (parameterNames.Count != 0)
|
||||
{
|
||||
parameterNames = new List<string>(parameters);
|
||||
if (parameterNames.Count != 0)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(parameterNames.Count == argCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
parameterNames = null;
|
||||
}
|
||||
System.Diagnostics.Debug.Assert(parameterNames.Count == argCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
parameterNames = null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < argCount; i++)
|
||||
{
|
||||
|
@ -194,24 +189,19 @@ namespace ILToNative.CppCodeGen
|
|||
argCount++;
|
||||
|
||||
List<string> parameterNames = null;
|
||||
#if TODO // PDBs
|
||||
if (ParameterNamesCallback != null)
|
||||
IEnumerable<string> parameters = _compilation.TypeSystemContext.GetParameterNamesForMethod(method);
|
||||
if (parameters != null)
|
||||
{
|
||||
IEnumerable<string> parameters = ParameterNamesCallback(method);
|
||||
if (parameters != null)
|
||||
parameterNames = new List<string>(parameters);
|
||||
if (parameterNames.Count != 0)
|
||||
{
|
||||
parameterNames = new List<string>(parameters);
|
||||
if (parameterNames.Count != 0)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(parameterNames.Count == argCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
parameterNames = null;
|
||||
}
|
||||
System.Diagnostics.Debug.Assert(parameterNames.Count == argCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
parameterNames = null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < argCount; i++)
|
||||
{
|
||||
|
@ -367,28 +357,22 @@ namespace ILToNative.CppCodeGen
|
|||
|
||||
var ilImporter = new ILImporter(_compilation, this, method, methodIL);
|
||||
|
||||
#if TODO // PDBS
|
||||
if (SequencePointsCallback != null)
|
||||
CompilerTypeSystemContext typeSystemContext = _compilation.TypeSystemContext;
|
||||
|
||||
if (!_compilation.Options.NoLineNumbers)
|
||||
{
|
||||
IEnumerable<ILSequencePoint> sequencePoints = SequencePointsCallback(method);
|
||||
IEnumerable<ILSequencePoint> sequencePoints = typeSystemContext.GetSequencePointsForMethod(method);
|
||||
if (sequencePoints != null)
|
||||
ilImporter.SetSequencePoints(sequencePoints);
|
||||
}
|
||||
|
||||
if (LocalVariablesCallback != null)
|
||||
{
|
||||
IEnumerable<LocalVariable> localVariables = LocalVariablesCallback(method);
|
||||
if (localVariables != null)
|
||||
ilImporter.SetLocalVariables(localVariables);
|
||||
}
|
||||
IEnumerable<LocalVariable> localVariables = typeSystemContext.GetLocalVariableNamesForMethod(method);
|
||||
if (localVariables != null)
|
||||
ilImporter.SetLocalVariables(localVariables);
|
||||
|
||||
if (ParameterNamesCallback != null)
|
||||
{
|
||||
IEnumerable<string> parameters = ParameterNamesCallback(method);
|
||||
if (parameters != null)
|
||||
ilImporter.SetParameterNames(parameters);
|
||||
}
|
||||
#endif
|
||||
IEnumerable<string> parameters = typeSystemContext.GetParameterNamesForMethod(method);
|
||||
if (parameters != null)
|
||||
ilImporter.SetParameterNames(parameters);
|
||||
|
||||
string methodCode;
|
||||
try
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
<Compile Include="Compiler\MethodCode.cs" />
|
||||
<Compile Include="Compiler\MethodExtensions.cs" />
|
||||
<Compile Include="Compiler\NameMangler.cs" />
|
||||
<Compile Include="Compiler\PdbSymbolProvider.cs" />
|
||||
<Compile Include="Compiler\ReadyToRunHelper.cs" />
|
||||
<Compile Include="Compiler\RegisteredField.cs" />
|
||||
<Compile Include="Compiler\RegisteredMethod.cs" />
|
||||
|
|
|
@ -16,9 +16,12 @@
|
|||
"System.Reflection.Extensions": "4.0.0",
|
||||
"System.AppContext": "4.0.0",
|
||||
"System.Collections.Immutable": "1.1.37",
|
||||
"System.Reflection.Metadata": "1.0.22"
|
||||
"System.Reflection.Metadata": "1.0.22",
|
||||
"Microsoft.DiaSymReader": "1.0.6"
|
||||
},
|
||||
"frameworks": {
|
||||
"dotnet": {}
|
||||
"dotnet": {
|
||||
"imports": "portable-net452"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,7 +17,8 @@
|
|||
"System.Console": "4.0.0-beta-*",
|
||||
"System.AppContext": "4.0.0",
|
||||
"System.Collections.Immutable": "1.1.37",
|
||||
"System.Reflection.Metadata": "1.0.22"
|
||||
"System.Reflection.Metadata": "1.0.22",
|
||||
"Microsoft.DiaSymReader": "1.0.6"
|
||||
},
|
||||
"frameworks": {
|
||||
"net46": {}
|
||||
|
|
|
@ -73,6 +73,10 @@ namespace ILToNative
|
|||
_options.IsCppCodeGen = true;
|
||||
break;
|
||||
|
||||
case "nolinenumbers":
|
||||
_options.NoLineNumbers = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new CommandLineException("Unrecognized option: " + parser.GetCurrentOption());
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче