vbc builds an exception filter as a single non-short-circuiting
expression: `isinst`, then the user's `when` conditions, combined with
`and`. DetectCatchWhenConditionBlocks only knew the branch chain csc
emits, so the type test stayed inside the filter and the handler kept the
`object` variable it has in IL:
catch (object obj) when ((obj is Exception) & (num2 != 0) & (num == 0))
{
ProjectData.SetProjectError((Exception)obj);
A catch type has to derive from Exception, so that does not compile.
The conjunction form is matched too now, lifting the test to the catch
type as the block form already does. The other conjuncts stop running for
a non-matching exception once the test moves, so the filter has to be
pure for this to be invisible; PropagateExceptionVariable then drops the
castclass in the handler:
catch (Exception ex) when ((num2 != 0) & (num == 0))
{
ProjectData.SetProjectError(ex);
Closes #3659
478 lines
9.1 KiB
C#
478 lines
9.1 KiB
C#
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
|
|
//
|
|
// 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;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Threading.Tasks;
|
|
|
|
using ICSharpCode.Decompiler.Tests.Helpers;
|
|
|
|
using NUnit.Framework;
|
|
|
|
namespace ICSharpCode.Decompiler.Tests
|
|
{
|
|
[TestFixture, Parallelizable(ParallelScope.All)]
|
|
public class ILPrettyTestRunner
|
|
{
|
|
static readonly string TestCasePath = Tester.TestCasePath + "/ILPretty";
|
|
|
|
[Test]
|
|
public void AllFilesHaveTests()
|
|
{
|
|
var testNames = typeof(ILPrettyTestRunner).GetMethods()
|
|
.Where(m => m.GetCustomAttributes(typeof(TestAttribute), false).Any())
|
|
.Select(m => m.Name)
|
|
.ToArray();
|
|
foreach (var file in new DirectoryInfo(TestCasePath).EnumerateFiles())
|
|
{
|
|
if (file.Extension.Equals(".il", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var testName = file.Name.Split('.')[0];
|
|
Assert.That(testNames, Has.Member(testName));
|
|
Assert.That(File.Exists(Path.Combine(TestCasePath, testName + ".cs")));
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public async Task ConditionalChain()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task AnonymousMethodEdgeCases()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test, Ignore("Need to decide how to represent virtual methods without 'newslot' flag")]
|
|
public async Task Issue379()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue646()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue684()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue959()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue982()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue1038()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue1047()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue1389()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue1918()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue1922()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task FSharpUsing_Debug()
|
|
{
|
|
await Run(settings: new DecompilerSettings { RemoveDeadStores = true, UseEnhancedUsing = false, FileScopedNamespaces = false });
|
|
}
|
|
|
|
[Test]
|
|
public async Task FSharpUsing_Release()
|
|
{
|
|
await Run(settings: new DecompilerSettings { RemoveDeadStores = true, UseEnhancedUsing = false, FileScopedNamespaces = false });
|
|
}
|
|
|
|
[Test]
|
|
public async Task DirectCallToExplicitInterfaceImpl()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task TruncatedAccessorBody()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task EvalOrder()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task TailCall()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task CS1xSwitch_Debug()
|
|
{
|
|
await Run(settings: new DecompilerSettings { SwitchExpressions = false, FileScopedNamespaces = false });
|
|
}
|
|
|
|
[Test]
|
|
public async Task CS1xSwitch_Release()
|
|
{
|
|
await Run(settings: new DecompilerSettings { SwitchExpressions = false, FileScopedNamespaces = false });
|
|
}
|
|
|
|
[Test]
|
|
public async Task UnknownTypes()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task NoAccessorProperties()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue1145()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue1157()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue1256()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue1323()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue1325()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue1681()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue1454()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue1638()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue2104()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue2443()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue3344CkFinite()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue3421()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue3442()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue3465()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue3659()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue3466()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue3504()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue3524()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue3552()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue2260SwitchString()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task SwitchOnStringNegativeCharIndex()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task CachedReadOnlySpanFromLazyCache()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task SpanConversionOperatorMismatch()
|
|
{
|
|
await Run(settings: new DecompilerSettings { FileScopedNamespaces = false, FirstClassSpanTypes = true });
|
|
}
|
|
|
|
[Test]
|
|
public async Task ConstantBlobs()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task SequenceOfNestedIfs()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task StackAllocDuplicateStore()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test, Platform("Win")] // UseLegacyAssembler requires the .NET Framework ilasm
|
|
public async Task Unsafe()
|
|
{
|
|
await Run(assemblerOptions: AssemblerOptions.Library | AssemblerOptions.UseLegacyAssembler);
|
|
}
|
|
|
|
[Test]
|
|
public async Task CallIndirect()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task InstanceOperatorCall()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task FSharpLoops_Debug()
|
|
{
|
|
CopyFSharpCoreDll();
|
|
await Run(settings: new DecompilerSettings { RemoveDeadStores = true, FileScopedNamespaces = false });
|
|
}
|
|
|
|
[Test]
|
|
public async Task FSharpLoops_Release()
|
|
{
|
|
CopyFSharpCoreDll();
|
|
await Run(settings: new DecompilerSettings { RemoveDeadStores = true, FileScopedNamespaces = false });
|
|
}
|
|
|
|
[Test]
|
|
public async Task WeirdEnums()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task EnumArithmeticOutOfRange()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task GuessAccessors()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task InaccessibleParameterTypes()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task EmptyBodies()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task SealedRecordProtectedCopyCtor()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task MonoFixed()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task ExtensionEncodingV1()
|
|
{
|
|
// uses Microsoft.Net.Compilers.Toolset 5.0.0-2.25380.108
|
|
// see ExtensionEncodingV1.il for details
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task ExtensionEncodingV2()
|
|
{
|
|
// uses Microsoft.Net.Compilers.Toolset 5.0.0-2.25451.107
|
|
// see ExtensionEncodingV2.il for details
|
|
await Run();
|
|
}
|
|
|
|
[Test]
|
|
public async Task SortSwitchSections()
|
|
{
|
|
await Run(settings: new DecompilerSettings { SortSwitchSections = true, FileScopedNamespaces = false });
|
|
}
|
|
|
|
[Test]
|
|
public async Task Issue3729()
|
|
{
|
|
await Run();
|
|
}
|
|
|
|
async Task Run([CallerMemberName] string testName = null, DecompilerSettings settings = null,
|
|
AssemblerOptions assemblerOptions = AssemblerOptions.Library)
|
|
{
|
|
if (settings == null)
|
|
{
|
|
// never use file-scoped namespaces, unless explicitly specified
|
|
settings = new DecompilerSettings { FileScopedNamespaces = 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"]);
|
|
Tester.RepeatOnIOError(() => File.Delete(decompiled));
|
|
}
|
|
|
|
static readonly object copyLock = new object();
|
|
|
|
static void CopyFSharpCoreDll()
|
|
{
|
|
lock (copyLock)
|
|
{
|
|
if (File.Exists(Path.Combine(TestCasePath, "FSharp.Core.dll")))
|
|
return;
|
|
string fsharpCoreDll = Path.Combine(TestCasePath, "..", "..", "..", "ILSpy-tests", "FSharp", "FSharp.Core.dll");
|
|
if (!File.Exists(fsharpCoreDll))
|
|
Assert.Ignore("Ignored because of missing ILSpy-tests repo. Must be checked out separately from https://github.com/icsharpcode/ILSpy-tests!");
|
|
File.Copy(fsharpCoreDll, Path.Combine(TestCasePath, "FSharp.Core.dll"));
|
|
}
|
|
}
|
|
}
|
|
}
|