mirror of
https://github.com/icsharpcode/ILSpy.git
synced 2026-10-12 02:15:58 +00:00
Cover more constructs in the PDB sequence-point tests
Extends the breakpoint-map comparison to hidden sequence points, anchoring each hidden point to the visible point it follows so the descriptor stays independent of the IL offsets the decompiler reconstructs. Adds PdbGen cases spanning try/catch/finally, switch, async/await, yield, loops, LINQ, pattern matching and more, pinning the known residuals where the decompiler folds a compiler-hidden branch into an adjacent point. Assisted-by: Claude:claude-opus-4-8:Claude Code
This commit is contained in:
committed by
Siegfried Pammer
parent
182fb4e814
commit
853220dba9
@@ -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 (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) |
|
||||
| PdbGen | `PdbGenerationTestRunner` / `PdbGen/*.cs` | 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,14 +69,14 @@ 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** (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
|
||||
- **PdbGen** (the reconstructed PDB's breakpoints match the C# compiler's): add `MyTest.cs`,
|
||||
written exactly as ILSpy pretty-prints a *single 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 the **breakpoint map**: per method, the
|
||||
ordered source locations of visible sequence points and the placement of hidden sequence
|
||||
points anchored to neighboring source locations. IL offsets, 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
|
||||
|
||||
@@ -117,17 +117,24 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
|
||||
/// line span is compared (use for methods where the decompiler's column placement within a
|
||||
/// statement legitimately differs).
|
||||
/// </param>
|
||||
/// <param name="includeHidden">
|
||||
/// When true, hidden sequence points are compared too. A hidden point has no source
|
||||
/// location, so it is described by the visible point it follows ("hidden after <loc>",
|
||||
/// or "hidden at method start"). Anchoring to a source location keeps the comparison
|
||||
/// independent of the exact IL offsets, which the decompiler reconstructs differently.
|
||||
/// </param>
|
||||
public static string CompareBreakpointMaps(
|
||||
Dictionary<int, List<RawSequencePoint>> expected,
|
||||
Dictionary<int, List<RawSequencePoint>> actual,
|
||||
Dictionary<int, string> methodNames,
|
||||
bool includeColumns)
|
||||
bool includeColumns,
|
||||
bool includeHidden)
|
||||
{
|
||||
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 expectedPoints = Tokens(expected, row, includeColumns, includeHidden);
|
||||
var actualPoints = Tokens(actual, row, includeColumns, includeHidden);
|
||||
|
||||
var expectedOnly = MultisetDifference(expectedPoints, actualPoints);
|
||||
var actualOnly = MultisetDifference(actualPoints, expectedPoints);
|
||||
@@ -144,11 +151,31 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
|
||||
return report.ToString();
|
||||
}
|
||||
|
||||
static List<string> Visible(Dictionary<int, List<RawSequencePoint>> map, int row, bool includeColumns)
|
||||
/// <summary>
|
||||
/// Projects a method's sequence points onto comparable descriptors: each visible point
|
||||
/// becomes its source location; each hidden point (when included) is anchored to the visible
|
||||
/// point it follows, so the descriptor does not depend on the reconstructed IL offset.
|
||||
/// </summary>
|
||||
static List<string> Tokens(Dictionary<int, List<RawSequencePoint>> map, int row, bool includeColumns, bool includeHidden)
|
||||
{
|
||||
var result = new List<string>();
|
||||
if (!map.TryGetValue(row, out var list))
|
||||
return new List<string>();
|
||||
return list.Where(sp => !sp.IsHidden).Select(sp => sp.Format(includeColumns)).ToList();
|
||||
return result;
|
||||
string lastVisible = null;
|
||||
foreach (var sp in list)
|
||||
{
|
||||
if (sp.IsHidden)
|
||||
{
|
||||
if (includeHidden)
|
||||
result.Add(lastVisible == null ? "hidden at method start" : "hidden after " + lastVisible);
|
||||
}
|
||||
else
|
||||
{
|
||||
lastVisible = sp.Format(includeColumns);
|
||||
result.Add(lastVisible);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static List<string> MultisetDifference(List<string> a, List<string> b)
|
||||
|
||||
@@ -162,12 +162,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="TestCases\PdbGen\Members.xml" />
|
||||
<Content Include="TestCases\PdbGen\ProgressReporting.xml" />
|
||||
<Content Include="TestCases\PdbGen\ForLoopTests.xml" />
|
||||
<Content Include="TestCases\PdbGen\CustomPdbId.xml" />
|
||||
<Content Include="TestCases\PdbGen\HelloWorld.xml" />
|
||||
<Content Include="TestCases\PdbGen\LambdaCapturing.xml" />
|
||||
<None Include="TestCases\PdbGen\*.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -185,6 +180,7 @@
|
||||
are not valid standalone C#, expected-output fixtures that reuse type names, and
|
||||
MetadataAttributes.cs (applies assembly/module attributes that break NUnit discovery).
|
||||
The harness compiles these from disk at test time; kept as None for IDE visibility. -->
|
||||
<Compile Remove="TestCases\PdbGen\*.cs" />
|
||||
<Compile Remove="TestCases\Correctness\ComInterop.cs" />
|
||||
<None Include="TestCases\Correctness\ComInterop.cs" />
|
||||
<Compile Remove="TestCases\ILPretty\CS1xSwitch_Debug.cs" />
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.Linq;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Xml.Linq;
|
||||
|
||||
using ICSharpCode.Decompiler.CSharp;
|
||||
using ICSharpCode.Decompiler.DebugInfo;
|
||||
@@ -41,7 +40,9 @@ namespace ICSharpCode.Decompiler.Tests
|
||||
[Test]
|
||||
public void ForLoopTests()
|
||||
{
|
||||
TestSequencePoints();
|
||||
// The compiler hides the unconditional branch from the loop setup to the condition test;
|
||||
// the decompiler folds that IL into the loop body's point instead. The residual pins it.
|
||||
TestSequencePoints(knownResidual: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -58,6 +59,117 @@ namespace ICSharpCode.Decompiler.Tests
|
||||
TestSequencePoints(knownResidual: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryCatchFinally()
|
||||
{
|
||||
TestSequencePoints(knownResidual: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SwitchStatement()
|
||||
{
|
||||
TestSequencePoints(knownResidual: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AsyncAwait()
|
||||
{
|
||||
TestSequencePoints(knownResidual: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void YieldReturn()
|
||||
{
|
||||
TestSequencePoints(knownResidual: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ForeachUsing()
|
||||
{
|
||||
// The using statement and foreach are lowered to try/finally; the decompiler places the
|
||||
// disposal and the closing braces differently from the compiler.
|
||||
TestSequencePoints(knownResidual: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WhileLoops()
|
||||
{
|
||||
TestSequencePoints();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LockStatement()
|
||||
{
|
||||
// The compiler breakpoints the lock's closing brace and the static-field initializer in
|
||||
// the .cctor; the decompiler reconstructs the lock exit and the cctor differently.
|
||||
TestSequencePoints(knownResidual: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LinqQuery()
|
||||
{
|
||||
// The query is lowered to lambdas; the decompiler emits an extra hidden point inside each
|
||||
// lambda and places the enclosing foreach's point differently from the compiler.
|
||||
TestSequencePoints(knownResidual: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SwitchExpression()
|
||||
{
|
||||
// The compiler breakpoints each switch-expression arm; the decompiler breakpoints the
|
||||
// method's opening brace and keeps the arm bodies hidden.
|
||||
TestSequencePoints(knownResidual: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LocalFunctions()
|
||||
{
|
||||
TestSequencePoints();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GotoLabels()
|
||||
{
|
||||
TestSequencePoints();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CheckedUnchecked()
|
||||
{
|
||||
TestSequencePoints();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NestedLoops()
|
||||
{
|
||||
// Same residual as ForLoopTests, once per loop: the compiler keeps the back-branch from
|
||||
// the increment to the condition test hidden, while the decompiler folds that IL into the
|
||||
// loop body's point.
|
||||
TestSequencePoints(knownResidual: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConditionalOperators()
|
||||
{
|
||||
// The decompiler emits an extra hidden point after each statement that the compiler
|
||||
// does not, where the ?? / ?: result is consumed.
|
||||
TestSequencePoints(knownResidual: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PatternMatching()
|
||||
{
|
||||
TestSequencePoints();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UsingDeclaration()
|
||||
{
|
||||
// The using declaration is lowered to a try/finally whose disposal point the decompiler
|
||||
// reconstructs at a different location than the compiler.
|
||||
TestSequencePoints(knownResidual: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CustomPdbId()
|
||||
{
|
||||
@@ -69,9 +181,8 @@ namespace ICSharpCode.Decompiler.Tests
|
||||
var decompiler = new CSharpDecompiler(moduleDefinition, resolver, new DecompilerSettings());
|
||||
var expectedPdbId = new BlobContentId(Guid.NewGuid(), (uint)Random.Shared.Next());
|
||||
|
||||
using (FileStream pdbStream = File.Open(Path.Combine(TestCasePath, nameof(CustomPdbId) + ".pdb"), FileMode.OpenOrCreate, FileAccess.ReadWrite))
|
||||
using (FileStream pdbStream = File.Open(Path.Combine(TestCasePath, nameof(CustomPdbId) + ".pdb"), FileMode.Create, FileAccess.ReadWrite))
|
||||
{
|
||||
pdbStream.SetLength(0);
|
||||
new PortablePdbWriter { NoLogo = true, PdbId = expectedPdbId }
|
||||
.WritePdb(moduleDefinition, decompiler, new DecompilerSettings(), pdbStream);
|
||||
|
||||
@@ -94,11 +205,9 @@ namespace ICSharpCode.Decompiler.Tests
|
||||
|
||||
// Compile HelloWorld's source into a uniquely-named assembly so this test never collides
|
||||
// with the HelloWorld fixture's .expected files under the fixture's parallel scope.
|
||||
string sourceXml = Path.Combine(TestCasePath, nameof(HelloWorld) + ".xml");
|
||||
var files = XDocument.Parse(File.ReadAllText(sourceXml))
|
||||
.Descendants("file").ToDictionary(f => f.Attribute("name").Value, f => f.Value);
|
||||
string sourceFile = Path.Combine(TestCasePath, nameof(HelloWorld) + ".cs");
|
||||
string outputBase = Path.Combine(TestCasePath, nameof(EmbedSourceFiles_False_Omits_Embedded_Source) + ".expected");
|
||||
Tester.CompileCSharpWithPdb(outputBase, files);
|
||||
CompileCSharpWithPdb(outputBase, sourceFile);
|
||||
string peFileName = outputBase + ".dll";
|
||||
|
||||
var module = new PEFile(peFileName);
|
||||
@@ -172,9 +281,8 @@ namespace ICSharpCode.Decompiler.Tests
|
||||
lastFilesWritten = progress.UnitsCompleted;
|
||||
};
|
||||
|
||||
using (FileStream pdbStream = File.Open(Path.Combine(TestCasePath, nameof(ProgressReporting) + ".pdb"), FileMode.OpenOrCreate, FileAccess.ReadWrite))
|
||||
using (FileStream pdbStream = File.Open(Path.Combine(TestCasePath, nameof(ProgressReporting) + ".pdb"), FileMode.Create, FileAccess.ReadWrite))
|
||||
{
|
||||
pdbStream.SetLength(0);
|
||||
new PortablePdbWriter { NoLogo = true, Progress = new TestProgressReporter(reportFunc) }
|
||||
.WritePdb(moduleDefinition, decompiler, new DecompilerSettings(), pdbStream);
|
||||
|
||||
@@ -204,9 +312,9 @@ namespace ICSharpCode.Decompiler.Tests
|
||||
/// <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.
|
||||
/// the two PDBs' breakpoint maps. The decompiler reconstructs IL ranges and local scopes
|
||||
/// differently from the compiler, so the comparison asserts on source locations and hidden
|
||||
/// point placement without depending on exact IL offsets.
|
||||
/// </summary>
|
||||
/// <param name="tolerance">
|
||||
/// Whether visible breakpoints must match on column as well as line.
|
||||
@@ -242,7 +350,8 @@ namespace ICSharpCode.Decompiler.Tests
|
||||
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);
|
||||
expected, actual, methodNames,
|
||||
includeColumns: tolerance == Tolerance.LinesAndColumns, includeHidden: true);
|
||||
|
||||
if (knownResidual)
|
||||
{
|
||||
@@ -275,13 +384,17 @@ namespace ICSharpCode.Decompiler.Tests
|
||||
}
|
||||
}
|
||||
|
||||
private static void CompileCSharpWithPdb(string outputBase, string sourceFile)
|
||||
{
|
||||
Tester.CompileCSharpWithPdb(outputBase, new Dictionary<string, string> {
|
||||
{ Path.GetFileName(sourceFile), File.ReadAllText(sourceFile) }
|
||||
});
|
||||
}
|
||||
|
||||
private (string peFileName, string pdbFileName) CompileTestCase(string testName)
|
||||
{
|
||||
string xmlFile = Path.Combine(TestCasePath, testName + ".xml");
|
||||
string xmlContent = File.ReadAllText(xmlFile);
|
||||
XDocument document = XDocument.Parse(xmlContent);
|
||||
var files = document.Descendants("file").ToDictionary(f => f.Attribute("name").Value, f => f.Value);
|
||||
Tester.CompileCSharpWithPdb(Path.Combine(TestCasePath, testName + ".expected"), files);
|
||||
string sourceFile = Path.Combine(TestCasePath, testName + ".cs");
|
||||
CompileCSharpWithPdb(Path.Combine(TestCasePath, testName + ".expected"), sourceFile);
|
||||
|
||||
string peFileName = Path.Combine(TestCasePath, testName + ".expected.dll");
|
||||
string pdbFileName = Path.Combine(TestCasePath, testName + ".expected.pdb");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*.dll
|
||||
/*.pdb
|
||||
/*.expected.xml
|
||||
/*.generated.xml
|
||||
/*.residual.txt.generated
|
||||
/*.expected.cs
|
||||
/*.generated.cs
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
internal class AsyncAwait
|
||||
{
|
||||
public static async Task<int> SumAsync(int a, int b)
|
||||
{
|
||||
await Task.Yield();
|
||||
int num = a + b;
|
||||
Console.WriteLine(num);
|
||||
return num;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
0x06000003 <SumAsync>d__0.MoveNext
|
||||
- compiler only: (12,2)-(12,3)
|
||||
- compiler only: hidden after (12,2)-(12,3)
|
||||
- compiler only: hidden at method start
|
||||
- compiler only: hidden at method start
|
||||
+ decompiler only: (7,2)-(7,3)
|
||||
+ decompiler only: hidden after (9,3)-(9,19)
|
||||
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
internal class CheckedUnchecked
|
||||
{
|
||||
public static void Run(int a, int b)
|
||||
{
|
||||
Console.WriteLine(checked(a + b));
|
||||
Console.WriteLine(a * b);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
internal class ConditionalOperators
|
||||
{
|
||||
public static void Run(string value, int n)
|
||||
{
|
||||
Console.WriteLine(value ?? "default");
|
||||
Console.WriteLine((n > 0) ? "positive" : "non-positive");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
0x06000001 ConditionalOperators.Run
|
||||
+ decompiler only: hidden after (7,3)-(7,41)
|
||||
+ decompiler only: hidden after (8,3)-(8,60)
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen;
|
||||
|
||||
public class CustomPdbId
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Console.ReadKey();
|
||||
Console.WriteLine("Hello World!");
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<symbols>
|
||||
<files>
|
||||
<file id="1" name="ICSharpCode.Decompiler.Tests.TestCases.PdbGen\CustomPdbId.cs" language="C#" checksumAlgorithm="SHA256"><![CDATA[using System;
|
||||
|
||||
namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen;
|
||||
|
||||
public class CustomPdbId
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Console.ReadKey();
|
||||
Console.WriteLine("Hello World!");
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
]]></file>
|
||||
</files>
|
||||
</symbols>
|
||||
+1
-7
@@ -1,7 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<symbols>
|
||||
<files>
|
||||
<file id="1" name="ICSharpCode.Decompiler.Tests.TestCases.PdbGen\ForLoopTests.cs" language="C#" checksumAlgorithm="SHA256"><![CDATA[using System;
|
||||
using System;
|
||||
|
||||
namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen;
|
||||
|
||||
@@ -26,6 +23,3 @@ public class ForLoopTests
|
||||
}
|
||||
}
|
||||
}
|
||||
]]></file>
|
||||
</files>
|
||||
</symbols>
|
||||
@@ -0,0 +1,4 @@
|
||||
0x06000001 ICSharpCode.Decompiler.Tests.TestCases.PdbGen.ForLoopTests.SimplePrintLoop
|
||||
- compiler only: hidden after (9,8)-(9,17)
|
||||
0x06000002 ICSharpCode.Decompiler.Tests.TestCases.PdbGen.ForLoopTests.SimplePrintLoopWithCondition
|
||||
- compiler only: hidden after (17,8)-(17,17)
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
internal class ForeachUsing
|
||||
{
|
||||
public static void Run(IEnumerable<int> items)
|
||||
{
|
||||
using (StringWriter stringWriter = new StringWriter())
|
||||
{
|
||||
stringWriter.Write("x");
|
||||
}
|
||||
foreach (int item in items)
|
||||
{
|
||||
Console.WriteLine(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
0x06000001 ForeachUsing.Run
|
||||
- compiler only: (17,2)-(17,3)
|
||||
- compiler only: (9,10)-(9,56)
|
||||
- compiler only: hidden after (12,3)-(12,4)
|
||||
- compiler only: hidden after (13,21)-(13,23)
|
||||
- compiler only: hidden after (13,24)-(13,29)
|
||||
+ decompiler only: (9,3)-(9,57)
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
internal class GotoLabels
|
||||
{
|
||||
public static void Run(int n)
|
||||
{
|
||||
if (n == 0)
|
||||
{
|
||||
goto IL_0008;
|
||||
}
|
||||
if (n != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
goto IL_0012;
|
||||
IL_0008:
|
||||
Console.WriteLine("zero");
|
||||
goto IL_0012;
|
||||
IL_0012:
|
||||
Console.WriteLine("one");
|
||||
n--;
|
||||
if (n <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
goto IL_0008;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen;
|
||||
|
||||
public class HelloWorld
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Console.ReadKey();
|
||||
Console.WriteLine("Hello World!");
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<symbols>
|
||||
<files>
|
||||
<file id="1" name="ICSharpCode.Decompiler.Tests.TestCases.PdbGen\HelloWorld.cs" language="C#" checksumAlgorithm="SHA256"><![CDATA[using System;
|
||||
|
||||
namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen;
|
||||
|
||||
public class HelloWorld
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Console.ReadKey();
|
||||
Console.WriteLine("Hello World!");
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
]]></file>
|
||||
</files>
|
||||
</symbols>
|
||||
+1
-7
@@ -1,7 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<symbols>
|
||||
<files>
|
||||
<file id="1" name="ICSharpCode.Decompiler.Tests.TestCases.PdbGen\LambdaCapturing.cs" language="C#" checksumAlgorithm="SHA256"><![CDATA[using System;
|
||||
using System;
|
||||
|
||||
namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen;
|
||||
|
||||
@@ -19,6 +16,3 @@ public class LambdaCapturing
|
||||
p(1, 2);
|
||||
}
|
||||
}
|
||||
]]></file>
|
||||
</files>
|
||||
</symbols>
|
||||
@@ -1,2 +1,8 @@
|
||||
0x06000001 ICSharpCode.Decompiler.Tests.TestCases.PdbGen.LambdaCapturing.Main
|
||||
- compiler only: hidden at method start
|
||||
+ decompiler only: (8,2)-(8,3)
|
||||
+ decompiler only: hidden after (10,3)-(10,46)
|
||||
+ decompiler only: hidden after (8,2)-(8,3)
|
||||
+ decompiler only: hidden after (9,3)-(9,15)
|
||||
0x06000005 <>c__DisplayClass0_0.<Main>b__0
|
||||
+ decompiler only: hidden after (11,26)-(11,42)
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
internal class LinqQuery
|
||||
{
|
||||
public static void Run(IEnumerable<int> numbers)
|
||||
{
|
||||
foreach (int item in from n in numbers
|
||||
where n > 0
|
||||
select n * n)
|
||||
{
|
||||
Console.WriteLine(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
0x06000001 LinqQuery.Run
|
||||
- compiler only: (15,2)-(15,3)
|
||||
- compiler only: hidden after (9,21)-(9,23)
|
||||
0x06000005 <>c.<Run>b__0_0
|
||||
+ decompiler only: hidden after (10,10)-(10,15)
|
||||
0x06000006 <>c.<Run>b__0_1
|
||||
+ decompiler only: hidden after (11,11)-(11,16)
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
internal class LocalFunctions
|
||||
{
|
||||
public static void Run(int n)
|
||||
{
|
||||
Console.WriteLine(Square(n));
|
||||
static int Square(int x)
|
||||
{
|
||||
return x * x;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
internal class LockStatement
|
||||
{
|
||||
private static readonly object sync = new object();
|
||||
|
||||
public static void Run()
|
||||
{
|
||||
lock (sync)
|
||||
{
|
||||
Console.WriteLine("locked");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
0x06000001 LockStatement.Run
|
||||
- compiler only: (13,2)-(13,3)
|
||||
- compiler only: hidden after (12,3)-(12,4)
|
||||
+ decompiler only: (10,3)-(10,4)
|
||||
0x06000003 LockStatement..cctor
|
||||
- compiler only: (5,2)-(5,53)
|
||||
+1
-7
@@ -1,7 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<symbols>
|
||||
<files>
|
||||
<file id="1" name="-\C.cs" language="C#" checksumAlgorithm="SHA256"><![CDATA[using System;
|
||||
using System;
|
||||
|
||||
internal class C : IDisposable
|
||||
{
|
||||
@@ -81,6 +78,3 @@ internal class C : IDisposable
|
||||
}
|
||||
}
|
||||
}
|
||||
]]></file>
|
||||
</files>
|
||||
</symbols>
|
||||
@@ -1,6 +1,13 @@
|
||||
0x06000001 C.get_ExpressionProperty
|
||||
+ decompiler only: hidden after (5,36)-(5,38)
|
||||
0x06000004 C.get_Item
|
||||
+ decompiler only: hidden after (18,31)-(18,35)
|
||||
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)
|
||||
+ decompiler only: hidden after (57,2)-(57,3)
|
||||
0x0600000e C.Main
|
||||
+ decompiler only: hidden after (66,3)-(68,5)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
internal class NestedLoops
|
||||
{
|
||||
public static void Print(int rows, int columns)
|
||||
{
|
||||
for (int i = 0; i < rows; i++)
|
||||
{
|
||||
for (int j = 0; j < columns; j++)
|
||||
{
|
||||
Console.WriteLine(i * j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
0x06000001 NestedLoops.Print
|
||||
- compiler only: hidden after (7,8)-(7,17)
|
||||
- compiler only: hidden after (9,9)-(9,18)
|
||||
@@ -0,0 +1,15 @@
|
||||
internal class PatternMatching
|
||||
{
|
||||
public static string Describe(object value)
|
||||
{
|
||||
if (value is int num)
|
||||
{
|
||||
return "int " + num;
|
||||
}
|
||||
if (value is string text)
|
||||
{
|
||||
return "string " + text;
|
||||
}
|
||||
return "other";
|
||||
}
|
||||
}
|
||||
+1
-8
@@ -1,8 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<symbols>
|
||||
<files>
|
||||
<file id="1" name="ICSharpCode.Decompiler.Tests.TestCases.PdbGen\ProgressReporting.cs" language="C#" checksumAlgorithm="SHA256">
|
||||
<![CDATA[using System;
|
||||
using System;
|
||||
|
||||
namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen;
|
||||
|
||||
@@ -39,6 +35,3 @@ public class Class3
|
||||
Console.WriteLine("Class3");
|
||||
}
|
||||
}
|
||||
]]></file>
|
||||
</files>
|
||||
</symbols>
|
||||
@@ -0,0 +1,12 @@
|
||||
internal class SwitchExpression
|
||||
{
|
||||
public static string Classify(int n)
|
||||
{
|
||||
return n switch
|
||||
{
|
||||
0 => "zero",
|
||||
1 => "one",
|
||||
_ => "many",
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
0x06000001 SwitchExpression.Classify
|
||||
- compiler only: (7,9)-(7,15)
|
||||
- compiler only: (8,9)-(8,14)
|
||||
- compiler only: (9,9)-(9,15)
|
||||
- compiler only: hidden after (9,9)-(9,15)
|
||||
+ decompiler only: (4,2)-(4,3)
|
||||
+ decompiler only: hidden after (5,3)-(10,5)
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
internal class SwitchStatement
|
||||
{
|
||||
public static string Classify(int n)
|
||||
{
|
||||
switch (n)
|
||||
{
|
||||
case 0:
|
||||
return "zero";
|
||||
case 1:
|
||||
case 2:
|
||||
return "small";
|
||||
default:
|
||||
Console.WriteLine(n);
|
||||
return "other";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
0x06000001 SwitchStatement.Classify
|
||||
- compiler only: hidden at method start
|
||||
+ decompiler only: (6,2)-(6,3)
|
||||
+ decompiler only: (7,3)-(7,13)
|
||||
+ decompiler only: hidden after (7,3)-(7,13)
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
internal class TryCatchFinally
|
||||
{
|
||||
public static void Run(int n)
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine(n);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
}
|
||||
catch (Exception) when (n > 0)
|
||||
{
|
||||
Console.WriteLine("filtered");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.WriteLine("done");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
0x06000001 TryCatchFinally.Run
|
||||
- compiler only: (23,2)-(23,3)
|
||||
- compiler only: hidden after (15,21)-(15,33)
|
||||
+ decompiler only: (16,3)-(16,4)
|
||||
+ decompiler only: hidden after (22,3)-(22,4)
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
internal class UsingDeclaration
|
||||
{
|
||||
public static void Run()
|
||||
{
|
||||
using StringWriter stringWriter = new StringWriter();
|
||||
stringWriter.Write("x");
|
||||
Console.WriteLine(stringWriter.ToString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
0x06000001 UsingDeclaration.Run
|
||||
- compiler only: (11,2)-(11,3)
|
||||
- compiler only: (11,2)-(11,3)
|
||||
- compiler only: (8,3)-(8,56)
|
||||
- compiler only: hidden after (11,2)-(11,3)
|
||||
- compiler only: hidden after (11,2)-(11,3)
|
||||
+ decompiler only: (8,3)-(8,55)
|
||||
+ decompiler only: hidden after (10,3)-(10,46)
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
internal class YieldReturn
|
||||
{
|
||||
public static IEnumerable<int> Range(int start, int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Console.WriteLine(i);
|
||||
yield return start + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
0x06000005 <Range>d__0.MoveNext
|
||||
- compiler only: hidden at method start
|
||||
+ decompiler only: (7,2)-(7,3)
|
||||
+ decompiler only: hidden after (7,2)-(7,3)
|
||||
+ decompiler only: hidden after (8,19)-(8,28)
|
||||
+ decompiler only: hidden after (8,30)-(8,33)
|
||||
Reference in New Issue
Block a user