Browse Source

Add pretty tests for "yield return" and fix some minor bugs:

* remove redundant casts on yield-return-expression
 * keep variable name from metadata for foreach loops
 * fix 'yield break;' in nested try-finally sometimes causing a decompiler error
pull/1317/head
Daniel Grunwald 7 years ago
parent
commit
e041454987
  1. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 82
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  3. 37
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/YieldReturn.cs
  4. 339
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs
  5. 7400
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.il
  6. 5897
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.opt.il
  7. 6068
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.opt.roslyn.il
  8. 6641
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.roslyn.il
  9. 8
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  10. 6
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  11. 3
      ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
  12. 1
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -74,6 +74,7 @@ @@ -74,6 +74,7 @@
<Compile Include="TestCases\Pretty\OptionalArguments.cs" />
<Compile Include="TestCases\Pretty\CustomShortCircuitOperators.cs" />
<Compile Include="TestCases\Pretty\TypeTests.cs" />
<Compile Include="TestCases\Pretty\YieldReturn.cs" />
<None Include="TestCases\ILPretty\Issue1256.il" />
<None Include="TestCases\Ugly\NoDecimalConstants.Expected.cs" />
<Compile Include="TestCases\Ugly\NoDecimalConstants.cs" />

82
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -85,31 +85,31 @@ namespace ICSharpCode.Decompiler.Tests @@ -85,31 +85,31 @@ namespace ICSharpCode.Decompiler.Tests
}
[Test]
public void InlineAssignmentTest([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void InlineAssignmentTest([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void CompoundAssignmentTest([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void CompoundAssignmentTest([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void ShortCircuit([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void ShortCircuit([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void CustomShortCircuitOperators([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void CustomShortCircuitOperators([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void ExceptionHandling([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void ExceptionHandling([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
NullPropagation = false,
@ -119,7 +119,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -119,7 +119,7 @@ namespace ICSharpCode.Decompiler.Tests
}
[Test]
public void Switch([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void Switch([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
// legacy csc generates a dead store in debug builds
@ -128,49 +128,49 @@ namespace ICSharpCode.Decompiler.Tests @@ -128,49 +128,49 @@ namespace ICSharpCode.Decompiler.Tests
}
[Test]
public void DelegateConstruction([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void DelegateConstruction([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void AnonymousTypes([ValueSource("defaultOptionsWithMcs")] CSharpCompilerOptions cscOptions)
public void AnonymousTypes([ValueSource(nameof(defaultOptionsWithMcs))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void Async([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void Async([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void Lock([ValueSource("defaultOptionsWithMcs")] CSharpCompilerOptions cscOptions)
public void Lock([ValueSource(nameof(defaultOptionsWithMcs))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void Using([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void Using([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void LiftedOperators([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void LiftedOperators([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void Generics([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void Generics([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void Loops([ValueSource("defaultOptionsWithMcs")] CSharpCompilerOptions cscOptions)
public void Loops([ValueSource(nameof(defaultOptionsWithMcs))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
// legacy csc generates a dead store in debug builds
@ -179,62 +179,62 @@ namespace ICSharpCode.Decompiler.Tests @@ -179,62 +179,62 @@ namespace ICSharpCode.Decompiler.Tests
}
[Test]
public void PropertiesAndEvents([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void PropertiesAndEvents([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void AutoProperties([ValueSource("roslynOnlyOptions")] CSharpCompilerOptions cscOptions)
public void AutoProperties([ValueSource(nameof(roslynOnlyOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void QueryExpressions([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void QueryExpressions([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void TypeAnalysisTests([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void TypeAnalysisTests([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void CheckedUnchecked([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void CheckedUnchecked([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void UnsafeCode([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void UnsafeCode([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void PInvoke([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void PInvoke([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
// This tests needs our own disassembler; ildasm has a bug with marshalinfo.
RunForLibrary(cscOptions: cscOptions, asmOptions: AssemblerOptions.UseOwnDisassembler);
}
[Test]
public void InitializerTests([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void InitializerTests([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void DynamicTests([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void DynamicTests([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void ExpressionTrees([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void ExpressionTrees([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
@ -246,73 +246,73 @@ namespace ICSharpCode.Decompiler.Tests @@ -246,73 +246,73 @@ namespace ICSharpCode.Decompiler.Tests
}
[Test]
public void VariableNaming([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void VariableNaming([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void VariableNamingWithoutSymbols([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void VariableNamingWithoutSymbols([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings { UseDebugSymbols = false });
}
[Test]
public void CS72_PrivateProtected([ValueSource("roslynOnlyOptions")] CSharpCompilerOptions cscOptions)
public void CS72_PrivateProtected([ValueSource(nameof(roslynOnlyOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void AsyncMain([ValueSource("roslynOnlyOptions")] CSharpCompilerOptions cscOptions)
public void AsyncMain([ValueSource(nameof(roslynOnlyOptions))] CSharpCompilerOptions cscOptions)
{
Run(cscOptions: cscOptions);
}
[Test]
public void NullPropagation([ValueSource("roslynOnlyOptions")] CSharpCompilerOptions cscOptions)
public void NullPropagation([ValueSource(nameof(roslynOnlyOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void CS6_StringInterpolation([ValueSource("roslynOnlyOptions")] CSharpCompilerOptions cscOptions)
public void CS6_StringInterpolation([ValueSource(nameof(roslynOnlyOptions))] CSharpCompilerOptions cscOptions)
{
Run(cscOptions: cscOptions);
}
[Test]
public void RefLocalsAndReturns([ValueSource("roslynOnlyOptions")] CSharpCompilerOptions cscOptions)
public void RefLocalsAndReturns([ValueSource(nameof(roslynOnlyOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void WellKnownConstants([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void WellKnownConstants([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void QualifierTests([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void QualifierTests([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void TupleTests([ValueSource("roslynOnlyOptions")] CSharpCompilerOptions cscOptions)
public void TupleTests([ValueSource(nameof(roslynOnlyOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void NamedArguments([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void NamedArguments([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void OptionalArguments([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void OptionalArguments([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
@ -348,13 +348,19 @@ namespace ICSharpCode.Decompiler.Tests @@ -348,13 +348,19 @@ namespace ICSharpCode.Decompiler.Tests
}
[Test]
public void MemberTests([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void MemberTests([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void TypeTests([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions)
public void TypeTests([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void YieldReturn([ValueSource(nameof(defaultOptions))] CSharpCompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}

37
ICSharpCode.Decompiler.Tests/TestCases/Correctness/YieldReturn.cs

@ -76,6 +76,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -76,6 +76,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
int fieldOnThis;
public static IEnumerable<char> YieldChars {
get {
yield return 'a';
yield return 'b';
yield return 'c';
}
}
public static IEnumerable<string> SimpleYieldReturn()
{
yield return "A";
@ -188,9 +196,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -188,9 +196,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
yield return "E";
yield return "F";
// outer try-finally block
foreach (string line in input)
foreach (string line in input) {
yield return line.ToUpper();
}
}
public static IEnumerable<Func<string>> YieldReturnWithAnonymousMethods1(IEnumerable<string> input)
{
@ -210,18 +219,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -210,18 +219,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
public static IEnumerable<int> GetEvenNumbers(int n)
{
for (int i = 0; i < n; i++) {
if (i % 2 == 0)
if (i % 2 == 0) {
yield return i;
}
}
public static IEnumerable<char> YieldChars
{
get {
yield return 'a';
yield return 'b';
yield return 'c';
}
}
@ -283,7 +284,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -283,7 +284,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
yield return 0;
try {
Console.WriteLine("In Try");
yield break; // same compiler bug as in YieldBreakInCatchInTryFinally
// same compiler bug as in YieldBreakInCatchInTryFinally
yield break;
} catch {
Console.WriteLine("Catch");
}
@ -299,8 +301,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -299,8 +301,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
yield return 0;
try {
Console.WriteLine("In Try");
if (b)
yield break; // same compiler bug as in YieldBreakInCatchInTryFinally
if (b) {
// same compiler bug as in YieldBreakInCatchInTryFinally
yield break;
}
} finally {
Console.WriteLine("Inner Finally");
}
@ -406,7 +410,6 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -406,7 +410,6 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
public static IEnumerable<int> TryFinallyWithTwoExitPointsInNestedCatch(bool b)
{
// The first user IL instruction is already in 2 nested try blocks.
try {
yield return 1;
try {
@ -434,9 +437,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -434,9 +437,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
try {
yield return 2;
} finally {
T b = a;
b.Dispose();
b.Dispose();
T val = a;
val.Dispose();
val.Dispose();
}
yield return 3;
}

339
ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs

@ -0,0 +1,339 @@ @@ -0,0 +1,339 @@
// 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.Collections.Generic;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class YieldReturnPrettyTest
{
private int fieldOnThis;
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);
}
}
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()
{
for (int i = 0; i < 100; i++) {
yield return i;
}
}
public static IEnumerable<int> YieldReturnWithTryFinally()
{
yield return 0;
try {
yield return 1;
} finally {
Console.WriteLine("Finally!");
}
yield return 2;
}
#if TODO
// TODO: adjust lock-pattern for this case
public static IEnumerable<int> YieldReturnInLock1(object o)
{
lock (o) {
yield return 1;
}
}
public static IEnumerable<int> YieldReturnInLock2(object o)
{
lock (o) {
yield return 1;
o = null;
yield return 2;
}
}
#endif
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 line in input) {
// nested try-finally block
try {
yield return line;
} finally {
Console.WriteLine("Processed " + line);
}
}
yield return "A";
yield return "B";
yield return "C";
yield return "D";
yield return "E";
yield return "F";
// outer try-finally block
foreach (string item in input) {
yield return item.ToUpper();
}
}
public static IEnumerable<Func<string>> YieldReturnWithAnonymousMethods1(IEnumerable<string> input)
{
foreach (string line in input) {
yield return () => line;
}
}
public static IEnumerable<Func<string>> YieldReturnWithAnonymousMethods2(IEnumerable<string> input)
{
foreach (string item in input) {
string copy = item;
yield return () => copy;
}
}
public static IEnumerable<int> GetEvenNumbers(int n)
{
for (int i = 0; i < n; i++) {
if (i % 2 == 0) {
yield return i;
}
}
}
public static IEnumerable<char> ExceptionHandling()
{
yield return 'a';
try {
Console.WriteLine("1 - try");
} catch (Exception) {
Console.WriteLine("1 - catch");
}
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 {
// 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 {
// 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 {
Console.WriteLine("Catch");
}
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.
try {
try {
yield return 0;
} finally {
Console.WriteLine("Inner Finally");
}
} finally {
Console.WriteLine("Outer Finally");
}
}
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;
}
public static IEnumerable<T> GenericYield<T>() where T : new()
{
T val = new T();
for (int i = 0; i < 3; i++) {
yield return val;
}
}
}
internal struct StructWithYieldReturn
{
private int val;
public IEnumerable<int> Count()
{
yield return val++;
yield return val++;
}
}
}

7400
ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.il

File diff suppressed because it is too large Load Diff

5897
ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.opt.il

File diff suppressed because it is too large Load Diff

6068
ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.opt.roslyn.il

File diff suppressed because it is too large Load Diff

6641
ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.roslyn.il

File diff suppressed because it is too large Load Diff

8
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -286,7 +286,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -286,7 +286,9 @@ namespace ICSharpCode.Decompiler.CSharp
return new YieldBreakStatement();
else if (!inst.Value.MatchNop()) {
IType targetType = currentFunction.IsAsync ? currentFunction.AsyncReturnType : currentFunction.ReturnType;
return new ReturnStatement(exprBuilder.Translate(inst.Value, typeHint: targetType).ConvertTo(targetType, exprBuilder, allowImplicitConversion: true));
var expr = exprBuilder.Translate(inst.Value, typeHint: targetType)
.ConvertTo(targetType, exprBuilder, allowImplicitConversion: true);
return new ReturnStatement(expr);
} else
return new ReturnStatement();
}
@ -311,8 +313,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -311,8 +313,10 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override Statement VisitYieldReturn(YieldReturn inst)
{
var elementType = currentFunction.ReturnType.GetElementTypeFromIEnumerable(typeSystem, true, out var isGeneric);
var expr = exprBuilder.Translate(inst.Value, typeHint: elementType)
.ConvertTo(elementType, exprBuilder, allowImplicitConversion: true);
return new YieldReturnStatement {
Expression = exprBuilder.Translate(inst.Value, typeHint: elementType).ConvertTo(elementType, exprBuilder)
Expression = expr
};
}

6
ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

@ -811,8 +811,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -811,8 +811,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
break;
case Leave leave:
if (leave.MatchReturn(out var value)) {
if (value.MatchLdLoc(out var v) && v.IsSingleDefinition
&& v.StoreInstructions.SingleOrDefault() is StLoc stloc) {
if (value.MatchLdLoc(out var v)
&& (v.Kind == VariableKind.Local || v.Kind == VariableKind.StackSlot)
&& v.StoreInstructions.Count == 1
&& v.StoreInstructions[0] is StLoc stloc) {
returnStores.Add(stloc);
value = stloc.Value;
}

3
ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs

@ -477,6 +477,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -477,6 +477,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
if (function == null)
throw new ArgumentNullException(nameof(function));
if (existingVariable != null && !existingVariable.HasGeneratedName) {
return existingVariable.Name;
}
var reservedVariableNames = CollectReservedVariableNames(function, existingVariable);
string baseName = GetNameFromInstruction(valueContext);

1
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -363,6 +363,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -363,6 +363,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// decide based on the target into which we are inlining
switch (next.OpCode) {
case OpCode.Leave:
case OpCode.YieldReturn:
return parent == next;
case OpCode.IfInstruction:
while (parent.MatchLogicNot(out _)) {

Loading…
Cancel
Save