Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions ICSharpCode.Decompiler.Tests/DecompilerSettingsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,12 @@ public void GetMinimumRequiredVersionReturnsTheHighestEnabledFeatureVersion()
settings.ParamsCollections = false;
Assert.That(settings.GetMinimumRequiredVersion(), Is.EqualTo(LanguageVersion.CSharp11_0));
}

[Test]
public void CollectionExpressionsRequireCSharp12()
{
Assert.That(new DecompilerSettings(LanguageVersion.CSharp11_0).CollectionExpressions, Is.False);
Assert.That(new DecompilerSettings(LanguageVersion.CSharp12_0).CollectionExpressions, Is.True);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,8 @@
<None Include="TestCases\ILPretty\GuessAccessors.cs" />
<Compile Remove="TestCases\ILPretty\InaccessibleParameterTypes.cs" />
<None Include="TestCases\ILPretty\InaccessibleParameterTypes.cs" />
<Compile Remove="TestCases\ILPretty\MissingBaseConstructor.cs" />
<None Include="TestCases\ILPretty\MissingBaseConstructor.cs" />
<Compile Remove="TestCases\ILPretty\NoAccessorProperties.cs" />
<None Include="TestCases\ILPretty\NoAccessorProperties.cs" />
<Compile Remove="TestCases\ILPretty\Issue1145.cs" />
Expand Down
23 changes: 22 additions & 1 deletion ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -429,21 +429,42 @@ public async Task Issue3729()
await Run();
}

[Test]
public async Task LateBaseConstructorCall()
{
await Run();
}

[Test]
public async Task CompilerGeneratedAutoProperty()
{
await Run();
}

[Test]
public async Task MissingBaseConstructor()
{
await Run(expectedText: "//IL_0001: Unknown result type (might be due to invalid IL or missing references)");
}

async Task Run([CallerMemberName] string testName = null, DecompilerSettings settings = null,
AssemblerOptions assemblerOptions = AssemblerOptions.Library)
AssemblerOptions assemblerOptions = AssemblerOptions.Library, string expectedText = null)
{
if (settings == null)
{
// never use file-scoped namespaces, unless explicitly specified
settings = new DecompilerSettings { FileScopedNamespaces = false };
}
settings.CollectionExpressions = false;
var ilFile = Path.Combine(TestCasePath, testName + ".il");
var csFile = Path.Combine(TestCasePath, testName + ".cs");

var executable = await Tester.AssembleIL(ilFile, assemblerOptions).ConfigureAwait(false);
var decompiled = await Tester.DecompileCSharp(executable, settings).ConfigureAwait(false);

CodeAssert.FilesAreEqual(csFile, decompiled, ["EXPECTED_OUTPUT"]);
if (expectedText != null)
Assert.That(File.ReadAllText(decompiled), Does.Contain(expectedText));
Tester.RepeatOnIOError(() => File.Delete(decompiled));
}

Expand Down
25 changes: 25 additions & 0 deletions ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

using ICSharpCode.Decompiler.CSharp;
Expand Down Expand Up @@ -58,6 +60,29 @@ public void HelloWorld()
TestSequencePoints();
}

[Test]
public void GlobalNamespaceDocument()
{
(string peFileName, _) = CompileTestCase(nameof(GlobalNamespaceDocument));
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());
const string sourceText = "source text";
using var generatedPdb = new MemoryStream();
new PortablePdbWriter {
NoLogo = true,
SourceTextProvider = _ => sourceText,
}.WritePdb(module, decompiler, new DecompilerSettings(), generatedPdb);

generatedPdb.Position = 0;
var reader = MetadataReaderProvider.FromPortablePdbStream(generatedPdb).GetMetadataReader();
var document = reader.GetDocument(reader.Documents.Single());
Assert.That(reader.GetString(document.Name), Is.EqualTo("GlobalNamespaceDocument.cs"));
Assert.That(reader.GetBlobBytes(document.Hash),
Is.EqualTo(SHA256.HashData(Encoding.UTF8.GetBytes(sourceText))));
}

