Browse Source

Add VBPretty test case for VB yield return decompilation

pull/2874/head
ElektroKill 2 years ago
parent
commit
8813d254f8
No known key found for this signature in database
GPG Key ID: 7E3C5C084E40E3EC
  1. 2
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 425
      ICSharpCode.Decompiler.Tests/TestCases/VBPretty/YieldReturn.cs
  3. 300
      ICSharpCode.Decompiler.Tests/TestCases/VBPretty/YieldReturn.vb
  4. 6
      ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs

2
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -117,6 +117,8 @@
<Compile Include="TestCases\VBPretty\VBAutomaticEvents.cs" /> <Compile Include="TestCases\VBPretty\VBAutomaticEvents.cs" />
<Compile Include="TestCases\VBPretty\VBNonGenericForEach.cs" /> <Compile Include="TestCases\VBPretty\VBNonGenericForEach.cs" />
<None Include="TestCases\VBPretty\VBNonGenericForEach.vb" /> <None Include="TestCases\VBPretty\VBNonGenericForEach.vb" />
<Compile Include="TestCases\VBPretty\YieldReturn.cs" />
<None Include="TestCases\VBPretty\YieldReturn.vb" />
<Compile Include="TypeSystem\ReflectionHelperTests.cs" /> <Compile Include="TypeSystem\ReflectionHelperTests.cs" />
<None Include="TestCases\Pretty\MetadataAttributes.cs" /> <None Include="TestCases\Pretty\MetadataAttributes.cs" />
<None Include="TestCases\Correctness\ComInterop.cs" /> <None Include="TestCases\Correctness\ComInterop.cs" />

425
ICSharpCode.Decompiler.Tests/TestCases/VBPretty/YieldReturn.cs

