Browse Source

Compare PDB sequence points against the compiler's breakpoint map

The PdbGen tests compared the reconstructed PDB to the C# compiler's
byte-for-byte, so any non-trivial method failed on reconstructed IL
ranges, hidden sequence points and local scopes - none of which the
decompiler can reproduce exactly. That left four of seven fixtures
[Ignore]d and the suite with almost no coverage.

Compare only what a debugging user actually feels: the visible (non-hidden)
breakpoint map, parsed straight from the sequence-point blobs and keyed by
method-definition row (shared between the PDB and the PE it describes). IL
offsets, hidden points, local scopes and the embedded source are dropped.
The compiler's own PDB is the oracle, so the tests assert correct debugging
behavior rather than the decompiler's past output. Methods where the
decompiler legitimately diverges pin an auto-derived residual snapshot, the
same accept-the-diff workflow as the pretty tests; a separate oracle-free
check rejects duplicate or overlapping sequence points.

Un-ignores ForLoopTests, LambdaCapturing and Members (its source is
regenerated to match the decompiler's per-type output, collapsing ~50 lines
of indentation-induced coordinate noise to two genuine differences).

Assisted-by: Claude:claude-opus-4-8:Claude Code
pull/3823/head
Siegfried Pammer 3 weeks ago committed by Siegfried Pammer
parent
commit
182fb4e814
  1. 22
      ICSharpCode.Decompiler.Tests/CLAUDE.md
  2. 198
      ICSharpCode.Decompiler.Tests/Helpers/PdbSequencePoints.cs
  3. 148
      ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs
  4. 1
      ICSharpCode.Decompiler.Tests/TestCases/PdbGen/.gitignore
  5. 41
      ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml
  6. 16
      ICSharpCode.Decompiler.Tests/TestCases/PdbGen/HelloWorld.xml
  7. 2
      ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.residual.txt
  8. 38
      ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.xml
  9. 6
      ICSharpCode.Decompiler.Tests/TestCases/PdbGen/Members.residual.txt
  10. 111
      ICSharpCode.Decompiler.Tests/TestCases/PdbGen/Members.xml

22
ICSharpCode.Decompiler.Tests/CLAUDE.md

