Browse Source

Merge pull request #4134 from dualfroz/fix-vb-exception-filter-type-test

Recognize a VB exception filter's type test
master
Siegfried Pammer 13 hours ago committed by GitHub
parent
commit
72dbe6f41d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 18
      ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs
  2. 7
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  3. 91
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/VBOnErrorCorrectness.vb
  4. 91
      ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Issue3659.cs
  5. 17
      ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Issue3659.vb
  6. 3120
      ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBOnError.cs
  7. 107
      ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBOnError.vb
  8. 619
      ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBTryCatchFinally.cs
  9. 308
      ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBTryCatchFinally.vb
  10. 20
      ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs
  11. 19
      ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
  12. 65
      ICSharpCode.Decompiler/IL/Transforms/DetectCatchWhenConditionBlocks.cs

18
ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs

@ -277,6 +277,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -277,6 +277,12 @@ namespace ICSharpCode.Decompiler.Tests
await RunVB(options: options);
}
[Test]
public async Task VBOnErrorCorrectness([ValueSource(nameof(noMonoOptions))] CompilerOptions options)
{
await RunVB(options: options);
}
[Test]
public async Task MemberLookup([ValueSource(nameof(defaultOptions))] CompilerOptions options)
{
@ -497,6 +503,18 @@ namespace ICSharpCode.Decompiler.Tests @@ -497,6 +503,18 @@ namespace ICSharpCode.Decompiler.Tests
outputFile = await Tester.CompileVB(Path.Combine(TestCasePath, testFileName), options,
outputFileName: testOutputFileName).ConfigureAwait(false);
string decompiledCodeFile = await Tester.DecompileCSharp(outputFile.PathToAssembly, Tester.GetSettings(options)).ConfigureAwait(false);
if ((options & CompilerOptions.UseRoslynMask) == 0)
{
// For second pass, use roslyn instead of the legacy csc.
// VB error handling compiles to exception filters, which C# 5 cannot express.
options |= CompilerOptions.UseRoslynLatest | CompilerOptions.TargetNet40;
}
else if ((options & CompilerOptions.UseRoslyn2_10_0) != 0 && (options & CompilerOptions.TargetNet40) == 0)
{
// The .NET Core 2.2 Microsoft.VisualBasic.dll lacks most of the VB runtime
// that decompiled VB code calls, such as Information.Err.
options = (options & ~CompilerOptions.UseRoslyn2_10_0) | CompilerOptions.UseRoslynLatest;
}
decompiledOutputFile = await Tester.CompileCSharp(decompiledCodeFile, options).ConfigureAwait(false);
await Tester.RunAndCompareOutput(testFileName, outputFile.PathToAssembly, decompiledOutputFile.PathToAssembly, decompiledCodeFile, (options & CompilerOptions.UseTestRunner) != 0, (options & CompilerOptions.Force32Bit) != 0);

7
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -109,6 +109,7 @@ @@ -109,6 +109,7 @@
<None Include="TestCases\Correctness\StackTests.il" />
<None Include="TestCases\Correctness\StackTypes.il" />
<None Include="TestCases\Correctness\Uninit.vb" />
<None Include="TestCases\Correctness\VBOnErrorCorrectness.vb" />
<None Include="TestCases\Disassembler\Pretty\GenericConstraints.il" />
<None Include="TestCases\Disassembler\Pretty\NegativeConstants.il" />
<None Include="TestCases\Disassembler\Pretty\InterfaceImplAttributes.il" />
@ -161,6 +162,8 @@ @@ -161,6 +162,8 @@
<None Include="TestCases\VBPretty\VBAutomaticEvents.vb" />
<None Include="TestCases\VBPretty\VBNonGenericForEach.vb" />
<None Include="TestCases\VBPretty\YieldReturn.vb" />
<None Include="TestCases\VBPretty\VBTryCatchFinally.vb" />
<None Include="TestCases\VBPretty\VBOnError.vb" />
<None Include="TestCases\VBPretty\Issue1906.vb" />
<None Include="TestCases\VBPretty\Issue2192.vb" />
<None Include="TestCases\VBPretty\Select.vb" />
@ -224,6 +227,8 @@ @@ -224,6 +227,8 @@
<Compile Remove="TestCases\ILPretty\Issue3729.cs" />
<None Include="TestCases\ILPretty\Issue3729.cs" />
<None Include="TestCases\ILPretty\Issue3729.il" />
<Compile Remove="TestCases\VBPretty\Issue3659.cs" />
<None Include="TestCases\VBPretty\Issue3659.cs" />
<Compile Remove="TestCases\ILPretty\SpanConversionOperatorMismatch.cs" />
<None Include="TestCases\ILPretty\SpanConversionOperatorMismatch.cs" />
<Compile Remove="TestCases\ILPretty\FSharpLoops_Debug.cs" />
@ -304,6 +309,8 @@ @@ -304,6 +309,8 @@
<None Include="TestCases\Ugly\TopLevelProgramAsync.Expected.cs" />
<Compile Remove="TestCases\VBPretty\VBCompoundAssign.cs" />
<None Include="TestCases\VBPretty\VBCompoundAssign.cs" />
<Compile Remove="TestCases\VBPretty\VBOnError.cs" />
<None Include="TestCases\VBPretty\VBOnError.cs" />
</ItemGroup>
</Project>

91
ICSharpCode.Decompiler.Tests/TestCases/Correctness/VBOnErrorCorrectness.vb

@ -0,0 +1,91 @@ @@ -0,0 +1,91 @@
Imports System
Imports Microsoft.VisualBasic
Module VBOnErrorCorrectness
Sub Main()
ResumeNext()
GoToHandler()
GoToHandlerResumeNext()
Console.WriteLine(GoToHandlerResume(3))
GoToHandlerResumeLabel()
Try
GoToZero()
Catch ex As InvalidOperationException
Console.WriteLine("outer: " & ex.Message)
End Try
Console.WriteLine(GoToHandlerWithResult())
End Sub
Sub FailWhilePositive(retries As Integer)
If retries > 0 Then Fail("retry " & retries)
End Sub
Sub Fail(message As String)
Throw New InvalidOperationException(message)
End Sub
Sub ResumeNext()
On Error Resume Next
Fail("a")
Console.WriteLine("after a: " & Err.Number & " " & Err.Description)
Err.Clear()
Console.WriteLine("cleared: " & Err.Number)
End Sub
Sub GoToHandler()
On Error GoTo Handler
Fail("b")
Console.WriteLine("unreachable")
Exit Sub
Handler:
Console.WriteLine("handler: " & Err.Description)
End Sub
Sub GoToHandlerResumeNext()
On Error GoTo Handler
Fail("c1")
Console.WriteLine("between")
Fail("c2")
Console.WriteLine("done")
Exit Sub
Handler:
Console.WriteLine("handler: " & Err.Description)
Resume Next
End Sub
Function GoToHandlerResume(retries As Integer) As Integer
On Error GoTo Handler
FailWhilePositive(retries)
Return retries
Handler:
retries -= 1
Resume
End Function
Sub GoToHandlerResumeLabel()
On Error GoTo Handler
Fail("d")
Done:
Console.WriteLine("done")
Exit Sub
Handler:
Console.WriteLine("handler: " & Err.Description)
Resume Done
End Sub
Sub GoToZero()
On Error Resume Next
Fail("e1")
Console.WriteLine("swallowed")
On Error GoTo 0
Fail("e2")
Console.WriteLine("unreachable")
End Sub
Function GoToHandlerWithResult() As Integer
On Error GoTo Handler
Return Integer.Parse("x")
Handler:
Return -1
End Function
End Module

91
ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Issue3659.cs

@ -0,0 +1,91 @@ @@ -0,0 +1,91 @@
using System;
using System.Runtime.CompilerServices;
using Microsoft.VisualBasic.CompilerServices;
public class Issue3659
{
public static void Func(ref Issue3659 obj, object value)
{
}
public static int ShowMessage(string a, int b, string c, object d, int e)
{
return 0;
}
internal void VBFunction(object value)
{
#if OPT || LEGACY_VBC
int try0000_dispatch = -1;
#else
int try0001_dispatch = -1;
#endif
int num2 = default;
int num = default;
while (true)
{
try
{
/*Note: ILSpy has introduced the following switch to emulate a goto from catch-block to try-block*/;
#if OPT || LEGACY_VBC
switch (try0000_dispatch)
#else
switch (try0001_dispatch)
#endif
{
default:
{
ProjectData.ClearProjectError();
num2 = 2;
Issue3659 obj = this;
Func(ref obj, RuntimeHelpers.GetObjectValue(value));
#if OPT || LEGACY_VBC
goto end_IL_0000;
#else
goto end_IL_0001;
#endif
}
#if OPT || LEGACY_VBC
case 45:
#else
case 50:
#endif
num = -1;
switch (num2)
{
case 2:
ShowMessage("VBFunction", 0, "Exception", null, 0);
#if OPT || LEGACY_VBC
goto end_IL_0000;
#else
goto end_IL_0001;
#endif
}
break;
}
}
catch (Exception ex) when ((num2 != 0) & (num == 0))
{
ProjectData.SetProjectError(ex);
#if OPT || LEGACY_VBC
try0000_dispatch = 45;
#else
try0001_dispatch = 50;
#endif
continue;
}
throw ProjectData.CreateProjectError(-2146828237);
continue;
#if OPT || LEGACY_VBC
end_IL_0000:
#else
end_IL_0001:
#endif
break;
}
if (num != 0)
{
ProjectData.ClearProjectError();
}
}
}

17
ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Issue3659.vb

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
Imports System
Public Class Issue3659
Public Shared Sub Func(ByRef obj As Issue3659, value As Object)
End Sub
Public Shared Function ShowMessage(a As String, b As Integer, c As String, d As Object, e As Integer) As Integer
Return 0
End Function
Friend Sub VBFunction(value As Object)
On Error GoTo Handler
Func(Me, value)
Exit Sub
Handler:
ShowMessage("VBFunction", 0, "Exception", Nothing, 0)
End Sub
End Class

3120
ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBOnError.cs

File diff suppressed because it is too large Load Diff

107
ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBOnError.vb

@ -0,0 +1,107 @@ @@ -0,0 +1,107 @@
Imports System
Imports Microsoft.VisualBasic
Public Class VBOnError
Public Shared Sub ResumeNext()
On Error Resume Next
Console.WriteLine("A")
Console.WriteLine("B")
End Sub
Public Shared Function ResumeNextWithResult() As Integer
On Error Resume Next
Dim x As Integer = 1
x = x \ 0
Return x
End Function
Public Shared Sub ResumeNextErrNumber()
On Error Resume Next
Console.WriteLine("A")
If Err.Number <> 0 Then
Console.WriteLine(Err.Description)
Err.Clear()
End If
End Sub
Public Shared Sub GoToHandler()
On Error GoTo Handler
Console.WriteLine("Body")
Exit Sub
Handler:
Console.WriteLine(Err.Description)
End Sub
Public Shared Sub GoToHandlerResumeNext()
On Error GoTo Handler
Console.WriteLine("A")
Console.WriteLine("B")
Exit Sub
Handler:
Console.WriteLine(Err.Number)
Resume Next
End Sub
Public Shared Sub GoToHandlerResume(retries As Integer)
On Error GoTo Handler
Console.WriteLine("Body")
Exit Sub
Handler:
retries -= 1
If retries > 0 Then
Resume
End If
End Sub
Public Shared Sub GoToHandlerResumeLabel()
On Error GoTo Handler
Console.WriteLine("Body")
Done:
Console.WriteLine("Done")
Exit Sub
Handler:
Console.WriteLine(Err.Description)
Resume Done
End Sub
Public Shared Sub GoToZero()
On Error Resume Next
Console.WriteLine("A")
On Error GoTo 0
Console.WriteLine("B")
End Sub
Public Shared Sub GoToMinusOne()
On Error GoTo Handler
Console.WriteLine("A")
Exit Sub
Handler:
On Error GoTo -1
On Error GoTo Handler2
Console.WriteLine("B")
Exit Sub
Handler2:
Console.WriteLine("C")
End Sub
Public Shared Sub SwitchHandlers()
On Error GoTo Handler1
Console.WriteLine("A")
On Error GoTo Handler2
Console.WriteLine("B")
Exit Sub
Handler1:
Console.WriteLine("Handler1")
Resume Next
Handler2:
Console.WriteLine("Handler2")
Resume Next
End Sub
Public Shared Function GoToHandlerWithResult() As Integer
On Error GoTo Handler
Return Integer.Parse("x")
Handler:
Return -1
End Function
End Class

619
ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBTryCatchFinally.cs

@ -0,0 +1,619 @@ @@ -0,0 +1,619 @@
using System;
using System.IO;
using Microsoft.VisualBasic.CompilerServices;
public class VBTryCatchFinally
{
private static bool Condition()
{
return true;
}
public static void TryFinally()
{
try
{
Console.WriteLine("Try");
}
finally
{
Console.WriteLine("Finally");
}
}
public static void TryCatchBare()
{
try
{
Console.WriteLine("Try");
}
catch (Exception projectError)
{
ProjectData.SetProjectError(projectError);
Console.WriteLine("Catch");
ProjectData.ClearProjectError();
}
}
public static void TryCatchVariable()
{
try
{
Console.WriteLine("Try");
}
catch (Exception ex)
{
ProjectData.SetProjectError(ex);
Exception ex2 = ex;
Console.WriteLine(ex2.Message);
ProjectData.ClearProjectError();
}
}
public static void TryCatchSpecificType()
{
try
{
Console.WriteLine("Try");
}
catch (IOException ex)
{
ProjectData.SetProjectError(ex);
IOException ex2 = ex;
Console.WriteLine(ex2.Message);
ProjectData.ClearProjectError();
}
}
public static void TryCatchUnusedVariable()
{
try
{
Console.WriteLine("Try");
}
catch (InvalidOperationException ex)
{
ProjectData.SetProjectError(ex);
InvalidOperationException ex2 = ex;
Console.WriteLine("Catch");
ProjectData.ClearProjectError();
}
}
public static void TryCatchWhen()
{
try
{
Console.WriteLine("Try");
}
catch (Exception projectError) when (((Func<bool>)delegate {
// Could not convert BlockContainer to single expression
ProjectData.SetProjectError(projectError);
return Condition();
}).Invoke())
{
Console.WriteLine("Catch");
ProjectData.ClearProjectError();
}
}
public static void TryCatchVariableWhen()
{
try
{
Console.WriteLine("Try");
}
catch (Exception ex) when (((Func<bool>)delegate {
// Could not convert BlockContainer to single expression
ProjectData.SetProjectError(ex);
return ex.Message != null;
}).Invoke())
{
Console.WriteLine(ex.Message);
ProjectData.ClearProjectError();
}
}
public static void TryCatchSpecificTypeWhen()
{
try
{
Console.WriteLine("Try");
}
catch (IOException ex) when (((Func<bool>)delegate {
// Could not convert BlockContainer to single expression
ProjectData.SetProjectError(ex);
return Condition();
}).Invoke())
{
Console.WriteLine(ex.Message);
ProjectData.ClearProjectError();
}
}
public static void TryCatchExistingLocal()
{
Exception value = null;
try
{
Console.WriteLine("Try");
}
catch (Exception ex)
{
ProjectData.SetProjectError(ex);
value = ex;
Console.WriteLine("Catch");
ProjectData.ClearProjectError();
}
Console.WriteLine(value);
}
public static void TryCatchExistingLocalWhen()
{
Exception value = null;
try
{
Console.WriteLine("Try");
}
catch (Exception ex) when (((Func<bool>)delegate {
// Could not convert BlockContainer to single expression
#if LEGACY_VBC
value = ex;
ProjectData.SetProjectError(ex);
#else
ProjectData.SetProjectError(ex);
value = ex;
#endif
return ex.Message != null;
}).Invoke())
{
Console.WriteLine("Catch");
ProjectData.ClearProjectError();
}
Console.WriteLine(value);
}
public static void TryCatchFinally()
{
try
{
Console.WriteLine("Try");
}
catch (Exception ex)
{
ProjectData.SetProjectError(ex);
Exception ex2 = ex;
Console.WriteLine(ex2.Message);
ProjectData.ClearProjectError();
}
finally
{
Console.WriteLine("Finally");
}
}
public static void TryCatchBareFinally()
{
try
{
Console.WriteLine("Try");
}
catch (Exception projectError)
{
ProjectData.SetProjectError(projectError);
Console.WriteLine("Catch");
ProjectData.ClearProjectError();
}
finally
{
Console.WriteLine("Finally");
}
}
public static void TryCatchWhenFinally()
{
try
{
Console.WriteLine("Try");
}
catch (Exception ex) when (((Func<bool>)delegate {
// Could not convert BlockContainer to single expression
ProjectData.SetProjectError(ex);
return Condition();
}).Invoke())
{
Console.WriteLine(ex.Message);
ProjectData.ClearProjectError();
}
finally
{
Console.WriteLine("Finally");
}
}
public static void TryMultipleCatch()
{
try
{
Console.WriteLine("Try");
}
catch (FileNotFoundException ex)
{
ProjectData.SetProjectError(ex);
FileNotFoundException ex2 = ex;
Console.WriteLine(ex2.FileName);
ProjectData.ClearProjectError();
}
catch (IOException ex3)
{
ProjectData.SetProjectError(ex3);
IOException ex4 = ex3;
Console.WriteLine(ex4.Message);
ProjectData.ClearProjectError();
}
catch (Exception projectError)
{
ProjectData.SetProjectError(projectError);
Console.WriteLine("Catch");
ProjectData.ClearProjectError();
}
}
public static void TryMultipleCatchFinally()
{
try
{
Console.WriteLine("Try");
}
catch (FileNotFoundException ex)
{
ProjectData.SetProjectError(ex);
FileNotFoundException ex2 = ex;
Console.WriteLine(ex2.FileName);
ProjectData.ClearProjectError();
}
catch (IOException ex3)
{
ProjectData.SetProjectError(ex3);
IOException ex4 = ex3;
Console.WriteLine(ex4.Message);
ProjectData.ClearProjectError();
}
finally
{
Console.WriteLine("Finally");
}
}
public static void TryMultipleCatchWhen()
{
try
{
Console.WriteLine("Try");
}
catch (IOException ex) when (((Func<bool>)delegate {
// Could not convert BlockContainer to single expression
ProjectData.SetProjectError(ex);
return Condition();
}).Invoke())
{
Console.WriteLine(ex.Message);
ProjectData.ClearProjectError();
}
catch (Exception projectError) when (((Func<bool>)delegate {
// Could not convert BlockContainer to single expression
ProjectData.SetProjectError(projectError);
return Condition();
}).Invoke())
{
Console.WriteLine("Catch When");
ProjectData.ClearProjectError();
}
catch (Exception ex2)
{
ProjectData.SetProjectError(ex2);
Exception ex3 = ex2;
Console.WriteLine(ex3.Message);
ProjectData.ClearProjectError();
}
}
public static void EmptyTryFinally()
{
try
{
}
finally
{
Console.WriteLine("Finally");
}
}
public static void EmptyCatch()
{
try
{
Console.WriteLine("Try");
}
catch (Exception projectError)
{
ProjectData.SetProjectError(projectError);
ProjectData.ClearProjectError();
}
}
public static void EmptyFinally()
{
try
{
Console.WriteLine("Try");
}
catch (Exception ex)
{
ProjectData.SetProjectError(ex);
Exception ex2 = ex;
Console.WriteLine(ex2.Message);
ProjectData.ClearProjectError();
}
#if !OPT || LEGACY_VBC
finally
{
}
#endif
}
public static void ExitTryInTry(bool b)
{
try
{
if (!b)
{
Console.WriteLine("Try");
}
}
catch (Exception projectError)
{
ProjectData.SetProjectError(projectError);
Console.WriteLine("Catch");
ProjectData.ClearProjectError();
}
Console.WriteLine("End");
}
public static void ExitTryInCatch(bool b)
{
try
{
Console.WriteLine("Try");
}
catch (Exception projectError)
{
ProjectData.SetProjectError(projectError);
if (b)
{
ProjectData.ClearProjectError();
}
else
{
Console.WriteLine("Catch");
ProjectData.ClearProjectError();
}
}
Console.WriteLine("End");
}
public static void ExitTryWithFinally(bool b)
{
try
{
if (!b)
{
Console.WriteLine("Try");
}
}
finally
{
Console.WriteLine("Finally");
}
Console.WriteLine("End");
}
public static void Rethrow()
{
try
{
Console.WriteLine("Try");
}
catch (Exception ex)
{
ProjectData.SetProjectError(ex);
Exception ex2 = ex;
Console.WriteLine(ex2.Message);
throw;
}
}
public static void ThrowNew()
{
try
{
Console.WriteLine("Try");
}
catch (Exception ex)
{
ProjectData.SetProjectError(ex);
Exception innerException = ex;
throw new InvalidOperationException("Catch", innerException);
}
}
public static int ReturnFromTry()
{
int result;
try
{
result = 1;
}
catch (Exception projectError)
{
ProjectData.SetProjectError(projectError);
result = 2;
ProjectData.ClearProjectError();
}
finally
{
Console.WriteLine("Finally");
}
return result;
}
public static int ReturnAfterTry()
{
#if !(LEGACY_VBC && OPT)
int result;
#endif
try
{
Console.WriteLine("Try");
}
catch (Exception ex)
{
ProjectData.SetProjectError(ex);
Exception ex2 = ex;
Console.WriteLine(ex2.Message);
#if LEGACY_VBC && OPT
int result = -1;
ProjectData.ClearProjectError();
return result;
}
return 0;
#else
result = -1;
ProjectData.ClearProjectError();
#if LEGACY_VBC
goto IL_0032;
#elif OPT
goto IL_0029;
#else
goto IL_0031;
#endif
}
result = 0;
#if LEGACY_VBC
goto IL_0032;
IL_0032:
#elif OPT
goto IL_0029;
IL_0029:
#else
goto IL_0031;
IL_0031:
#endif
return result;
#endif
}
public static void NestedTryInTry()
{
try
{
try
{
Console.WriteLine("Inner Try");
}
catch (IOException ex)
{
ProjectData.SetProjectError(ex);
IOException ex2 = ex;
Console.WriteLine(ex2.Message);
ProjectData.ClearProjectError();
}
}
catch (Exception ex3)
{
ProjectData.SetProjectError(ex3);
Exception ex4 = ex3;
Console.WriteLine(ex4.Message);
ProjectData.ClearProjectError();
}
}
public static void NestedTryInCatch()
{
try
{
Console.WriteLine("Try");
}
catch (Exception ex)
{
ProjectData.SetProjectError(ex);
Exception ex2 = ex;
try
{
Console.WriteLine(ex2.Message);
}
catch (Exception ex3)
{
ProjectData.SetProjectError(ex3);
Exception ex4 = ex3;
Console.WriteLine(ex4.Message);
ProjectData.ClearProjectError();
}
ProjectData.ClearProjectError();
}
}
public static void NestedTryInFinally()
{
try
{
Console.WriteLine("Try");
}
finally
{
try
{
Console.WriteLine("Inner Try");
}
catch (Exception projectError)
{
ProjectData.SetProjectError(projectError);
Console.WriteLine("Inner Catch");
ProjectData.ClearProjectError();
}
}
}
public static void TryInLoop()
{
int num = 0;
do
{
try
{
switch (num)
{
case 5:
break;
case 8:
return;
default:
Console.WriteLine(num);
break;
}
}
catch (Exception projectError)
{
ProjectData.SetProjectError(projectError);
Console.WriteLine("Catch");
ProjectData.ClearProjectError();
}
finally
{
Console.WriteLine("Finally");
}
num = checked(num + 1);
} while (num <= 9);
}
}