@ -0,0 +1,425 @@
using System;
using System.Collections.Generic;
#if LEGACY_VBC
using System.Diagnostics;
#endif
using Microsoft.VisualBasic.CompilerServices;
namespace ICSharpCode.Decompiler.Tests.TestCases.VBPretty
{
internal struct StructWithYieldReturn
{
private int val;
public IEnumerable<int> Count()
{
yield return val;
yield return val;
}
}
public class YieldReturnPrettyTest
{
private int fieldOnThis;
#if LEGACY_VBC
[DebuggerStepThrough]
#endif
public static IEnumerable<char> YieldChars {
get {
yield return 'a';
yield return 'b';
yield return 'c';
}
}
internal static void Print<T>(string name, IEnumerator<T> enumerator)
{
Console.WriteLine(name + ": Test start");
while (enumerator.MoveNext())
{
Console.WriteLine(name + ": " + enumerator.Current.ToString());
}
}
public static IEnumerable<string> SimpleYieldReturn()
{
yield return "A";
yield return "B";
yield return "C";
}
public static IEnumerator<string> SimpleYieldReturnEnumerator()
{
yield return "A";
yield return "B";
yield return "C";
}
public IEnumerable<int> YieldReturnParameters(int p)
{
yield return p;
yield return fieldOnThis;
}
public IEnumerator<int> YieldReturnParametersEnumerator(int p)
{
yield return p;
yield return fieldOnThis;
}
public static IEnumerable<int> YieldReturnInLoop()
{
int num = 0;
do
{
yield return num;
num = checked(num + 1);
} while (num <= 99);
}
public static IEnumerable<int> YieldReturnWithTryFinally()
{
yield return 0;
try
{
yield return 1;
}
finally
{
Console.WriteLine("Finally!");
}
yield return 2;
}
public static IEnumerable<string> YieldReturnWithNestedTryFinally(bool breakInMiddle)
{
Console.WriteLine("Start of method - 1");
yield return "Start of method";
Console.WriteLine("Start of method - 2");
try
{
Console.WriteLine("Within outer try - 1");
yield return "Within outer try";
Console.WriteLine("Within outer try - 2");
try
{
Console.WriteLine("Within inner try - 1");
yield return "Within inner try";
Console.WriteLine("Within inner try - 2");
if (breakInMiddle)
{
Console.WriteLine("Breaking...");
yield break;
}
Console.WriteLine("End of inner try - 1");
yield return "End of inner try";
Console.WriteLine("End of inner try - 2");
}
finally
{
Console.WriteLine("Inner Finally");
}
Console.WriteLine("End of outer try - 1");
yield return "End of outer try";
Console.WriteLine("End of outer try - 2");
}
finally
{
Console.WriteLine("Outer Finally");
}
Console.WriteLine("End of method - 1");
yield return "End of method";
Console.WriteLine("End of method - 2");
}
public static IEnumerable<string> YieldReturnWithTwoNonNestedFinallyBlocks(IEnumerable<string> input)
{
// outer try-finally block
foreach (string item in input)
{
// nested try-finally block
try
{
yield return item;
}
finally
{
Console.WriteLine("Processed " + item);
}
}
yield return "A";
yield return "B";
yield return "C";
yield return "D";
yield return "E";
yield return "F";
// outer try-finally block
foreach (string item2 in input)
{
yield return item2.ToUpper();
}
}
public static IEnumerable<int> GetEvenNumbers(int n)
{
int num = checked(n - 1);
for (int i = 0; i <= num; i = checked(i + 1))
{
if (i % 2 == 0)
{
yield return i;
}
}
}
public static IEnumerable<char> ExceptionHandling()
{
yield return 'a';
try
{
Console.WriteLine("1 - try");
}
catch (Exception projectError)
{
ProjectData.SetProjectError(projectError);
Console.WriteLine("1 - catch");
ProjectData.ClearProjectError();
}
yield return 'b';
try
{
try
{
Console.WriteLine("2 - try");
}
finally
{
Console.WriteLine("2 - finally");
}
yield return 'c';
}
finally
{
Console.WriteLine("outer finally");
}
}
public static IEnumerable<int> YieldBreakInCatch()
{
yield return 0;
try
{
Console.WriteLine("In Try");
}
catch (Exception projectError)
{
ProjectData.SetProjectError(projectError);
ProjectData.ClearProjectError();
// yield return is not allowed in catch, but yield break is
yield break;
}
yield return 1;
}
public static IEnumerable<int> YieldBreakInCatchInTryFinally()
{
try
{
yield return 0;
try
{
Console.WriteLine("In Try");
}
catch (Exception projectError)
{
ProjectData.SetProjectError(projectError);
ProjectData.ClearProjectError();
// yield return is not allowed in catch, but yield break is
// Note that pre-roslyn, this code triggers a compiler bug:
// If the finally block throws an exception, it ends up getting
// called a second time.
yield break;
}
yield return 1;
}
finally
{
Console.WriteLine("Finally");
}
}
public static IEnumerable<int> YieldBreakInTryCatchInTryFinally()
{
try
{
yield return 0;
try
{
Console.WriteLine("In Try");
// same compiler bug as in YieldBreakInCatchInTryFinally
yield break;
}
catch (Exception projectError)
{
ProjectData.SetProjectError(projectError);
Console.WriteLine("Catch");
ProjectData.ClearProjectError();
}
yield return 1;
}
finally
{
Console.WriteLine("Finally");
}
}
public static IEnumerable<int> YieldBreakInTryFinallyInTryFinally(bool b)
{
try
{
yield return 0;
try
{
Console.WriteLine("In Try");
if (b)
{
// same compiler bug as in YieldBreakInCatchInTryFinally
yield break;
}
}
finally
{
Console.WriteLine("Inner Finally");
}
yield return 1;
}
finally
{
Console.WriteLine("Finally");
}
}
public static IEnumerable<int> YieldBreakOnly()
{
yield break;
}
public static IEnumerable<int> UnconditionalThrowInTryFinally()
{
// Here, MoveNext() doesn't call the finally methods at all
// (only indirectly via Dispose())
try
{
yield return 0;
throw new NotImplementedException();
}
finally
{
Console.WriteLine("Finally");
}
}
public static IEnumerable<int> NestedTryFinallyStartingOnSamePosition()
{
// The first user IL instruction is already in 2 nested try blocks.
#if ((ROSLYN2 && !ROSLYN4) && OPT)
int num = -1;
#endif
try
{
#if ((ROSLYN2 && !ROSLYN4) && OPT)
_ = num - 1;
#endif
try
{
yield return 0;
}
finally
{
Console.WriteLine("Inner Finally");
}
}
finally
{
Console.WriteLine("Outer Finally");
}
}
#if ROSLYN
public static IEnumerable<int> LocalInFinally<T>(T a) where T : IDisposable
{
yield return 1;
try
{
yield return 2;
}
finally
{
T val = a;
val.Dispose();
val.Dispose();
}
yield return 3;
}
#endif
public static IEnumerable<T> GenericYield<T>() where T : new()
{
T val = new T();
int num = 0;
do
{
yield return val;
num = checked(num + 1);
} while (num <= 2);
}
public static IEnumerable<int> MultipleYieldBreakInTryFinally(int i)
{
try
{
if (i == 2)
{
yield break;
}
while (i < 40)
{
if (i % 2 == 0)
{
yield break;
}
i = checked(i + 1);
yield return i;
}
}
finally
{
Console.WriteLine("finally");
}
Console.WriteLine("normal exit");
}
internal IEnumerable<int> ForLoopWithYieldReturn(int end, int evil)
{
// This loop needs to pick the implicit "yield break;" as exit point
// in order to produce pretty code; not the "throw" which would
// be a less-pretty option.
checked
{
int num = end - 1;
for (int i = 0; i <= num; i++)
{
if (i == evil)
{
throw new InvalidOperationException("Found evil number");
}
yield return i;
}
}
}
}
}