@ -42,7 +42,7 @@ root `CLAUDE.md` section on the submodule). @@ -42,7 +42,7 @@ root `CLAUDE.md` section on the submodule).
| Ugly | `UglyTestRunner` / `Ugly/*.cs` | compile -> decompile with sugar settings disabled | sibling `.Expected.cs` file |
| Disassembler | `DisassemblerPrettyTestRunner` / `Disassembler/Pretty/*.il` | ilasm -> disassemble with our `ReflectionDisassembler` | the `.il` source (or `.expected.il`, e.g. `SortedOutput`) |
| VBPretty | `VBPrettyTestRunner` / `VBPretty/*.vb` | vbc -> decompile to C# | sibling `.cs` file |
| PdbGen | `PdbGenerationTestRunner` / `PdbGen/*.xml` | in-proc Roslyn compile, generate portable PDB with `PortablePdbWriter`, dump both PDBs with `PdbToXmlConverter` | `.expected.xml` |
| PdbGen | `PdbGenerationTestRunner` / `PdbGen/*.xml` | in-proc Roslyn compile (real PDB = oracle), decompile, generate portable PDB with `PortablePdbWriter`, parse both PDBs' sequence-point blobs | the compiler's PDB, projected to the visible breakpoint map (see below) |
| Roundtrip | `RoundtripAssembly` (inputs from `ILSpy-tests/`) | whole-project decompile -> MSBuild rebuild -> run original NUnit tests against the rebuilt assembly | test-run success |
| Unit tests | `TypeSystem/`, `Semantics/`, `Output/`, `Util/`, `DataFlowTest`, `Metadata/`, `ProjectDecompiler/` | plain in-process NUnit | assertions |
@ -69,8 +69,24 @@ helper, which picks the file via `[CallerMemberName]`. @@ -69,8 +69,24 @@ helper, which picks the file via `[CallerMemberName]`.
- **ILPretty / Disassembler**: add a `.il` file (assembled with the NuGet ilasm) and the
expected `.cs` (`ILPretty`) or rely on round-tripping the `.il` itself (`Disassembler`).
- **VBPretty**: add `MyTest.vb` and the expected C# decompilation `MyTest.cs`.
- **PdbGen**: add `MyTest.xml` containing the source files inside `<file name="...">` elements;
the expected PDB dump lives in `MyTest.expected.xml`.
- **PdbGen** (the reconstructed PDB's breakpoints match the C# compiler's): add `MyTest.xml`
containing the source inside `<file name="...">` elements, written exactly as ILSpy
pretty-prints a *single type* (one document per type, no assembly-attribute header - the same
discipline as Pretty). The runner compiles it with Roslyn (whose PDB is the oracle), decompiles,
reconstructs a PDB, and compares only the **visible breakpoint map**: per method, the ordered
source locations of the non-hidden sequence points. IL offsets, hidden sequence points, local
scopes and the embedded source are all dropped, because the decompiler reconstructs them
differently and they never match byte-for-byte. The comparer also runs an oracle-free
well-formedness check (strictly increasing IL offsets = no duplicate/overlapping points).
`TestSequencePoints()` asserts the map matches the compiler exactly; for the handful of methods
where the decompiler legitimately diverges (e.g. it breakpoints a method's opening brace where
the compiler keeps it hidden), call `TestSequencePoints(knownResidual: true)`; the residual is
auto-derived and committed as `MyTest.residual.txt`, so improvements and regressions both flip
the test like a pretty diff. On a mismatch the test writes `MyTest.residual.txt.generated` and
fails; accept a deliberate change by re-running with `ILSPY_ACCEPT_PDB_RESIDUAL=1` set (which
overwrites the snapshot in place) or by copying the `.generated` file over it. `Tolerance.Lines` drops column comparison
for statements whose column placement differs. Nothing is hand-maintained except the `.cs`
source and, rarely, the residual snapshot. No `.expected.*` is committed (all regenerated).
## Conditional expectations (#if) and comparison rules

198
ICSharpCode.Decompiler.Tests/Helpers/PdbSequencePoints.cs