308
ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBTryCatchFinally.vb

@ -0,0 +1,308 @@ @@ -0,0 +1,308 @@
Imports System
Imports System.IO
Public Class VBTryCatchFinally
Private Shared Function Condition() As Boolean
Return True
End Function
Public Shared Sub TryFinally()
Try
Console.WriteLine("Try")
Finally
Console.WriteLine("Finally")
End Try
End Sub
Public Shared Sub TryCatchBare()
Try
Console.WriteLine("Try")
Catch
Console.WriteLine("Catch")
End Try
End Sub
Public Shared Sub TryCatchVariable()
Try
Console.WriteLine("Try")
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Public Shared Sub TryCatchSpecificType()
Try
Console.WriteLine("Try")
Catch ex As IOException
Console.WriteLine(ex.Message)
End Try
End Sub
Public Shared Sub TryCatchUnusedVariable()
Try
Console.WriteLine("Try")
Catch ex As InvalidOperationException
Console.WriteLine("Catch")
End Try
End Sub
Public Shared Sub TryCatchWhen()
Try
Console.WriteLine("Try")
Catch When Condition()
Console.WriteLine("Catch")
End Try
End Sub
Public Shared Sub TryCatchVariableWhen()
Try
Console.WriteLine("Try")
Catch ex As Exception When ex.Message IsNot Nothing
Console.WriteLine(ex.Message)
End Try
End Sub
Public Shared Sub TryCatchSpecificTypeWhen()
Try
Console.WriteLine("Try")
Catch ex As IOException When Condition()
Console.WriteLine(ex.Message)
End Try
End Sub
Public Shared Sub TryCatchExistingLocal()
Dim ex As Exception = Nothing
Try
Console.WriteLine("Try")
Catch ex
Console.WriteLine("Catch")
End Try
Console.WriteLine(ex)
End Sub
Public Shared Sub TryCatchExistingLocalWhen()
Dim ex As Exception = Nothing
Try
Console.WriteLine("Try")
Catch ex When ex.Message IsNot Nothing
Console.WriteLine("Catch")
End Try
Console.WriteLine(ex)
End Sub
Public Shared Sub TryCatchFinally()
Try
Console.WriteLine("Try")
Catch ex As Exception
Console.WriteLine(ex.Message)
Finally
Console.WriteLine("Finally")
End Try
End Sub
Public Shared Sub TryCatchBareFinally()
Try
Console.WriteLine("Try")
Catch
Console.WriteLine("Catch")
Finally
Console.WriteLine("Finally")
End Try
End Sub
Public Shared Sub TryCatchWhenFinally()
Try
Console.WriteLine("Try")
Catch ex As Exception When Condition()
Console.WriteLine(ex.Message)
Finally
Console.WriteLine("Finally")
End Try
End Sub
Public Shared Sub TryMultipleCatch()
Try
Console.WriteLine("Try")
Catch ex As FileNotFoundException
Console.WriteLine(ex.FileName)
Catch ex As IOException
Console.WriteLine(ex.Message)
Catch
Console.WriteLine("Catch")
End Try
End Sub
Public Shared Sub TryMultipleCatchFinally()
Try
Console.WriteLine("Try")
Catch ex As FileNotFoundException
Console.WriteLine(ex.FileName)
Catch ex As IOException
Console.WriteLine(ex.Message)
Finally
Console.WriteLine("Finally")
End Try
End Sub
Public Shared Sub TryMultipleCatchWhen()
Try
Console.WriteLine("Try")
Catch ex As IOException When Condition()
Console.WriteLine(ex.Message)
Catch When Condition()
Console.WriteLine("Catch When")
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Public Shared Sub EmptyTryFinally()
Try
Finally
Console.WriteLine("Finally")
End Try
End Sub
Public Shared Sub EmptyCatch()
Try
Console.WriteLine("Try")
Catch
End Try
End Sub
Public Shared Sub EmptyFinally()
Try
Console.WriteLine("Try")
Catch ex As Exception
Console.WriteLine(ex.Message)
Finally
End Try
End Sub
Public Shared Sub ExitTryInTry(b As Boolean)
Try
If b Then
Exit Try
End If
Console.WriteLine("Try")
Catch
Console.WriteLine("Catch")
End Try
Console.WriteLine("End")
End Sub
Public Shared Sub ExitTryInCatch(b As Boolean)
Try
Console.WriteLine("Try")
Catch
If b Then
Exit Try
End If
Console.WriteLine("Catch")
End Try
Console.WriteLine("End")
End Sub
Public Shared Sub ExitTryWithFinally(b As Boolean)
Try
If b Then
Exit Try
End If
Console.WriteLine("Try")
Finally
Console.WriteLine("Finally")
End Try
Console.WriteLine("End")
End Sub
Public Shared Sub Rethrow()
Try
Console.WriteLine("Try")
Catch ex As Exception
Console.WriteLine(ex.Message)
Throw
End Try
End Sub
Public Shared Sub ThrowNew()
Try
Console.WriteLine("Try")
Catch ex As Exception
Throw New InvalidOperationException("Catch", ex)
End Try
End Sub
Public Shared Function ReturnFromTry() As Integer
Try
Return 1
Catch
Return 2
Finally
Console.WriteLine("Finally")
End Try
End Function
Public Shared Function ReturnAfterTry() As Integer
Try
Console.WriteLine("Try")
Catch ex As Exception
Console.WriteLine(ex.Message)
Return -1
End Try
Return 0
End Function
Public Shared Sub NestedTryInTry()
Try
Try
Console.WriteLine("Inner Try")
Catch ex As IOException
Console.WriteLine(ex.Message)
End Try
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Public Shared Sub NestedTryInCatch()
Try
Console.WriteLine("Try")
Catch ex As Exception
Try
Console.WriteLine(ex.Message)
Catch ex2 As Exception
Console.WriteLine(ex2.Message)
End Try
End Try
End Sub
Public Shared Sub NestedTryInFinally()
Try
Console.WriteLine("Try")
Finally
Try
Console.WriteLine("Inner Try")
Catch
Console.WriteLine("Inner Catch")
End Try
End Try
End Sub
Public Shared Sub TryInLoop()
For i = 0 To 9
Try
If i = 5 Then
Continue For
End If
If i = 8 Then
Exit For
End If
Console.WriteLine(i)
Catch
Console.WriteLine("Catch")
Finally
Console.WriteLine("Finally")
End Try
Next
End Sub
End Class