[Test]
public void ForLoopTests()
{
Expand Down
14 changes: 12 additions & 2 deletions ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,8 @@ public async Task UnsafeCode([ValueSource(nameof(defaultOptions))] CompilerOptio
[Test]
public async Task ConstructorInitializers([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions)
{
await RunForLibrary(cscOptions: cscOptions | CompilerOptions.ProcessXmlDoc);
await RunForLibrary(cscOptions: cscOptions | CompilerOptions.ProcessXmlDoc,
configureDecompiler: settings => settings.CollectionExpressions = Tester.GetPreprocessorSymbols(cscOptions).Contains("CS120"));
}

[Test]
Expand Down Expand Up @@ -744,7 +745,8 @@ public async Task RefLocalsAndReturns([ValueSource(nameof(roslyn2OrNewerOptions)
[Test]
public async Task CachedReadOnlySpanInitialization([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions)
{
await RunForLibrary(cscOptions: cscOptions);
await RunForLibrary(cscOptions: cscOptions,
configureDecompiler: settings => settings.CollectionExpressions = Tester.GetPreprocessorSymbols(cscOptions).Contains("CS120"));
}

[Test]
Expand Down Expand Up @@ -1040,6 +1042,13 @@ public async Task InlineArrayTests([ValueSource(nameof(roslyn4OrNewerOptions))]
await RunForLibrary(cscOptions: cscOptions);
}

[Test]
public async Task CollectionExpressions([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions)
{
await RunForLibrary(cscOptions: cscOptions,
configureDecompiler: settings => settings.CollectionExpressions = Tester.GetPreprocessorSymbols(cscOptions).Contains("CS120"));
}

[Test]
public async Task Issue3684([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions)
{
Expand Down Expand Up @@ -1080,6 +1089,7 @@ async Task Run([CallerMemberName] string testName = null, AssemblerOptions asmOp

// 2. Decompile
var settings = Tester.GetSettings(cscOptions);
settings.CollectionExpressions = false;
configureDecompiler?.Invoke(settings);
var decompiled = await Tester.DecompileCSharp(exeFile, settings).ConfigureAwait(false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
// DEALINGS IN THE SOFTWARE.

using System;
using System.IO;
using System.Reflection;

using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata;
Expand Down Expand Up @@ -129,5 +131,63 @@ public void VerifyUniversalAssemblyResolverParseTargetFramework(string targetFra
Assert.That(id, Is.EqualTo(identifier));
Assert.That(v.ToString(3), Is.EqualTo(version));
}

[TestCase(TargetFrameworkIdentifier.NET, true)]
[TestCase(TargetFrameworkIdentifier.NETCoreApp, true)]
[TestCase(TargetFrameworkIdentifier.NETStandard, true)]
[TestCase(TargetFrameworkIdentifier.Silverlight, false)]
public void VerifyUseOfDotNetCorePathFinder(TargetFrameworkIdentifier identifier, bool expected)
{
Assert.That(UniversalAssemblyResolver.UsesDotNetCorePathFinder(identifier), Is.EqualTo(expected));
}
[Test]
public void NetStandardResolvesSharedRuntimeAssembly()
{
var reference = AssemblyNameReference.Parse(
"System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51");
var resolver = new UniversalAssemblyResolver(null, false, ".NETStandard,Version=v2.0");

var file = resolver.FindAssemblyFile(reference);

Assert.That(file, Is.Not.Null);
Assert.That(File.Exists(file), Is.True);
}

[Test]
public void NetCoreAppResolvesCompatibleSharedRuntimeAssembly()
{
var reference = AssemblyNameReference.Parse(
"System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51");
var resolver = new UniversalAssemblyResolver(null, false, ".NETCoreApp,Version=v3.1");

var file = resolver.FindAssemblyFile(reference);

Assert.That(file, Is.Not.Null);
Assert.That(File.Exists(file), Is.True);
Assert.That(AssemblyName.GetAssemblyName(file!).Version, Is.GreaterThanOrEqualTo(reference.Version));
}

[Test]
public void LaterRuntimeAssemblyResolvesWithoutBecomingImplicitProjectReference()
{
var reference = AssemblyNameReference.Parse(
"System.Net.ServerSentEvents, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
var resolver = new UniversalAssemblyResolver(null, false, ".NETCoreApp,Version=v8.0");

Assert.That(resolver.FindAssemblyFile(reference), Is.Not.Null);
Assert.That(resolver.IsSharedAssembly(reference, out _), Is.False);
}

[TestCase(TargetFrameworkIdentifier.NETStandard, ".NETStandard,Version=v2.0", PlatformID.Win32NT, false)]
[TestCase(TargetFrameworkIdentifier.NET, ".NETFramework,Version=v4.7.2", PlatformID.Win32NT, false)]
[TestCase(TargetFrameworkIdentifier.NETCoreApp, ".NETCoreApp,Version=v3.1", PlatformID.Win32NT, true)]
[TestCase(TargetFrameworkIdentifier.NET, ".NETCoreApp,Version=v10.0", PlatformID.Win32NT, true)]
[TestCase(TargetFrameworkIdentifier.NETStandard, ".NETStandard,Version=v2.0", PlatformID.Unix, true)]
public void VerifyHostRuntimeFallback(TargetFrameworkIdentifier identifier, string targetFramework,
PlatformID platform, bool expected)
{
Assert.That(UniversalAssemblyResolver.ShouldUseHostRuntimeFallback(identifier, targetFramework, platform),
Is.EqualTo(expected));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

using NUnit.Framework;

namespace ICSharpCode.Decompiler.Tests.ProjectDecompiler;
Expand Down Expand Up @@ -134,6 +137,75 @@ public void OneFailingResourceDoesNotDropTheOthers()
}
}

[Test]
public void HiddenReferencedTypesDoNotCreateEmptyProjectFiles()
{
string assemblyPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".dll");
try
{
CompileCollectionExpressionAssembly(assemblyPath);
TestFriendlyProjectDecompiler decompiler = new(new UniversalAssemblyResolver(assemblyPath, false, null));
decompiler.Settings.CollectionExpressions = true;

using PEFile module = new(assemblyPath);
decompiler.DecompileProject(module, Path.GetTempPath(), new StringWriter());

using (Assert.EnterMultipleScope())
{
Assert.That(decompiler.Files.Keys.Select(Path.GetFileName),
Has.None.StartsWith("--z__ReadOnly"));
string source = decompiler.Files.Single(file => Path.GetFileName(file.Key) == "CollectionSource.cs")
.Value.ToString();
Assert.That(source, Does.Contain("Consume([1]);"));
Assert.That(source, Does.Contain("Consume([1, 2, 3]);"));
Assert.That(source, Does.Match(@"Consume\(\[\.\. \w+\]\);"));
}
}
finally
{
File.Delete(assemblyPath);
}
}

static void CompileCollectionExpressionAssembly(string assemblyPath)
{
const string source = """
using System.Collections.Generic;

public static class CollectionSource
{
public static void Call(int[] values)
{
Consume([1]);
Consume([1, 2, 3]);
Consume([0, .. values, 4]);
}

private static void Consume(IReadOnlyList<int> values)
{
}
}
""";
string runtimeDirectory = Path.GetDirectoryName(typeof(object).Assembly.Location)!;
var compilation = CSharpCompilation.Create(
"CollectionExpressionProject",
new[] {
CSharpSyntaxTree.ParseText(source,
new CSharpParseOptions(Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12))
},
new[] {
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(Path.Combine(runtimeDirectory, "System.Runtime.dll"))
},
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Release));

using FileStream output = File.Create(assemblyPath);
var result = compilation.Emit(output);
Assert.That(result.Success, Is.True,
string.Join(Environment.NewLine, result.Diagnostics.Select(diagnostic => diagnostic.ToString())));
}

sealed class ThrowingAstTransform(string typeName) : IAstTransform
{
public const string Failure = "Simulated AST transform failure";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
{
public static class CachedReadOnlySpanFromLazyCache
{
public static ReadOnlySpan<char> NewLine {
get {
return new ReadOnlySpan<char>(new char[2] { '\r', '\n' });
}
}
public static ReadOnlySpan<char> NewLine => new ReadOnlySpan<char>(new char[2] { '\r', '\n' });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Runtime.CompilerServices;

namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
{
#if !EXPECTED_OUTPUT
public struct MissingMemory<T>
{
}
#endif

[CompilerGenerated]
public sealed class CompilerGeneratedAutoProperty
{
public string Name { get; }

public CompilerGeneratedAutoProperty(string name)
{
Name = name;
}
}

public class UnresolvedGenericAutoProperty<T>
{
public MissingMemory<T> Data { get; set; }
}
}
Loading
Loading