@ -0,0 +1,198 @@ @@ -0,0 +1,198 @@
// Copyright (c) 2026 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.
//
// 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.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Text;
namespace ICSharpCode.Decompiler.Tests.Helpers
{
/// <summary>
/// Reads the sequence points out of a portable PDB and compares two PDBs (typically the one
/// the original C# compiler produced versus the one <c>PortablePdbWriter</c> reconstructs)
/// at the granularity that actually matters to a debugging user: the breakpoint map.
/// </summary>
/// <remarks>
/// The decompiler reconstructs IL ranges, hidden sequence points and local scopes from the
/// ILAst, so those never match the original compiler byte-for-byte. What it can and should
/// get right is <em>where the visible (non-hidden) breakpoints land in the source</em>.
/// The comparison therefore projects each PDB onto the ordered list of visible sequence-point
/// source locations per method (keyed by method-definition row, which is shared between the
/// PDB and the PE it describes) and reports the residual difference.
/// </remarks>
static class PdbSequencePoints
{
internal readonly record struct RawSequencePoint(int Offset, bool IsHidden,
int StartLine, int StartColumn, int EndLine, int EndColumn)
{
/// <summary>Visible source location, formatted with or without columns.</summary>
public string Format(bool includeColumns)
{
return includeColumns
? $"({StartLine},{StartColumn})-({EndLine},{EndColumn})"
: $"L{StartLine}-{EndLine}";
}
}
/// <summary>
/// Reads every method's sequence points (hidden ones included, in document order) from a
/// portable PDB, keyed by the row number of the owning method definition.
/// </summary>
public static Dictionary<int, List<RawSequencePoint>> Read(Stream portablePdb)
{
portablePdb.Position = 0;
using var provider = MetadataReaderProvider.FromPortablePdbStream(portablePdb, MetadataStreamOptions.LeaveOpen);
var reader = provider.GetMetadataReader();
var result = new Dictionary<int, List<RawSequencePoint>>();
foreach (var handle in reader.MethodDebugInformation)
{
var info = reader.GetMethodDebugInformation(handle);
if (info.SequencePointsBlob.IsNil)
continue;
var list = new List<RawSequencePoint>();
foreach (var sp in info.GetSequencePoints())
{
list.Add(new RawSequencePoint(sp.Offset, sp.IsHidden,
sp.StartLine, sp.StartColumn, sp.EndLine, sp.EndColumn));
}
if (list.Count > 0)
{
// A MethodDebugInformation row is parallel to its MethodDefinition row.
result[MetadataTokens.GetRowNumber(handle)] = list;
}
}
return result;
}
/// <summary>
/// Resolves a readable "Namespace.Type.Method" label for each method-definition row,
/// for use in comparison failure messages.
/// </summary>
public static Dictionary<int, string> ReadMethodNames(string peFileName)
{
using var peStream = File.OpenRead(peFileName);
using var peReader = new PEReader(peStream);
var md = peReader.GetMetadataReader();
var names = new Dictionary<int, string>();
foreach (var handle in md.MethodDefinitions)
{
var method = md.GetMethodDefinition(handle);
var type = md.GetTypeDefinition(method.GetDeclaringType());
string typeName = md.GetString(type.Name);
if (!type.Namespace.IsNil && md.GetString(type.Namespace) is { Length: > 0 } ns)
typeName = ns + "." + typeName;
names[MetadataTokens.GetRowNumber(handle)] = typeName + "." + md.GetString(method.Name);
}
return names;
}
/// <summary>
/// Compares the visible breakpoint maps of two PDBs and returns the residual difference as
/// a deterministic, human-readable report. An empty string means the maps are identical.
/// </summary>
/// <param name="expected">PDB used as the oracle (the original compiler's PDB).</param>
/// <param name="actual">PDB under test (the decompiler's reconstruction).</param>
/// <param name="methodNames">Row-to-label map from <see cref="ReadMethodNames"/>.</param>
/// <param name="includeColumns">
/// When true, breakpoints must match on line <em>and</em> column; when false, only the
/// line span is compared (use for methods where the decompiler's column placement within a
/// statement legitimately differs).
/// </param>
public static string CompareBreakpointMaps(
Dictionary<int, List<RawSequencePoint>> expected,
Dictionary<int, List<RawSequencePoint>> actual,
Dictionary<int, string> methodNames,
bool includeColumns)
{
var report = new StringBuilder();
foreach (int row in expected.Keys.Union(actual.Keys).OrderBy(r => r))
{
var expectedPoints = Visible(expected, row, includeColumns);
var actualPoints = Visible(actual, row, includeColumns);
var expectedOnly = MultisetDifference(expectedPoints, actualPoints);
var actualOnly = MultisetDifference(actualPoints, expectedPoints);
if (expectedOnly.Count == 0 && actualOnly.Count == 0)
continue;
methodNames.TryGetValue(row, out var name);
report.Append($"0x{0x06000000 | row:x8} {name}").Append('\n');
foreach (var p in expectedOnly.OrderBy(p => p))
report.Append(" - compiler only: ").Append(p).Append('\n');
foreach (var p in actualOnly.OrderBy(p => p))
report.Append(" + decompiler only: ").Append(p).Append('\n');
}
return report.ToString();
}
static List<string> Visible(Dictionary<int, List<RawSequencePoint>> map, int row, bool includeColumns)
{
if (!map.TryGetValue(row, out var list))
return new List<string>();
return list.Where(sp => !sp.IsHidden).Select(sp => sp.Format(includeColumns)).ToList();
}
static List<string> MultisetDifference(List<string> a, List<string> b)
{
var counts = new Dictionary<string, int>();
foreach (var x in b)
counts[x] = counts.GetValueOrDefault(x) + 1;
var result = new List<string>();
foreach (var x in a)
{
if (counts.TryGetValue(x, out int c) && c > 0)
counts[x] = c - 1;
else
result.Add(x);
}
return result;
}
/// <summary>
/// Checks that the decompiler's own PDB is structurally well-formed, independent of any
/// oracle: within each method the sequence points' IL offsets must be strictly increasing,
/// i.e. no duplicate or overlapping sequence points (a duplicate is exactly the failure
/// that aborts <c>PortablePdbWriter</c> in debug builds). Returns the offending methods, or
/// an empty string when well-formed.
/// </summary>
public static string CheckWellFormed(
Dictionary<int, List<RawSequencePoint>> pdb,
Dictionary<int, string> methodNames)
{
var report = new StringBuilder();
foreach (int row in pdb.Keys.OrderBy(r => r))
{
var list = pdb[row];
for (int i = 1; i < list.Count; i++)
{
if (list[i].Offset <= list[i - 1].Offset)
{
methodNames.TryGetValue(row, out var name);
report.Append($"0x{0x06000000 | row:x8} {name}: non-increasing IL offset 0x{list[i].Offset:x} after 0x{list[i - 1].Offset:x}").Append('\n');
break;
}
}
}
return report.ToString();
}
}
}

