diff --git a/DotNetAstGen/DotNetAstGen.csproj b/DotNetAstGen/DotNetAstGen.csproj index fb079fc..fd967eb 100644 --- a/DotNetAstGen/DotNetAstGen.csproj +++ b/DotNetAstGen/DotNetAstGen.csproj @@ -15,6 +15,8 @@ + + diff --git a/DotNetAstGen/PDBGenerator.cs b/DotNetAstGen/PDBGenerator.cs new file mode 100644 index 0000000..4f9697b --- /dev/null +++ b/DotNetAstGen/PDBGenerator.cs @@ -0,0 +1,63 @@ +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.CSharp; +using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; +using ICSharpCode.Decompiler.DebugInfo; +using ICSharpCode.Decompiler.Disassembler; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.Solution; +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpyX.PdbProvider; + +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +using Microsoft.Extensions.Logging; + +namespace DotNetAstGen +{ + public class PDBGenerator + { + public static ILoggerFactory? LoggerFactory; + private static ILogger? _logger; + + private static DecompilerSettings GetSettings(PEFile module) + { + return new DecompilerSettings(ICSharpCode.Decompiler.CSharp.LanguageVersion.Latest) + { + ThrowOnAssemblyResolveErrors = false, + RemoveDeadCode = false, + RemoveDeadStores = false, + UseSdkStyleProjectFormat = WholeProjectDecompiler.CanUseSdkStyleProjectFormat(module), + UseNestedDirectoriesForNamespaces = false, + }; + } + private static CSharpDecompiler GetDecompiler(string assemblyFileName) + { + var module = new PEFile(assemblyFileName); + var resolver = new UniversalAssemblyResolver(assemblyFileName, false, module.Metadata.DetectTargetFrameworkId()); + return new CSharpDecompiler(assemblyFileName, resolver, GetSettings(module)); + } + public void GeneratePDBforDLLFile(string dllFileName, string pdbFileName) + { + using ILoggerFactory factory = Microsoft.Extensions.Logging.LoggerFactory.Create(builder => builder.AddConsole()); + _logger = factory.CreateLogger(); + + var module = new PEFile(dllFileName, + new FileStream(dllFileName, FileMode.Open, FileAccess.Read), + PEStreamOptions.PrefetchEntireImage, + metadataOptions: MetadataReaderOptions.None); + + if (!PDBWriter.HasCodeViewDebugDirectoryEntry(module)) + { + _logger?.LogWarning($"Cannot create PDB file for {dllFileName}, because it does not contain a PE Debug Directory Entry of type 'CodeView'. Skipping..."); + } + else + { + using (FileStream stream = new FileStream(pdbFileName, FileMode.OpenOrCreate, FileAccess.Write)) + { + var decompiler = GetDecompiler(dllFileName); + PDBWriter.WritePdb(module, decompiler, GetSettings(module), stream); + } + } + } + } +} \ No newline at end of file diff --git a/DotNetAstGen/PDBWriter.cs b/DotNetAstGen/PDBWriter.cs new file mode 100644 index 0000000..4f8ee25 --- /dev/null +++ b/DotNetAstGen/PDBWriter.cs @@ -0,0 +1,642 @@ +// PDBWriter.cs +// +// This is created to allow PDB generation functionality via a self-contained DotNetAstGen binary. Original PDB +// generation tool ilspycmd from which this is built required presence of dependent DLLs and was installed as a +// dotnet tool. Derived from ILSpy Code at following locations: +// -- https://github.com/icsharpcode/ILSpy/blob/release/8.1/ICSharpCode.Decompiler/DebugInfo/DebugInfoGenerator.cs +// -- https://github.com/icsharpcode/ILSpy/blob/release/8.1/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs +// +// Original Copyright notice follows: + +// Copyright (c) 2018 Siegfried Pammer +// +// 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. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; +using System.Runtime; +using System.Security.Cryptography; +using System.Text; +using System.Threading; + +using ICSharpCode.Decompiler.CSharp; +using ICSharpCode.Decompiler.CSharp.OutputVisitor; +using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; +using ICSharpCode.Decompiler.CSharp.Syntax; +using ICSharpCode.Decompiler.CSharp.Transforms; +using ICSharpCode.Decompiler.IL; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.Util; +using ICSharpCode.Decompiler; + +using ICSharpCode.Decompiler.DebugInfo; +using ICSharpCode.Decompiler.Disassembler; +using ICSharpCode.Decompiler.Solution; +using ICSharpCode.ILSpyX.PdbProvider; + +namespace DotNetAstGen +{ + class DebugInfoGenerator : DepthFirstAstVisitor + { + static readonly KeyComparer ILVariableKeyComparer = new KeyComparer(l => l.Index.Value, Comparer.Default, EqualityComparer.Default); + + IDecompilerTypeSystem typeSystem; + readonly ImportScopeInfo globalImportScope = new ImportScopeInfo(); + ImportScopeInfo currentImportScope; + List importScopes = new List(); + internal List<(MethodDefinitionHandle Method, ImportScopeInfo Import, int Offset, int Length, HashSet Locals)> LocalScopes { get; } = new List<(MethodDefinitionHandle Method, ImportScopeInfo Import, int Offset, int Length, HashSet Locals)>(); + List functions = new List(); + + public IReadOnlyList Functions + { + get => functions; + } + + public DebugInfoGenerator(IDecompilerTypeSystem typeSystem) + { + this.typeSystem = typeSystem ?? throw new ArgumentNullException(nameof(typeSystem)); + this.currentImportScope = globalImportScope; + } + + public void GenerateImportScopes(MetadataBuilder metadata, ImportScopeHandle globalImportScope) + { + foreach (var scope in importScopes) + { + var blob = EncodeImports(metadata, scope); + scope.Handle = metadata.AddImportScope(scope.Parent == null ? globalImportScope : scope.Parent.Handle, blob); + } + } + + static BlobHandle EncodeImports(MetadataBuilder metadata, ImportScopeInfo scope) + { + var writer = new BlobBuilder(); + + foreach (var import in scope.Imports) + { + writer.WriteByte((byte)ImportDefinitionKind.ImportNamespace); + writer.WriteCompressedInteger(MetadataTokens.GetHeapOffset(metadata.GetOrAddBlobUTF8(import))); + } + + return metadata.GetOrAddBlob(writer); + } + + public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) + { + var parentImportScope = currentImportScope; + currentImportScope = new ImportScopeInfo(parentImportScope); + importScopes.Add(currentImportScope); + base.VisitNamespaceDeclaration(namespaceDeclaration); + currentImportScope = parentImportScope; + } + + public override void VisitUsingDeclaration(UsingDeclaration usingDeclaration) + { + currentImportScope.Imports.Add(usingDeclaration.Namespace); + } + + public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) + { + HandleMethod(methodDeclaration); + } + + public override void VisitAccessor(Accessor accessor) + { + HandleMethod(accessor); + } + + public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) + { + HandleMethod(constructorDeclaration); + } + + public override void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) + { + HandleMethod(destructorDeclaration); + } + + public override void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) + { + HandleMethod(operatorDeclaration); + } + + public override void VisitLambdaExpression(LambdaExpression lambdaExpression) + { + HandleMethod(lambdaExpression); + } + + public override void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) + { + HandleMethod(anonymousMethodExpression); + } + + public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) + { + if (!propertyDeclaration.ExpressionBody.IsNull) + { + HandleMethod(propertyDeclaration.ExpressionBody, propertyDeclaration.Annotation()); + } + else + { + base.VisitPropertyDeclaration(propertyDeclaration); + } + } + + public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) + { + if (!indexerDeclaration.ExpressionBody.IsNull) + { + HandleMethod(indexerDeclaration.ExpressionBody, indexerDeclaration.Annotation()); + } + else + { + base.VisitIndexerDeclaration(indexerDeclaration); + } + } + + public override void VisitQueryFromClause(QueryFromClause queryFromClause) + { + if (queryFromClause.Parent.FirstChild != queryFromClause) + { + HandleMethod(queryFromClause); + } + else + { + base.VisitQueryFromClause(queryFromClause); + } + } + + public override void VisitQueryGroupClause(QueryGroupClause queryGroupClause) + { + var annotation = queryGroupClause.Annotation(); + if (annotation == null) + { + base.VisitQueryGroupClause(queryGroupClause); + return; + } + HandleMethod(queryGroupClause.Projection, annotation.ProjectionLambda); + HandleMethod(queryGroupClause.Key, annotation.KeyLambda); + } + + public override void VisitQueryJoinClause(QueryJoinClause queryJoinClause) + { + var annotation = queryJoinClause.Annotation(); + if (annotation == null) + { + base.VisitQueryJoinClause(queryJoinClause); + return; + } + HandleMethod(queryJoinClause.OnExpression, annotation.OnLambda); + HandleMethod(queryJoinClause.EqualsExpression, annotation.EqualsLambda); + } + + public override void VisitQueryLetClause(QueryLetClause queryLetClause) + { + HandleMethod(queryLetClause); + } + + public override void VisitQueryOrdering(QueryOrdering queryOrdering) + { + HandleMethod(queryOrdering); + } + + public override void VisitQuerySelectClause(QuerySelectClause querySelectClause) + { + HandleMethod(querySelectClause); + } + + public override void VisitQueryWhereClause(QueryWhereClause queryWhereClause) + { + HandleMethod(queryWhereClause); + } + + void HandleMethod(AstNode node) + { + HandleMethod(node, node.Annotation()); + } + + void HandleMethod(AstNode node, ILFunction function) + { + // Look into method body, e.g. in order to find lambdas + VisitChildren(node); + + if (function == null || function.Method == null || function.Method.MetadataToken.IsNil) + return; + this.functions.Add(function); + var method = function.MoveNextMethod ?? function.Method; + MethodDefinitionHandle handle = (MethodDefinitionHandle)method.MetadataToken; + var file = typeSystem.MainModule.PEFile; + MethodDefinition md = file.Metadata.GetMethodDefinition(handle); + if (md.HasBody()) + { + HandleMethodBody(function, file.Reader.GetMethodBody(md.RelativeVirtualAddress)); + } + } + + void HandleMethodBody(ILFunction function, MethodBodyBlock methodBody) + { + var method = function.MoveNextMethod ?? function.Method; + + // we don't really need variables, but need to add empty set + var localVariables = new HashSet(ILVariableKeyComparer); + + LocalScopes.Add(((MethodDefinitionHandle)method!.MetadataToken, currentImportScope, + 0, methodBody.GetCodeSize(), localVariables)); + } + } + + class ImportScopeInfo + { + public readonly ImportScopeInfo? Parent; + public ImportScopeHandle Handle; + public readonly HashSet Imports = new HashSet(); + + public ImportScopeInfo() + { + Parent = null; + } + + public ImportScopeInfo(ImportScopeInfo parent) + { + Parent = parent; + } + } + + public class PDBWriter + { + internal static readonly HashSet attributeNames = new HashSet() { + "System.Runtime.CompilerServices.IsReadOnlyAttribute", + "System.Runtime.CompilerServices.IsByRefLikeAttribute", + "System.Runtime.CompilerServices.IsUnmanagedAttribute", + "System.Runtime.CompilerServices.NullableAttribute", + "System.Runtime.CompilerServices.NullableContextAttribute", + "System.Runtime.CompilerServices.NativeIntegerAttribute", + "System.Runtime.CompilerServices.RefSafetyRulesAttribute", + "System.Runtime.CompilerServices.ScopedRefAttribute", + "Microsoft.CodeAnalysis.EmbeddedAttribute", + }; + + public static bool HasCodeViewDebugDirectoryEntry(PEFile file) + { + return file.Reader.ReadDebugDirectory().Any(entry => entry.Type == DebugDirectoryEntryType.CodeView); + } + + private static bool IncludeTypeWhenGeneratingPdb(PEFile module, TypeDefinitionHandle type, DecompilerSettings settings) + { + var metadata = module.Metadata; + var typeDef = metadata.GetTypeDefinition(type); + string name = metadata.GetString(typeDef.Name); + string ns = metadata.GetString(typeDef.Namespace); + if (name == "" || CSharpDecompiler.MemberIsHidden(module, type, settings)) + return false; + if (ns == "XamlGeneratedNamespace" && name == "GeneratedInternalTypeHelper") + return false; + if (!typeDef.IsNested && attributeNames.Contains(ns + "." + name)) + return false; + return true; + } + + public static void WritePdb( + PEFile file, + CSharpDecompiler decompiler, + DecompilerSettings settings, + Stream targetStream, + BlobContentId? pdbId = null, + IProgress? progress = null) + { + MetadataBuilder metadata = new MetadataBuilder(); + MetadataReader reader = file.Metadata; + var entrypointHandle = MetadataTokens.MethodDefinitionHandle(file!.Reader.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress); + + var sequencePointBlobs = new Dictionary(); + var emptyList = new List(); + var localScopes = new List<(MethodDefinitionHandle Method, ImportScopeInfo Import, int Offset, int Length, HashSet Locals)>(); + var stateMachineMethods = new List<(MethodDefinitionHandle MoveNextMethod, MethodDefinitionHandle KickoffMethod)>(); + var customDebugInfo = new List<(EntityHandle Parent, GuidHandle Guid, BlobHandle Blob)>(); + var customMethodDebugInfo = new List<(MethodDefinitionHandle Parent, GuidHandle Guid, BlobHandle Blob)>(); + var globalImportScope = metadata.AddImportScope(default, default); + + string BuildFileNameFromTypeName(TypeDefinitionHandle handle) + { + var typeName = handle.GetFullTypeName(reader).TopLevelTypeName; + string ns = settings.UseNestedDirectoriesForNamespaces + ? WholeProjectDecompiler.CleanUpPath(typeName.Namespace) + : WholeProjectDecompiler.CleanUpDirectoryName(typeName.Namespace); + return Path.Combine(ns, WholeProjectDecompiler.CleanUpFileName(typeName.Name) + ".cs"); + } + + var sourceFiles = reader.GetTopLevelTypeDefinitions().Where(t => IncludeTypeWhenGeneratingPdb(file, t, settings)).GroupBy(BuildFileNameFromTypeName).ToList(); + DecompilationProgress currentProgress = new() + { + TotalUnits = sourceFiles.Count, + UnitsCompleted = 0, + Title = "Generating portable PDB..." + }; + + foreach (var sourceFile in sourceFiles) + { + // Generate syntax tree + var syntaxTree = decompiler.DecompileTypes(sourceFile); + + if (progress != null) + { + currentProgress.UnitsCompleted++; + progress.Report(currentProgress); + } + + if (!syntaxTree.HasChildren) + continue; + + // Generate source and checksum + syntaxTree.InsertChildAfter(null, new Comment(" PDB and source generated by ICSharpCode.Decompiler"), Roles.Comment); + var sourceText = SyntaxTreeToString(syntaxTree, settings); + + // Generate sequence points for the syntax tree + var sequencePoints = decompiler.CreateSequencePoints(syntaxTree); + if (sequencePoints == null) + { + continue; + } + + // Generate other debug information + var debugInfoGen = new DebugInfoGenerator(decompiler.TypeSystem); + syntaxTree.AcceptVisitor(debugInfoGen); + + lock (metadata) + { + var sourceBlob = WriteSourceToBlob(metadata, sourceText, out var sourceCheckSum); + var name = metadata.GetOrAddDocumentName(sourceFile.Key); + + // Create Document(Handle) + var document = metadata.AddDocument(name, + hashAlgorithm: metadata.GetOrAddGuid(KnownGuids.HashAlgorithmSHA256), + hash: metadata.GetOrAddBlob(sourceCheckSum), + language: metadata.GetOrAddGuid(KnownGuids.CSharpLanguageGuid)); + + // Add embedded source to the PDB + customDebugInfo.Add((document, + metadata.GetOrAddGuid(KnownGuids.EmbeddedSource), + sourceBlob)); + + debugInfoGen.GenerateImportScopes(metadata, globalImportScope); + + localScopes.AddRange(debugInfoGen.LocalScopes); + + foreach (var function in debugInfoGen.Functions) + { + var method = function.MoveNextMethod ?? function.Method; + var methodHandle = (MethodDefinitionHandle)method!.MetadataToken; + sequencePoints.TryGetValue(function, out var points); + ProcessMethod(methodHandle, document, points, syntaxTree); + if (function.MoveNextMethod != null) + { + stateMachineMethods.Add(( + (MethodDefinitionHandle)function.MoveNextMethod.MetadataToken, + (MethodDefinitionHandle)function.Method!.MetadataToken + )); + customDebugInfo.Add(( + function.MoveNextMethod.MetadataToken, + metadata.GetOrAddGuid(KnownGuids.StateMachineHoistedLocalScopes), + metadata.GetOrAddBlob(BuildStateMachineHoistedLocalScopes(function)) + )); + } + if (function.IsAsync) + { + customMethodDebugInfo.Add((methodHandle, + metadata.GetOrAddGuid(KnownGuids.MethodSteppingInformation), + metadata.GetOrAddBlob(function.AsyncDebugInfo.BuildBlob(methodHandle)))); + } + } + } + } + + foreach (var method in reader.MethodDefinitions) + { + var md = reader.GetMethodDefinition(method); + + if (sequencePointBlobs.TryGetValue(method, out var info)) + { + metadata.AddMethodDebugInformation(info.Document, info.SequencePoints); + } + else + { + metadata.AddMethodDebugInformation(default, default); + } + } + + localScopes.Sort((x, y) => + { + if (x.Method != y.Method) + { + return MetadataTokens.GetRowNumber(x.Method) - MetadataTokens.GetRowNumber(y.Method); + } + if (x.Offset != y.Offset) + { + return x.Offset - y.Offset; + } + return y.Length - x.Length; + }); + foreach (var localScope in localScopes) + { + int nextRow = metadata.GetRowCount(TableIndex.LocalVariable) + 1; + var firstLocalVariable = MetadataTokens.LocalVariableHandle(nextRow); + + foreach (var local in localScope.Locals.OrderBy(l => l.Index)) + { + var localVarName = local.Name != null ? metadata.GetOrAddString(local.Name) : default; + metadata.AddLocalVariable(LocalVariableAttributes.None, local.Index!.Value, localVarName); + } + + metadata.AddLocalScope(localScope.Method, localScope.Import.Handle, firstLocalVariable, + default, localScope.Offset, localScope.Length); + } + + stateMachineMethods.SortBy(row => MetadataTokens.GetRowNumber(row.MoveNextMethod)); + foreach (var row in stateMachineMethods) + { + metadata.AddStateMachineMethod(row.MoveNextMethod, row.KickoffMethod); + } + customMethodDebugInfo.SortBy(row => MetadataTokens.GetRowNumber(row.Parent)); + foreach (var row in customMethodDebugInfo) + { + metadata.AddCustomDebugInformation(row.Parent, row.Guid, row.Blob); + } + customDebugInfo.SortBy(row => MetadataTokens.GetRowNumber(row.Parent)); + foreach (var row in customDebugInfo) + { + metadata.AddCustomDebugInformation(row.Parent, row.Guid, row.Blob); + } + + if (pdbId == null) + { + var debugDir = file.Reader.ReadDebugDirectory().FirstOrDefault(dir => dir.Type == DebugDirectoryEntryType.CodeView); + var portable = file.Reader.ReadCodeViewDebugDirectoryData(debugDir); + pdbId = new BlobContentId(portable.Guid, debugDir.Stamp); + } + + PortablePdbBuilder serializer = new PortablePdbBuilder(metadata, GetRowCounts(reader), entrypointHandle, blobs => pdbId.Value); + BlobBuilder blobBuilder = new BlobBuilder(); + serializer.Serialize(blobBuilder); + blobBuilder.WriteContentTo(targetStream); + + void ProcessMethod(MethodDefinitionHandle method, DocumentHandle document, + List? sequencePoints, SyntaxTree syntaxTree) + { + var methodDef = reader.GetMethodDefinition(method); + int localSignatureRowId; + MethodBodyBlock methodBody; + if (methodDef.RelativeVirtualAddress != 0) + { + methodBody = file.Reader.GetMethodBody(methodDef.RelativeVirtualAddress); + localSignatureRowId = methodBody.LocalSignature.IsNil ? 0 : MetadataTokens.GetRowNumber(methodBody.LocalSignature); + } + else + { + localSignatureRowId = 0; + } + + // Check if sequence points were already processed - ILFunction gets defined in C# twice: + // This may happen if a compiler-generated function gets transformed into a lambda expression, + // but its method definition is not removed from the syntax tree. + if (!sequencePointBlobs.ContainsKey(method)) + { + if (sequencePoints?.Count > 0) + sequencePointBlobs.Add(method, (document, EncodeSequencePoints(metadata, localSignatureRowId, sequencePoints))); + else + sequencePointBlobs.Add(method, (default, default)); + } + else + { + Debug.Assert(false, "Duplicate sequence point definition detected: " + MetadataTokens.GetToken(method).ToString("X8")); + } + } + } + + static BlobBuilder BuildStateMachineHoistedLocalScopes(ILFunction function) + { + var builder = new BlobBuilder(); + foreach (var variable in function.Variables.Where(v => v.StateMachineField != null).OrderBy(v => MetadataTokens.GetRowNumber(v.StateMachineField.MetadataToken))) + { + builder.WriteUInt32(0); + builder.WriteUInt32((uint)function.CodeSize); + } + return builder; + } + + static BlobHandle WriteSourceToBlob(MetadataBuilder metadata, string sourceText, out byte[] sourceCheckSum) + { + var builder = new BlobBuilder(); + using (var memory = new MemoryStream()) + { + var deflate = new DeflateStream(memory, CompressionLevel.Optimal, leaveOpen: true); + byte[] bytes = Encoding.UTF8.GetBytes(sourceText); + deflate.Write(bytes, 0, bytes.Length); + deflate.Close(); + byte[] buffer = memory.ToArray(); + builder.WriteInt32(bytes.Length); // compressed + builder.WriteBytes(buffer); + using (var hasher = SHA256.Create()) + { + sourceCheckSum = hasher.ComputeHash(bytes); + } + } + + return metadata.GetOrAddBlob(builder); + } + + static BlobHandle EncodeSequencePoints(MetadataBuilder metadata, int localSignatureRowId, List sequencePoints) + { + if (sequencePoints.Count == 0) + return default; + var writer = new BlobBuilder(); + // header: + writer.WriteCompressedInteger(localSignatureRowId); + + int previousOffset = -1; + int previousStartLine = -1; + int previousStartColumn = -1; + + for (int i = 0; i < sequencePoints.Count; i++) + { + var sequencePoint = sequencePoints[i]; + // delta IL offset: + if (i > 0) + writer.WriteCompressedInteger(sequencePoint.Offset - previousOffset); + else + writer.WriteCompressedInteger(sequencePoint.Offset); + previousOffset = sequencePoint.Offset; + + if (sequencePoint.IsHidden) + { + writer.WriteInt16(0); + continue; + } + + int lineDelta = sequencePoint.EndLine - sequencePoint.StartLine; + int columnDelta = sequencePoint.EndColumn - sequencePoint.StartColumn; + + writer.WriteCompressedInteger(lineDelta); + + if (lineDelta == 0) + { + writer.WriteCompressedInteger(columnDelta); + } + else + { + writer.WriteCompressedSignedInteger(columnDelta); + } + + if (previousStartLine < 0) + { + writer.WriteCompressedInteger(sequencePoint.StartLine); + writer.WriteCompressedInteger(sequencePoint.StartColumn); + } + else + { + writer.WriteCompressedSignedInteger(sequencePoint.StartLine - previousStartLine); + writer.WriteCompressedSignedInteger(sequencePoint.StartColumn - previousStartColumn); + } + + previousStartLine = sequencePoint.StartLine; + previousStartColumn = sequencePoint.StartColumn; + } + + return metadata.GetOrAddBlob(writer); + } + + static ImmutableArray GetRowCounts(MetadataReader reader) + { + var builder = ImmutableArray.CreateBuilder(MetadataTokens.TableCount); + for (int i = 0; i < MetadataTokens.TableCount; i++) + { + builder.Add(reader.GetTableRowCount((TableIndex)i)); + } + + return builder.MoveToImmutable(); + } + + static string SyntaxTreeToString(SyntaxTree syntaxTree, DecompilerSettings settings) + { + StringWriter w = new StringWriter(); + TokenWriter tokenWriter = new TextWriterTokenWriter(w); + tokenWriter = TokenWriter.WrapInWriterThatSetsLocationsInAST(tokenWriter); + syntaxTree.AcceptVisitor(new CSharpOutputVisitor(tokenWriter, settings.CSharpFormattingOptions)); + return w.ToString(); + } + } +} \ No newline at end of file diff --git a/DotNetAstGen/Program.cs b/DotNetAstGen/Program.cs index 0f32515..098ad4b 100644 --- a/DotNetAstGen/Program.cs +++ b/DotNetAstGen/Program.cs @@ -227,10 +227,19 @@ static void ProcessDll(FileInfo dllFile, string jsonPath) var dllPath = dllFile.FullName; var pdbPath = Path.Combine(dllFile.DirectoryName ?? "./", $"{Path.GetFileNameWithoutExtension(dllPath)}.pdb"); + + // check if PDB exists if (!File.Exists(pdbPath)) { - _logger?.LogWarning("{}.dll does not have an accompanying PDB file, skipping...", - Path.GetFileNameWithoutExtension(dllPath)); + _logger?.LogInformation("PDB not found! trying to generate PDB locally for: {filePath}", dllPath); + PDBGenerator pdbgen = new PDBGenerator(); + pdbgen.GeneratePDBforDLLFile(dllPath, pdbPath); + } + + // check again + if (!File.Exists(pdbPath)) { + _logger?.LogWarning("{}.dll does not have an accompanying PDB file, skipping...", + Path.GetFileNameWithoutExtension(dllPath)); } else {