20
ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs

@ -132,6 +132,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -132,6 +132,12 @@ namespace ICSharpCode.Decompiler.Tests
await Run(options: options | CompilerOptions.Library);
}
[Test]
public async Task Issue3659([ValueSource(nameof(defaultOptions))] CompilerOptions options)
{
await Run(options: options | CompilerOptions.Library);
}
[Test]
public async Task Issue1906([ValueSource(nameof(defaultOptions))] CompilerOptions options)
{
@ -145,6 +151,13 @@ namespace ICSharpCode.Decompiler.Tests @@ -145,6 +151,13 @@ namespace ICSharpCode.Decompiler.Tests
await Run(options: options | CompilerOptions.Library);
}
[Test]
public async Task VBOnError([ValueSource(nameof(defaultOptions))] CompilerOptions options)
{
IgnoreIfVbRuntimeSubstituted(options);
await Run(options: options | CompilerOptions.Library);
}
[Test]
public async Task VBPropertiesTest([ValueSource(nameof(defaultOptions))] CompilerOptions options)
{
@ -164,6 +177,13 @@ namespace ICSharpCode.Decompiler.Tests @@ -164,6 +177,13 @@ namespace ICSharpCode.Decompiler.Tests
await Run(options: options | CompilerOptions.Library);
}
[Test]
public async Task VBTryCatchFinally([ValueSource(nameof(defaultOptions))] CompilerOptions options)
{
IgnoreIfVbRuntimeSubstituted(options);
await Run(options: options | CompilerOptions.Library);
}
[Test]
public async Task YieldReturn([ValueSource(nameof(defaultOptions))] CompilerOptions options)
{

19
ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using ICSharpCode.Decompiler.TypeSystem;
@ -467,6 +468,24 @@ namespace ICSharpCode.Decompiler.IL @@ -467,6 +468,24 @@ namespace ICSharpCode.Decompiler.IL
}
}
/// <summary>
/// For <paramref name="kind"/> == Equality, matches 'comp(arg == ldc.i4 0)' or 'comp.unsigned(arg &lt;= ldc.i4 0)'.
/// For <paramref name="kind"/> == Inequality, matches 'comp(arg != ldc.i4 0)' or 'comp.unsigned(arg > ldc.i4 0)'.
/// </summary>
public bool MatchCompUnsignedZero(ComparisonKind kind, [NotNullWhen(true)] out ILInstruction? arg)
{
Debug.Assert(kind.IsEqualityOrInequality());
var unsignedKind = kind == ComparisonKind.Equality ? ComparisonKind.LessThanOrEqual : ComparisonKind.GreaterThan;
if (this is Comp { IsLifted: false } comp && comp.Right.MatchLdcI4(0)
&& (comp.Kind == kind || (comp.Kind == unsignedKind && comp.Sign == Sign.Unsigned)))
{
arg = comp.Left;
return true;
}
arg = null;
return false;
}
public bool MatchLdFld([NotNullWhen(true)] out ILInstruction? target, [NotNullWhen(true)] out IField? field)
{
if (this is LdObj ldobj && ldobj.Target is LdFlda ldflda && ldobj.UnalignedPrefix == 0 && !ldobj.IsVolatile)

65
ICSharpCode.Decompiler/IL/Transforms/DetectCatchWhenConditionBlocks.cs

@ -69,11 +69,67 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -69,11 +69,67 @@ namespace ICSharpCode.Decompiler.IL.Transforms
container.SortBlocks(deleteUnreachableBlocks: true);
}
PropagateExceptionVariable(context, catchBlock);
}
else if (catchBlock.Filter is BlockContainer filterContainer
&& MatchVBOnErrorCatchFilter(context, catchBlock.Variable, filterContainer, out exceptionType, out var typeTest)
&& exceptionType.GetStackType() == catchBlock.Variable.StackType)
{
context.Step($"Detected catch-when for {catchBlock.Variable.Name} (bit.and)", typeTest);
catchBlock.Variable.Type = exceptionType;
var condition = typeTest.Right;
typeTest.ReplaceWith(condition);
context.EndStep(condition);
PropagateExceptionVariable(context, catchBlock);
}
}
}
/// <summary>
/// BlockContainer {
/// Block entryPoint (incoming: 1) {
/// leave container(bit.and(bit.and(comp(isinst System.Exception(ldloc exceptionVar) > ldnull), comp.unsigned(ldloc activeHandler > ldc.i4 0)), logic.not(ldloc resumeTarget)))
/// }
/// }
/// Only emitted for On Error Resume Next/GoTo.
/// </summary>
bool MatchVBOnErrorCatchFilter(ILTransformContext context, ILVariable exceptionVar, BlockContainer container, out IType exceptionType, out BinaryNumericInstruction typeTest)
{
exceptionType = null;
typeTest = null;
var entryPoint = container.EntryPoint;
if (entryPoint == null || entryPoint.IncomingEdgeCount != 1 || entryPoint.Instructions.Count != 1)
return false;
if (!entryPoint.Instructions[0].MatchLeave(container, out var condition))
return false;
if (condition is not BinaryNumericInstruction { Operator: BinaryNumericOperator.BitAnd, Left: BinaryNumericInstruction { Operator: BinaryNumericOperator.BitAnd } bitAnd } outer)
return false;
if (!outer.Right.MatchCompUnsignedZero(ComparisonKind.Equality, out var resumeTarget) || !resumeTarget.MatchLdLoc(out _))
return false;
if (!bitAnd.Right.MatchCompUnsignedZero(ComparisonKind.Inequality, out var activeHandler) || !activeHandler.MatchLdLoc(out _))
return false;
if (bitAnd.Left is not Comp comp)
return false;
EarlyExpressionTransforms.FixComparisonKindLdNull(comp, context);
if (!MatchIsInstNotNull(comp, exceptionVar, out _, out exceptionType) || !exceptionType.IsKnownType(KnownTypeCode.Exception))
return false;
typeTest = bitAnd;
return true;
}
/// <summary>
/// comp(isinst exceptionType(ldloc exceptionVar) != ldnull)
/// </summary>
static bool MatchIsInstNotNull(ILInstruction condition, ILVariable exceptionVar, out ILInstruction exceptionSlot, out IType exceptionType)
{
exceptionSlot = null;
exceptionType = null;
return condition.MatchCompNotEqualsNull(out var arg)
&& arg.MatchIsInst(out exceptionSlot, out exceptionType)
&& exceptionSlot.MatchLdLoc(exceptionVar);
}
/// <summary>
/// catch E_189 : 0200007C System.Exception when (BlockContainer {
/// Block IL_0079 (incoming: 1) {
@ -195,19 +251,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -195,19 +251,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// br falseBlock
if (!entryPoint.Instructions[0].MatchIfInstruction(out var condition, out var branch))
return false;
if (!condition.MatchCompNotEquals(out var left, out var right))
if (!MatchIsInstNotNull(condition, exceptionVar, out exceptionSlot, out exceptionType))
return false;
if (!entryPoint.Instructions[1].MatchBranch(out var falseBlock) || !MatchFalseBlock(container, falseBlock, out var returnVar, out var exitBlock))
return false;
if (!left.MatchIsInst(out exceptionSlot, out exceptionType))
return false;
if (!exceptionSlot.MatchLdLoc(exceptionVar))
return false;
if (right.MatchLdNull())
{
return branch.MatchBranch(out whenConditionBlock);
}
}
return false;
}

Loading…
Cancel
Save