148
ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs

@ -5,7 +5,6 @@ using System.Linq; @@ -5,7 +5,6 @@ using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
using System.Text;
using System.Xml.Linq;
using ICSharpCode.Decompiler.CSharp;
@ -13,8 +12,6 @@ using ICSharpCode.Decompiler.DebugInfo; @@ -13,8 +12,6 @@ using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Tests.Helpers;
using Microsoft.DiaSymReader.Tools;
using NUnit.Framework;
namespace ICSharpCode.Decompiler.Tests
@ -24,31 +21,41 @@ namespace ICSharpCode.Decompiler.Tests @@ -24,31 +21,41 @@ namespace ICSharpCode.Decompiler.Tests
{
static readonly string TestCasePath = Tester.TestCasePath + "/PdbGen";
/// <summary>
/// How strictly a reconstructed breakpoint must match the original compiler's.
/// </summary>
enum Tolerance
{
/// <summary>Visible breakpoints must match on source line and column.</summary>
LinesAndColumns,
/// <summary>Only the source line span must match; column placement may differ.</summary>
Lines
}
[Test]
public void HelloWorld()
{
TestGeneratePdb();
TestSequencePoints();
}
[Test]
[Ignore("Missing nested local scopes for loops, differences in IL ranges")]
public void ForLoopTests()
{
TestGeneratePdb();
TestSequencePoints();
}
[Test]
[Ignore("Differences in IL ranges")]
public void LambdaCapturing()
{
TestGeneratePdb();
// The decompiler emits one extra visible breakpoint that the C# compiler keeps hidden;
// the recorded residual pins that known, deliberate difference.
TestSequencePoints(knownResidual: true);
}
[Test]
[Ignore("Duplicate sequence points for local function")]
public void Members()
{
TestGeneratePdb();
TestSequencePoints(knownResidual: true);
}
[Test]
@ -194,41 +201,78 @@ namespace ICSharpCode.Decompiler.Tests @@ -194,41 +201,78 @@ namespace ICSharpCode.Decompiler.Tests
}
}
private void TestGeneratePdb([CallerMemberName] string testName = null)
/// <summary>
/// Compiles the fixture with the C# compiler (producing a real PDB used as the oracle),
/// decompiles it and reconstructs a PDB with <see cref="PortablePdbWriter"/>, then compares
/// the two PDBs' visible breakpoint maps. The decompiler reconstructs IL ranges, hidden
/// sequence points and local scopes differently from the compiler, so the comparison drops
/// those and asserts only on the source location of each visible (non-hidden) sequence point.
/// </summary>
/// <param name="tolerance">
/// Whether visible breakpoints must match on column as well as line.
/// </param>
/// <param name="knownResidual">
/// When false, the breakpoint map must match the compiler exactly (after the projection
/// above). When true, the residual difference must match the committed
/// <c>&lt;testName&gt;.residual.txt</c> snapshot - this pins the known imperfections the
/// decompiler cannot yet reproduce, so an improvement or a regression both flip the test and
/// prompt a deliberate snapshot update (the same accept-the-diff workflow as the pretty tests).
/// </param>
private void TestSequencePoints(Tolerance tolerance = Tolerance.LinesAndColumns, bool knownResidual = false,
[CallerMemberName] string testName = null)
{
const PdbToXmlOptions options = PdbToXmlOptions.IncludeEmbeddedSources | PdbToXmlOptions.ThrowOnError | PdbToXmlOptions.IncludeTokens | PdbToXmlOptions.ResolveTokens | PdbToXmlOptions.IncludeMethodSpans;
string xmlFile = Path.Combine(TestCasePath, testName + ".xml");
(string peFileName, string pdbFileName) = CompileTestCase(testName);
var moduleDefinition = new PEFile(peFileName);
var resolver = new UniversalAssemblyResolver(peFileName, false, moduleDefinition.Metadata.DetectTargetFrameworkId(), null, PEStreamOptions.PrefetchEntireImage);
var decompiler = new CSharpDecompiler(moduleDefinition, resolver, new DecompilerSettings());
using (FileStream pdbStream = File.Open(Path.Combine(TestCasePath, testName + ".pdb"), FileMode.OpenOrCreate, FileAccess.ReadWrite))
var module = new PEFile(peFileName);
var resolver = new UniversalAssemblyResolver(peFileName, false, module.Metadata.DetectTargetFrameworkId(), null, PEStreamOptions.PrefetchEntireImage);
var decompiler = new CSharpDecompiler(module, resolver, new DecompilerSettings());
using var generatedPdb = new MemoryStream();
new PortablePdbWriter { NoLogo = true }
.WritePdb(module, decompiler, new DecompilerSettings(), generatedPdb);
var methodNames = PdbSequencePoints.ReadMethodNames(peFileName);
var actual = PdbSequencePoints.Read(generatedPdb);
Dictionary<int, List<PdbSequencePoints.RawSequencePoint>> expected;
using (var compilerPdb = File.OpenRead(pdbFileName))
expected = PdbSequencePoints.Read(compilerPdb);
// Oracle-free structural check on the decompiler's own PDB.
string wellFormed = PdbSequencePoints.CheckWellFormed(actual, methodNames);
Assert.That(wellFormed, Is.Empty, "the reconstructed PDB is not well-formed:\n" + wellFormed);
string residual = PdbSequencePoints.CompareBreakpointMaps(
expected, actual, methodNames, includeColumns: tolerance == Tolerance.LinesAndColumns);
if (knownResidual)
{
pdbStream.SetLength(0);
new PortablePdbWriter { NoLogo = true }
.WritePdb(moduleDefinition, decompiler, new DecompilerSettings(), pdbStream);
pdbStream.Position = 0;
using (Stream peStream = File.OpenRead(peFileName))
using (Stream expectedPdbStream = File.OpenRead(pdbFileName))
string snapshotFile = Path.Combine(TestCasePath, testName + ".residual.txt");
string expectedResidual = File.Exists(snapshotFile)
? File.ReadAllText(snapshotFile).Replace("\r\n", "\n")
: "";
if (residual != expectedResidual)
{
using (StreamWriter writer = new StreamWriter(Path.ChangeExtension(pdbFileName, ".xml"), false, Encoding.UTF8))
// Setting ILSPY_ACCEPT_PDB_RESIDUAL=1 accepts the new residual in place, so a
// deliberate change is committed by re-running the test with the variable set
// rather than by hand-copying files.
if (Environment.GetEnvironmentVariable("ILSPY_ACCEPT_PDB_RESIDUAL") == "1")
{
PdbToXmlConverter.ToXml(writer, expectedPdbStream, peStream, options);
File.WriteAllText(snapshotFile, residual);
}
peStream.Position = 0;
using (StreamWriter writer = new StreamWriter(Path.ChangeExtension(xmlFile, ".generated.xml"), false, Encoding.UTF8))
else
{
PdbToXmlConverter.ToXml(writer, pdbStream, peStream, options);
File.WriteAllText(snapshotFile + ".generated", residual);
Assert.Fail($"the breakpoint-map residual changed; review the difference and, if intended, "
+ $"accept it by re-running this test with ILSPY_ACCEPT_PDB_RESIDUAL=1 set (or copy "
+ $"'{testName}.residual.txt.generated' over '{testName}.residual.txt')."
+ $"\n\nExpected:\n{expectedResidual}\nActual:\n{residual}");
}
}
}
string expectedFileName = Path.ChangeExtension(xmlFile, ".expected.xml");
ProcessXmlFile(expectedFileName);
string generatedFileName = Path.ChangeExtension(xmlFile, ".generated.xml");
ProcessXmlFile(generatedFileName);
CodeAssert.AreEqual(Normalize(expectedFileName), Normalize(generatedFileName));
else
{
Assert.That(residual, Is.Empty, "the reconstructed breakpoint map differs from the compiler's:\n" + residual);
}
}
private (string peFileName, string pdbFileName) CompileTestCase(string testName)
@ -244,41 +288,5 @@ namespace ICSharpCode.Decompiler.Tests @@ -244,41 +288,5 @@ namespace ICSharpCode.Decompiler.Tests
return (peFileName, pdbFileName);
}
private void ProcessXmlFile(string fileName)
{
var document = XDocument.Load(fileName);
foreach (var file in document.Descendants("file"))
{
file.Attribute("checksum").Remove();
file.Attribute("embeddedSourceLength")?.Remove();
var name = file.Attribute("name");
if (name != null)
{
// Generated document names use the platform's directory separator;
// the expected files were produced on Windows.
name.Value = name.Value.Replace('/', '\\');
}
file.ReplaceNodes(new XCData(file.Value.Replace("\uFEFF", "")));
}
document.Save(fileName, SaveOptions.None);
}
private string Normalize(string inputFileName)
{
return File.ReadAllText(inputFileName).Replace("\r\n", "\n").Replace("\r", "\n");
}
}
class StringWriterWithEncoding : StringWriter
{
readonly Encoding encoding;
public StringWriterWithEncoding(Encoding encoding)
{
this.encoding = encoding ?? throw new ArgumentNullException("encoding");
}
public override Encoding Encoding => encoding;
}
}