300
ICSharpCode.Decompiler.Tests/TestCases/VBPretty/YieldReturn.vb

@ -0,0 +1,300 @@
Imports System
Imports System.Collections.Generic
Namespace ICSharpCode.Decompiler.Tests.TestCases.VBPretty
Friend Structure StructWithYieldReturn
Private val As Integer
Public Iterator Function Count() As IEnumerable(Of Integer)
Yield val
Yield val
End Function
End Structure
Public Class YieldReturnPrettyTest
Private fieldOnThis As Integer
Public Shared ReadOnly Iterator Property YieldChars As IEnumerable(Of Char)
Get
Yield "a"c
Yield "b"c
Yield "c"c
End Get
End Property
Friend Shared Sub Print(Of T)(ByVal name As String, ByVal enumerator As IEnumerator(Of T))
Console.WriteLine(name + ": Test start")
While enumerator.MoveNext()
Console.WriteLine(name + ": " + enumerator.Current.ToString())
End While
End Sub
Public Shared Iterator Function SimpleYieldReturn() As IEnumerable(Of String)
Yield "A"
Yield "B"
Yield "C"
End Function
Public Shared Iterator Function SimpleYieldReturnEnumerator() As IEnumerator(Of String)
Yield "A"
Yield "B"
Yield "C"
End Function
Public Iterator Function YieldReturnParameters(ByVal p As Integer) As IEnumerable(Of Integer)
Yield p
Yield fieldOnThis
End Function
Public Iterator Function YieldReturnParametersEnumerator(ByVal p As Integer) As IEnumerator(Of Integer)
Yield p
Yield fieldOnThis
End Function
Public Shared Iterator Function YieldReturnInLoop() As IEnumerable(Of Integer)
For i = 0 To 99
Yield i
Next
End Function
Public Shared Iterator Function YieldReturnWithTryFinally() As IEnumerable(Of Integer)
Yield 0
Try
Yield 1
Finally
Console.WriteLine("Finally!")
End Try
Yield 2
End Function
Public Shared Iterator Function YieldReturnWithNestedTryFinally(ByVal breakInMiddle As Boolean) As IEnumerable(Of String)
Console.WriteLine("Start of method - 1")
Yield "Start of method"
Console.WriteLine("Start of method - 2")
Try
Console.WriteLine("Within outer try - 1")
Yield "Within outer try"
Console.WriteLine("Within outer try - 2")
Try
Console.WriteLine("Within inner try - 1")
Yield "Within inner try"
Console.WriteLine("Within inner try - 2")
If breakInMiddle Then
Console.WriteLine("Breaking...")
Return
End If
Console.WriteLine("End of inner try - 1")
Yield "End of inner try"
Console.WriteLine("End of inner try - 2")
Finally
Console.WriteLine("Inner Finally")
End Try
Console.WriteLine("End of outer try - 1")
Yield "End of outer try"
Console.WriteLine("End of outer try - 2")
Finally
Console.WriteLine("Outer Finally")
End Try
Console.WriteLine("End of method - 1")
Yield "End of method"
Console.WriteLine("End of method - 2")
End Function
Public Shared Iterator Function YieldReturnWithTwoNonNestedFinallyBlocks(ByVal input As IEnumerable(Of String)) As IEnumerable(Of String)
' outer try-finally block
For Each line In input
' nested try-finally block
Try
Yield line
Finally
Console.WriteLine("Processed " & line)
End Try
Next
Yield "A"
Yield "B"
Yield "C"
Yield "D"
Yield "E"
Yield "F"
' outer try-finally block
For Each item In input
Yield item.ToUpper()
Next
End Function
Public Shared Iterator Function GetEvenNumbers(ByVal n As Integer) As IEnumerable(Of Integer)
For i = 0 To n - 1
If i Mod 2 = 0 Then
Yield i
End If
Next
End Function
Public Shared Iterator Function ExceptionHandling() As IEnumerable(Of Char)
Yield "a"c
Try
Console.WriteLine("1 - try")
Catch __unusedException1__ As Exception
Console.WriteLine("1 - catch")
End Try
Yield "b"c
Try
Try
Console.WriteLine("2 - try")
Finally
Console.WriteLine("2 - finally")
End Try
Yield "c"c
Finally
Console.WriteLine("outer finally")
End Try
End Function
Public Shared Iterator Function YieldBreakInCatch() As IEnumerable(Of Integer)
Yield 0
Try
Console.WriteLine("In Try")
Catch
' yield return is not allowed in catch, but yield break is
Return
End Try
Yield 1
End Function
Public Shared Iterator Function YieldBreakInCatchInTryFinally() As IEnumerable(Of Integer)
Try
Yield 0
Try
Console.WriteLine("In Try")
Catch
' yield return is not allowed in catch, but yield break is
' Note that pre-roslyn, this code triggers a compiler bug:
' If the finally block throws an exception, it ends up getting
' called a second time.
Return
End Try
Yield 1
Finally
Console.WriteLine("Finally")
End Try
End Function
Public Shared Iterator Function YieldBreakInTryCatchInTryFinally() As IEnumerable(Of Integer)
Try
Yield 0
Try
Console.WriteLine("In Try")
' same compiler bug as in YieldBreakInCatchInTryFinally
Return
Catch
Console.WriteLine("Catch")
End Try
Yield 1
Finally
Console.WriteLine("Finally")
End Try
End Function
Public Shared Iterator Function YieldBreakInTryFinallyInTryFinally(ByVal b As Boolean) As IEnumerable(Of Integer)
Try
Yield 0
Try
Console.WriteLine("In Try")
If b Then
' same compiler bug as in YieldBreakInCatchInTryFinally
Return
End If
Finally
Console.WriteLine("Inner Finally")
End Try
Yield 1
Finally
Console.WriteLine("Finally")
End Try
End Function
Public Shared Iterator Function YieldBreakOnly() As IEnumerable(Of Integer)
Return
End Function
Public Shared Iterator Function UnconditionalThrowInTryFinally() As IEnumerable(Of Integer)
' Here, MoveNext() doesn't call the finally methods at all
' (only indirectly via Dispose())
Try
Yield 0
Throw New NotImplementedException()
Finally
Console.WriteLine("Finally")
End Try
End Function
Public Shared Iterator Function NestedTryFinallyStartingOnSamePosition() As IEnumerable(Of Integer)
' The first user IL instruction is already in 2 nested try blocks.
Try
Try
Yield 0
Finally
Console.WriteLine("Inner Finally")
End Try
Finally
Console.WriteLine("Outer Finally")
End Try
End Function
#If ROSLYN Then
Public Shared Iterator Function LocalInFinally(Of T As IDisposable)(ByVal a As T) As IEnumerable(Of Integer)
Yield 1
Try
Yield 2
Finally
Dim val = a
val.Dispose()
val.Dispose()
End Try
Yield 3
End Function
#End If
Public Shared Iterator Function GenericYield(Of T As New)() As IEnumerable(Of T)
Dim val As T = New T()
For i = 0 To 2
Yield val
Next
End Function
Public Shared Iterator Function MultipleYieldBreakInTryFinally(ByVal i As Integer) As IEnumerable(Of Integer)
Try
If i = 2 Then
Return
End If
While i < 40
If i Mod 2 = 0 Then
Return
End If
i += 1
Yield i
End While
Finally
Console.WriteLine("finally")
End Try
Console.WriteLine("normal exit")
End Function
Friend Iterator Function ForLoopWithYieldReturn(ByVal [end] As Integer, ByVal evil As Integer) As IEnumerable(Of Integer)
' This loop needs to pick the implicit "yield break;" as exit point
' in order to produce pretty code; not the "throw" which would
' be a less-pretty option.
For i = 0 To [end] - 1
If i = evil Then
Throw New InvalidOperationException("Found evil number")
End If
Yield i
Next
End Function
End Class
End Namespace

6
ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs

@ -137,6 +137,12 @@ namespace ICSharpCode.Decompiler.Tests
await Run(options: options | CompilerOptions.Library); await Run(options: options | CompilerOptions.Library);
} }
[Test]
public async Task YieldReturn([ValueSource(nameof(defaultOptions))] CompilerOptions options)
{
await Run(options: options | CompilerOptions.Library);
}
async Task Run([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug, DecompilerSettings settings = null) async Task Run([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug, DecompilerSettings settings = null)
{ {
var vbFile = Path.Combine(TestCasePath, testName + ".vb"); var vbFile = Path.Combine(TestCasePath, testName + ".vb");

Loading…
Cancel
Save