1
ICSharpCode.Decompiler.Tests/TestCases/PdbGen/.gitignore vendored

@ -2,3 +2,4 @@ @@ -2,3 +2,4 @@
/*.pdb
/*.expected.xml
/*.generated.xml
/*.residual.txt.generated

41
ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml

@ -28,45 +28,4 @@ public class ForLoopTests @@ -28,45 +28,4 @@ public class ForLoopTests
}
]]></file>
</files>
<methods>
<method containingType="ICSharpCode.Decompiler.Tests.TestCases.PdbGen.ForLoopTests" name="SimplePrintLoop" parameterNames="args" token="0x6000001">
<sequencePoints>
<entry offset="0x0" startLine="9" startColumn="9" endLine="9" endColumn="18" document="1" />
<entry offset="0x2" hidden="true" document="1" />
<entry offset="0x4" startLine="11" startColumn="5" endLine="11" endColumn="32" document="1" />
<entry offset="0xc" startLine="9" startColumn="37" endLine="9" endColumn="40" document="1" />
<entry offset="0x10" startLine="9" startColumn="20" endLine="9" endColumn="35" document="1" />
<entry offset="0x16" startLine="13" startColumn="3" endLine="13" endColumn="4" document="1" />
</sequencePoints>
<scope startOffset="0x0" endOffset="0x17">
<scope startOffset="0x0" endOffset="0x16">
<local name="i" il_index="0" il_start="0x0" il_end="0x16" attributes="0" />
</scope>
</scope>
</method>
<method containingType="ICSharpCode.Decompiler.Tests.TestCases.PdbGen.ForLoopTests" name="SimplePrintLoopWithCondition" parameterNames="args" token="0x6000002">
<sequencePoints>
<entry offset="0x0" startLine="17" startColumn="9" endLine="17" endColumn="18" document="1" />
<entry offset="0x2" hidden="true" document="1" />
<entry offset="0x4" startLine="19" startColumn="5" endLine="19" endColumn="20" document="1" />
<entry offset="0x9" startLine="21" startColumn="6" endLine="21" endColumn="33" document="1" />
<entry offset="0x11" startLine="17" startColumn="37" endLine="17" endColumn="40" document="1" />
<entry offset="0x15" startLine="17" startColumn="20" endLine="17" endColumn="35" document="1" />
<entry offset="0x1b" startLine="24" startColumn="3" endLine="24" endColumn="4" document="1" />
</sequencePoints>
<scope startOffset="0x0" endOffset="0x1c">
<scope startOffset="0x0" endOffset="0x1b">
<local name="i" il_index="0" il_start="0x0" il_end="0x1b" attributes="0" />
</scope>
</scope>
</method>
</methods>
<method-spans>
<method declaringType="ICSharpCode.Decompiler.Tests.TestCases.PdbGen.ForLoopTests" methodName="SimplePrintLoop" parameterNames="args" token="0x6000001">
<document startLine="9" endLine="13" />
</method>
<method declaringType="ICSharpCode.Decompiler.Tests.TestCases.PdbGen.ForLoopTests" methodName="SimplePrintLoopWithCondition" parameterNames="args" token="0x6000002">
<document startLine="17" endLine="24" />
</method>
</method-spans>
</symbols>

16
ICSharpCode.Decompiler.Tests/TestCases/PdbGen/HelloWorld.xml

@ -16,20 +16,4 @@ public class HelloWorld @@ -16,20 +16,4 @@ public class HelloWorld
}
]]></file>
</files>
<methods>
<method containingType="ICSharpCode.Decompiler.Tests.TestCases.PdbGen.HelloWorld" name="Main" parameterNames="args" token="0x6000001">
<sequencePoints>
<entry offset="0x0" startLine="9" startColumn="4" endLine="9" endColumn="22" document="1" />
<entry offset="0x6" startLine="10" startColumn="4" endLine="10" endColumn="38" document="1" />
<entry offset="0x10" startLine="11" startColumn="4" endLine="11" endColumn="22" document="1" />
<entry offset="0x16" startLine="12" startColumn="3" endLine="12" endColumn="4" document="1" />
</sequencePoints>
<scope startOffset="0x0" endOffset="0x17" />
</method>
</methods>
<method-spans>
<method declaringType="ICSharpCode.Decompiler.Tests.TestCases.PdbGen.HelloWorld" methodName="Main" parameterNames="args" token="0x6000001">
<document startLine="9" endLine="12" />
</method>
</method-spans>
</symbols>

2
ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.residual.txt

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
0x06000001 ICSharpCode.Decompiler.Tests.TestCases.PdbGen.LambdaCapturing.Main
+ decompiler only: (8,2)-(8,3)

38
ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.xml

@ -21,42 +21,4 @@ public class LambdaCapturing @@ -21,42 +21,4 @@ public class LambdaCapturing
}
]]></file>
</files>
<methods>
<method containingType="ICSharpCode.Decompiler.Tests.TestCases.PdbGen.LambdaCapturing" name="Main" parameterNames="args" token="0x6000001">
<sequencePoints>
<entry offset="0x0" hidden="true" document="1" />
<entry offset="0x5" startLine="9" startColumn="13" endLine="9" endColumn="27" document="1" />
<entry offset="0x7" startLine="10" startColumn="13" endLine="10" endColumn="58" document="1" />
<entry offset="0x14" startLine="11" startColumn="13" endLine="11" endColumn="46" document="1" />
<entry offset="0x24" startLine="12" startColumn="9" endLine="12" endColumn="10" document="1" />
</sequencePoints>
<scope startOffset="0x0" endOffset="0x25">
<local name="local" il_index="0" il_start="0x0" il_end="0x25" attributes="0" />
</scope>
</method>
<method containingType="ICSharpCode.Decompiler.Tests.TestCases.PdbGen.LambdaCapturing" name="Test" parameterNames="p" token="0x6000002">
<sequencePoints>
<entry offset="0x0" startLine="16" startColumn="13" endLine="16" endColumn="21" document="1" />
<entry offset="0x9" startLine="17" startColumn="9" endLine="17" endColumn="10" document="1" />
</sequencePoints>
<scope startOffset="0x0" endOffset="0xa" />
</method>
<method containingType="ICSharpCode.Decompiler.Tests.TestCases.PdbGen.LambdaCapturing+&lt;&gt;c__DisplayClass0_0" name="&lt;Main&gt;b__0" parameterNames="a, b" token="0x6000005">
<sequencePoints>
<entry offset="0x0" startLine="11" startColumn="28" endLine="11" endColumn="44" document="1" />
</sequencePoints>
<scope startOffset="0x0" endOffset="0xb" />
</method>
</methods>
<method-spans>
<method declaringType="ICSharpCode.Decompiler.Tests.TestCases.PdbGen.LambdaCapturing" methodName="Main" parameterNames="args" token="0x6000001">
<document startLine="9" endLine="12" />
</method>
<method declaringType="ICSharpCode.Decompiler.Tests.TestCases.PdbGen.LambdaCapturing" methodName="Test" parameterNames="p" token="0x6000002">
<document startLine="16" endLine="17" />
</method>
<method declaringType="ICSharpCode.Decompiler.Tests.TestCases.PdbGen.LambdaCapturing+&lt;&gt;c__DisplayClass0_0" methodName="&lt;Main&gt;b__0" parameterNames="a, b" token="0x6000005">
<document startLine="11" endLine="11" />
</method>
</method-spans>
</symbols>

6
ICSharpCode.Decompiler.Tests/TestCases/PdbGen/Members.residual.txt

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
0x0600000b C..ctor
- compiler only: (50,2)-(50,12)
+ decompiler only: (51,2)-(51,3)
0x0600000c C.Finalize
- compiler only: (56,2)-(56,3)
- compiler only: (57,2)-(57,3)

111
ICSharpCode.Decompiler.Tests/TestCases/PdbGen/Members.xml

@ -1,95 +1,86 @@ @@ -1,95 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<symbols>
<files>
<file id="1" name="-\C.cs" language="C#" checksumAlgorithm="SHA256">
<![CDATA[using System;
<file id="1" name="-\C.cs" language="C#" checksumAlgorithm="SHA256"><![CDATA[using System;
internal class C : IDisposable
{
private int ExpressionProperty => 42;
private int Property
{
get
private int Property
{
get
{
return 0;
}
set
set
{
}
}
}
private C this[int index] => null;
private C this[int index] => null;
private C this[string s]
{
get
private C this[string s]
{
get
{
return null;
}
set
set
{
}
}
}
public event Action Event
{
add
{
}
remove
{
}
}
public event Action Event
{
add
{
}
remove
{
}
}
public static implicit operator C(int i)
{
return null;
}
public static implicit operator C(int i)
{
return null;
}
static C()
{
}
static C()
{
}
public C()
{
public C()
{
Console.WriteLine();
}
~C()
{
}
}
void IDisposable.Dispose()
{
}
~C()
{
}
private static void Main()
{
C c = new C();
void IDisposable.Dispose()
{
}
c.Event += delegate
private static void Main()
{
C c = new C();
c.Event += delegate
{
};
_ = c.Property;
_ = c.ExpressionProperty;
_ = c[0];
_ = c[""];
_ = (C)1;
Local();
static void Local()
{
_ = c.Property;
_ = c.ExpressionProperty;
_ = c[0];
_ = c[""];
_ = (C)1;
Local();
static void Local()
{
Console.WriteLine();
}
}
}
}
}
]]></file>
</files>
<methods>
</methods>
<method-spans>
</method-spans>
</symbols>
Loading…
Cancel
Save