Browse Source

Merge pull request #2874 from ElektroKill/vb-yield-return

pull/2944/head
Siegfried Pammer 2 years ago committed by GitHub
parent
commit
75149910de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 419
      ICSharpCode.Decompiler.Tests/TestCases/VBPretty/YieldReturn.cs
  3. 300
      ICSharpCode.Decompiler.Tests/TestCases/VBPretty/YieldReturn.vb
  4. 6
      ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs
  5. 8
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  6. 10
      ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs
  7. 8
      ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs
  8. 267
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  9. 7
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  10. 10
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

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

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

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

@ -0,0 +1,419 @@ @@ -0,0 +1,419 @@
using System;
using System.Collections.Generic;
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;
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 @@ @@ -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 @@ -137,6 +137,12 @@ namespace ICSharpCode.Decompiler.Tests
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)
{
var vbFile = Path.Combine(TestCasePath, testName + ".vb");

8
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -1804,6 +1804,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1804,6 +1804,14 @@ namespace ICSharpCode.Decompiler.CSharp
{
RemoveAttribute(entityDecl, KnownAttribute.DebuggerHidden);
}
if (function.StateMachineCompiledWithLegacyVisualBasic)
{
RemoveAttribute(entityDecl, KnownAttribute.DebuggerStepThrough);
if (function.Method?.IsAccessor == true && entityDecl.Parent is EntityDeclaration parentDecl)
{
RemoveAttribute(parentDecl, KnownAttribute.DebuggerStepThrough);
}
}
}
if (function.IsAsync)
{

10
ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// Copyright (c) 2012 AlphaSierraPapa for the SharpDevelop Team
// Copyright (c) 2012 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
@ -58,6 +58,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -58,6 +58,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
public CancellationToken CancellationToken;
readonly StateRangeAnalysisMode mode;
readonly IField? stateField;
readonly bool legacyVisualBasic;
readonly SymbolicEvaluationContext evalContext;
readonly Dictionary<Block, LongSet> ranges = new Dictionary<Block, LongSet>();
@ -67,10 +68,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -67,10 +68,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
internal ILVariable? doFinallyBodies;
internal ILVariable? skipFinallyBodies;
public StateRangeAnalysis(StateRangeAnalysisMode mode, IField? stateField, ILVariable? cachedStateVar = null)
public StateRangeAnalysis(StateRangeAnalysisMode mode, IField? stateField, ILVariable? cachedStateVar = null, bool legacyVisualBasic = false)
{
this.mode = mode;
this.stateField = stateField;
this.legacyVisualBasic = legacyVisualBasic;
if (mode == StateRangeAnalysisMode.IteratorDispose)
{
finallyMethodToStateRange = new Dictionary<IMethod, LongSet>();
@ -80,7 +82,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -80,7 +82,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
rangesForLeave = new Dictionary<BlockContainer, LongSet>();
}
evalContext = new SymbolicEvaluationContext(stateField);
evalContext = new SymbolicEvaluationContext(stateField, legacyVisualBasic);
if (cachedStateVar != null)
evalContext.AddStateVariable(cachedStateVar);
}
@ -94,7 +96,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -94,7 +96,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// </summary>
internal StateRangeAnalysis CreateNestedAnalysis()
{
var sra = new StateRangeAnalysis(mode, stateField);
var sra = new StateRangeAnalysis(mode, stateField, legacyVisualBasic: legacyVisualBasic);
sra.doFinallyBodies = this.doFinallyBodies;
sra.skipFinallyBodies = this.skipFinallyBodies;
foreach (var v in this.evalContext.StateVariables)

8
ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// Copyright (c) 2011 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
@ -99,10 +99,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -99,10 +99,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
class SymbolicEvaluationContext
{
readonly IField stateField;
readonly bool legacyVisualBasic;
readonly List<ILVariable> stateVariables = new List<ILVariable>();
public SymbolicEvaluationContext(IField stateField)
public SymbolicEvaluationContext(IField stateField, bool legacyVisualBasic = false)
{
this.legacyVisualBasic = legacyVisualBasic;
this.stateField = stateField;
}
@ -118,7 +120,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -118,7 +120,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
public SymbolicValue Eval(ILInstruction inst)
{
if (inst is BinaryNumericInstruction bni && bni.Operator == BinaryNumericOperator.Sub && !bni.CheckForOverflow)
if (inst is BinaryNumericInstruction bni && bni.Operator == BinaryNumericOperator.Sub && (legacyVisualBasic || !bni.CheckForOverflow))
{
var left = Eval(bni.Left);
var right = Eval(bni.Right);

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

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// Copyright (c) 2011 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
@ -59,6 +59,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -59,6 +59,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// <remarks>Set in MatchEnumeratorCreationPattern()</remarks>
bool isCompiledWithMono;
/// <remarks>Set in MatchEnumeratorCreationPattern() or ConstructExceptionTable()</remarks>
bool isCompiledWithVisualBasic;
/// <remarks>Set in MatchEnumeratorCreationPattern()</remarks>
/// <remarks>If this is true, then isCompiledWithVisualBasic is also true.</remarks>
bool isCompiledWithLegacyVisualBasic;
/// <summary>The dispose method of the compiler-generated enumerator class.</summary>
/// <remarks>Set in ConstructExceptionTable()</remarks>
MethodDefinitionHandle disposeMethod;
@ -100,6 +107,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -100,6 +107,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// </summary>
ILVariable skipFinallyBodies;
/// <summary>
/// Local bool variable in MoveNext() that signifies whether to execute finally bodies.
/// </summary>
ILVariable doFinallyBodies;
/// <summary>
/// Set of variables might hold copies of the generated state field.
/// </summary>
@ -115,6 +127,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -115,6 +127,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
this.currentType = metadata.GetMethodDefinition((MethodDefinitionHandle)context.Function.Method.MetadataToken).GetDeclaringType();
this.enumeratorType = default;
this.enumeratorCtor = default;
this.isCompiledWithMono = false;
this.isCompiledWithVisualBasic = false;
this.isCompiledWithLegacyVisualBasic = false;
this.stateField = null;
this.currentField = null;
this.disposingField = null;
@ -123,6 +138,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -123,6 +138,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
this.decompiledFinallyMethods.Clear();
this.returnStores.Clear();
this.skipFinallyBodies = null;
this.doFinallyBodies = null;
this.cachedStateVars = null;
if (!MatchEnumeratorCreationPattern(function))
return;
@ -144,6 +160,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -144,6 +160,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
context.Step("Replacing body with MoveNext() body", function);
function.IsIterator = true;
function.StateMachineCompiledWithMono = isCompiledWithMono;
function.StateMachineCompiledWithLegacyVisualBasic = isCompiledWithLegacyVisualBasic;
var oldBody = function.Body;
function.Body = newBody;
// register any locals used in newBody
@ -159,7 +176,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -159,7 +176,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
context.Step("Delete unreachable blocks", function);
if (isCompiledWithMono)
if (isCompiledWithMono || isCompiledWithVisualBasic)
{
// mono has try-finally inline (like async on MS); we also need to sort nested blocks:
foreach (var nestedContainer in newBody.Blocks.SelectMany(c => c.Descendants).OfType<BlockContainer>())
@ -180,6 +197,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -180,6 +197,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
{
CleanSkipFinallyBodies(function);
}
else if (isCompiledWithLegacyVisualBasic)
{
CleanDoFinallyBodies(function);
}
else if (isCompiledWithVisualBasic)
{
CleanFinallyStateChecks(function);
}
else
{
DecompileFinallyBlocks();
@ -192,6 +217,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -192,6 +217,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
context.Step("Transform failed, roll it back", function);
function.IsIterator = false;
function.StateMachineCompiledWithMono = false;
function.StateMachineCompiledWithLegacyVisualBasic = false;
function.Body = oldBody;
function.Variables.RemoveDead();
function.Warnings.Add($"yield-return decompiler failed: {ex.Message}");
@ -202,7 +228,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -202,7 +228,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
TranslateFieldsToLocalAccess(function, function, fieldToParameterMap, isCompiledWithMono);
// On mono, we still need to remove traces of the state variable(s):
if (isCompiledWithMono)
if (isCompiledWithMono || isCompiledWithVisualBasic)
{
if (fieldToParameterMap.TryGetValue(stateField, out var stateVar))
{
@ -275,18 +301,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -275,18 +301,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (MatchEnumeratorCreationNewObj(newObj))
{
pos++; // OK
isCompiledWithMono = false;
}
else if (MatchMonoEnumeratorCreationNewObj(newObj))
{
pos++;
isCompiledWithMono = true;
if (TransformDisplayClassUsage.ValidateConstructor(context, ((NewObj)newObj).Method))
{
isCompiledWithMono = true;
}
else
{
isCompiledWithVisualBasic = true;
isCompiledWithLegacyVisualBasic = true;
}
}
else
{
return false;
}
bool stateFieldInitialized = false;
for (; pos < body.Instructions.Count; pos++)
{
// stfld(..., ldloc(var_1), ldloc(parameter))
@ -306,6 +340,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -306,6 +340,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// copy of 'this' in struct
fieldToParameterMap[(IField)storedField.MemberDefinition] = ((LdLoc)ldobj.Target).Variable;
}
else if ((isCompiledWithMono || isCompiledWithLegacyVisualBasic) && (value.MatchLdcI4(-2) || value.MatchLdcI4(-1) || value.MatchLdcI4(0)))
{
stateField = (IField)storedField.MemberDefinition;
stateFieldInitialized = true;
}
else
{
return false;
@ -319,7 +358,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -319,7 +358,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// stloc(var_2, ldloc(var_1))
pos++;
}
if (isCompiledWithMono)
if (isCompiledWithMono && !stateFieldInitialized)
{
// Mono initializes the state field separately:
// (but not if it's left at the default value 0)
@ -328,7 +367,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -328,7 +367,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
&& (value.MatchLdcI4(-2) || value.MatchLdcI4(0)))
{
stateField = (IField)field.MemberDefinition;
isCompiledWithMono = true;
pos++;
}
}
@ -477,8 +515,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -477,8 +515,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
void AnalyzeCurrentProperty()
{
MethodDefinitionHandle getCurrentMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault(
m => metadata.GetString(metadata.GetMethodDefinition(m).Name).StartsWith("System.Collections.Generic.IEnumerator", StringComparison.Ordinal)
&& metadata.GetString(metadata.GetMethodDefinition(m).Name).EndsWith(".get_Current", StringComparison.Ordinal));
m => IsMethod(m, "get_Current"));
Block body = SingleBlock(CreateILAst(getCurrentMethod, context).Body);
if (body == null)
throw new SymbolicAnalysisFailedException("get_Current has no body");
@ -516,8 +553,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -516,8 +553,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
void ResolveIEnumerableIEnumeratorFieldMapping()
{
MethodDefinitionHandle getEnumeratorMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault(
m => metadata.GetString(metadata.GetMethodDefinition(m).Name).StartsWith("System.Collections.Generic.IEnumerable", StringComparison.Ordinal)
&& metadata.GetString(metadata.GetMethodDefinition(m).Name).EndsWith(".GetEnumerator", StringComparison.Ordinal));
m => IsMethod(m, "GetEnumerator"));
ResolveIEnumerableIEnumeratorFieldMapping(getEnumeratorMethod, context, fieldToParameterMap);
}
@ -551,12 +587,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -551,12 +587,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
void ConstructExceptionTable()
{
if (isCompiledWithMono)
disposeMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault(m => IsMethod(m, "Dispose"));
var function = CreateILAst(disposeMethod, context);
if (!isCompiledWithVisualBasic && !isCompiledWithMono)
{
disposeMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault(m => metadata.GetString(metadata.GetMethodDefinition(m).Name) == "Dispose");
var function = CreateILAst(disposeMethod, context);
BlockContainer body = (BlockContainer)function.Body;
foreach (var instr in body.Blocks.SelectMany(block => block.Instructions))
{
if (instr is CallInstruction call && call.Arguments.Count == 1 && call.Arguments[0].MatchLdThis() &&
IsMethod((MethodDefinitionHandle)call.Method.MetadataToken, "MoveNext"))
{
isCompiledWithVisualBasic = true;
break;
}
}
}
if (isCompiledWithMono || isCompiledWithVisualBasic)
{
BlockContainer body = (BlockContainer)function.Body;
for (var i = 0; (i < body.EntryPoint.Instructions.Count) && !(body.EntryPoint.Instructions[i] is Branch); i++)
{
if (body.EntryPoint.Instructions[i] is StObj stobj
@ -570,14 +620,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -570,14 +620,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
// On mono, we don't need to analyse Dispose() to reconstruct the try-finally structure.
// On mono and VB, we don't need to analyse Dispose() to reconstruct the try-finally structure.
finallyMethodToStateRange = default;
}
else
{
// Non-Mono: analyze try-finally structure in Dispose()
disposeMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault(m => metadata.GetString(metadata.GetMethodDefinition(m).Name) == "System.IDisposable.Dispose");
var function = CreateILAst(disposeMethod, context);
// Non-Mono/Non-VB: analyze try-finally structure in Dispose()
var rangeAnalysis = new StateRangeAnalysis(StateRangeAnalysisMode.IteratorDispose, stateField);
rangeAnalysis.AssignStateRanges(function.Body, LongSet.Universe);
finallyMethodToStateRange = rangeAnalysis.finallyMethodToStateRange;
@ -616,6 +664,17 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -616,6 +664,17 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
CopyPropagation.Propagate(stloc, context);
}
// Copy propagate stack slots holding a 32 bit integer.
foreach (var stloc in moveNextFunction.Descendants.OfType<StLoc>().Where(s => s.Variable.Kind == VariableKind.StackSlot && s.Variable.IsSingleDefinition && s.Value.OpCode == OpCode.LdcI4).ToList())
{
CopyPropagation.Propagate(stloc, context);
}
foreach (var block in moveNextFunction.Descendants.OfType<Block>())
{
block.Instructions.RemoveAll(inst => inst.OpCode == OpCode.LdcI4);
}
var body = (BlockContainer)moveNextFunction.Body;
if (body.Blocks.Count == 1 && body.Blocks[0].Instructions.Count == 1 && body.Blocks[0].Instructions[0] is TryFault tryFault)
{
@ -635,6 +694,35 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -635,6 +694,35 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
if (isCompiledWithLegacyVisualBasic && (body.Blocks.Count == 2 || body.Blocks.Count == 1) &&
body.Blocks[0].Instructions.Count == 2 &&
body.Blocks[0].Instructions[0].MatchStLoc(out var firstVar, out var ldc) && ldc.MatchLdcI4(1))
{
doFinallyBodies = firstVar;
if (body.Blocks[0].Instructions[1] is TryCatch tryCatch && tryCatch.Handlers.Count == 1)
{
TryCatchHandler catchHandler = tryCatch.Handlers[0];
var catchBlockContainer = catchHandler.Body as BlockContainer;
if (catchBlockContainer?.Blocks.Count != 1)
throw new SymbolicAnalysisFailedException("Unexpected number of blocks in MoveNext() catch block");
var catchBlock = catchBlockContainer.Blocks.Single();
if (!(catchBlock.Instructions.Count == 4 && catchBlock.Instructions[0] is Call call &&
call.Method.Name == "SetProjectError" && call.Arguments.Count == 1 &&
call.Arguments[0].MatchLdLoc(catchHandler.Variable) &&
catchBlock.Instructions[1].MatchStLoc(out _, out var ldloc) &&
ldloc.MatchLdLoc(catchHandler.Variable) &&
catchBlock.Instructions[2].MatchStFld(out var ldThis, out var fld, out var value) &&
ldThis.MatchLdThis() && fld.MemberDefinition.Equals(stateField) && value is LdcI4 &&
catchBlock.Instructions[3] is Rethrow))
throw new SymbolicAnalysisFailedException("Unexpected catch block contents in MoveNext()");
BlockContainer tryCatchBody = (BlockContainer)tryCatch.TryBlock;
// Move return block
if (body.Blocks.Count == 2)
tryCatchBody.Blocks.Add(body.Blocks[1]);
body = tryCatchBody;
}
}
if (stateField == null)
{
// With mono-compiled state machines, it's possible that we haven't discovered the state field
@ -688,8 +776,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -688,8 +776,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// but those cannot contain any yield statements.
// So for reconstructing the control flow, we only consider the blocks directly within body.
var rangeAnalysis = new StateRangeAnalysis(StateRangeAnalysisMode.IteratorMoveNext, stateField);
var rangeAnalysis = new StateRangeAnalysis(StateRangeAnalysisMode.IteratorMoveNext, stateField, legacyVisualBasic: isCompiledWithLegacyVisualBasic);
rangeAnalysis.skipFinallyBodies = skipFinallyBodies;
rangeAnalysis.doFinallyBodies = doFinallyBodies;
rangeAnalysis.CancellationToken = context.CancellationToken;
rangeAnalysis.AssignStateRanges(body, LongSet.Universe);
cachedStateVars = rangeAnalysis.CachedStateVars.ToHashSet();
@ -813,7 +902,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -813,7 +902,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// (this allows us to consider each block individually for try-finally reconstruction)
newBlock = SplitBlock(newBlock, oldInst);
}
else if (oldInst is TryFinally tryFinally && isCompiledWithMono)
else if (oldInst is TryFinally tryFinally && (isCompiledWithMono || isCompiledWithVisualBasic))
{
// with mono, we have to recurse into try-finally blocks
var oldTryBlock = (BlockContainer)tryFinally.TryBlock;
@ -821,6 +910,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -821,6 +910,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
sra.AssignStateRanges(oldTryBlock, LongSet.Universe);
tryFinally.TryBlock = ConvertBody(oldTryBlock, sra);
}
else if (isCompiledWithLegacyVisualBasic && oldInst is IfInstruction ifInstruction &&
ifInstruction.FalseInst.MatchNop() &&
ifInstruction.Condition.MatchCompEquals(out var left, out var right) &&
left.MatchLdFld(out var ldThis, out var fld) && ldThis.MatchLdThis() &&
fld.MemberDefinition.Equals(disposingField) &&
right.MatchLdcI4(0))
{
newBlock.Instructions.Add(ifInstruction.TrueInst);
newBlock.AddILRange(ifInstruction.TrueInst);
UpdateBranchTargets(ifInstruction.TrueInst);
break;
}
// copy over the instruction to the new block
newBlock.Instructions.Add(oldInst);
newBlock.AddILRange(oldInst);
@ -828,12 +930,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -828,12 +930,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
// Insert new artificial block as entry point, and jump to state 0.
// Insert new artificial block as entry point, and jump to the initial state.
// This causes the method to start directly at the first user code,
// and the whole compiler-generated state-dispatching logic becomes unreachable code
// and gets deleted.
int initialState = isCompiledWithLegacyVisualBasic ? -1 : 0;
newBody.Blocks.Insert(0, new Block {
Instructions = { MakeGoTo(0) }
Instructions = { MakeGoTo(initialState) }
});
return newBody;
@ -870,10 +973,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -870,10 +973,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
if (oldBlock.Instructions[pos].MatchStFld(out var target, out var field, out var value)
// Visual Basic Compiler emits additional stores to variables.
int? localNewState = null;
if (oldBlock.Instructions[pos].MatchStLoc(out _, out var value) && value is LdcI4 ldci4)
{
localNewState = ldci4.Value;
pos++;
}
if (oldBlock.Instructions[pos].MatchStFld(out var target, out var field, out value)
&& target.MatchLdThis()
&& field.MemberDefinition == stateField
&& value.MatchLdcI4(out int newState))
&& value.MatchLdcI4(out int newState)
&& (localNewState is null || localNewState == newState))
{
pos++;
}
@ -899,6 +1011,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -899,6 +1011,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
pos++;
}
// We can't use MatchStLoc like above since the doFinallyBodies variable is split by SplitVariables.
// This occurs for the Legacy VBC compiler.
if (oldBlock.Instructions[pos].MatchStLoc(out var var, out value) && var.Kind == VariableKind.Local && var.Index == doFinallyBodies.Index)
{
if (!value.MatchLdcI4(0))
{
newBlock.Instructions.Add(new InvalidExpression {
ExpectedResultType = StackType.Void,
Message = "Unexpected assignment to doFinallyBodies"
});
}
pos++;
}
if (oldBlock.Instructions[pos].MatchReturn(out var retVal) && retVal.MatchLdcI4(1))
{
@ -1251,6 +1376,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -1251,6 +1376,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
#endregion
#region Cleanup finally blocks
/// <summary>
/// Eliminates usage of doFinallyBodies
@ -1264,7 +1390,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -1264,7 +1390,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
context.StepStartGroup("CleanFinallyBlocks", function);
if (skipFinallyBodies.StoreInstructions.Count != 0 || skipFinallyBodies.AddressCount != 0)
{
// misdetected another variable as doFinallyBodies?
// misdetected another variable as skipFinallyBodies?
// Fortunately removing the initial store of 0 is harmless, as we
// default-initialize the variable on uninit uses
return;
@ -1318,5 +1444,96 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -1318,5 +1444,96 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return !call.Method.MetadataToken.IsNil;
}
}
private void CleanDoFinallyBodies(ILFunction function)
{
if (doFinallyBodies == null)
{
return; // only VB code uses doFinallyBodies
}
context.StepStartGroup("CleanDoFinallyBodies", function);
if (doFinallyBodies.StoreInstructions.Count != 0 || doFinallyBodies.AddressCount != 0)
{
// misdetected another variable as skipFinallyBodies?
// Fortunately removing the initial store of 0 is harmless, as we
// default-initialize the variable on uninit uses
return;
}
foreach (var tryFinally in function.Descendants.OfType<TryFinally>())
{
if (!(tryFinally.FinallyBlock is BlockContainer container))
continue;
Block entryPoint = AsyncAwaitDecompiler.GetBodyEntryPoint(container);
if (entryPoint?.Instructions[0] is IfInstruction ifInst)
{
if (ifInst.Condition.MatchCompEquals(out var left, out var right) && left.MatchLdLoc(doFinallyBodies) && right.MatchLdcI4(0))
{
context.Step("Remove if (doFinallyBodies) from try-finally", tryFinally);
// condition will always be false now that we're using 'yield' instructions
entryPoint.Instructions.RemoveAt(0);
}
}
}
foreach (LdLoc load in doFinallyBodies.LoadInstructions.ToArray())
{
load.ReplaceWith(new LdcI4(1).WithILRange(load));
}
context.StepEndGroup(keepIfEmpty: true);
}
private void CleanFinallyStateChecks(ILFunction function)
{
context.StepStartGroup("CleanFinallyStateChecks", function);
foreach (var tryFinally in function.Descendants.OfType<TryFinally>())
{
if (!(tryFinally.FinallyBlock is BlockContainer container))
continue;
Block entryPoint = AsyncAwaitDecompiler.GetBodyEntryPoint(container);
if (entryPoint?.Instructions[0] is IfInstruction ifInst)
{
if (ifInst.Condition is Comp comp && comp.Kind == ComparisonKind.GreaterThanOrEqual &&
comp.InputType == StackType.I4 && comp.Sign == Sign.Signed && comp.Left.MatchLdLoc(out var variable) &&
cachedStateVars.Contains(variable) &&
comp.Right.MatchLdcI4(0))
{
context.Step("Remove if (stateVar >= 0) from try-finally", tryFinally);
// condition will always be false now that we're using 'yield' instructions
entryPoint.Instructions.RemoveAt(0);
}
}
}
context.StepEndGroup(keepIfEmpty: true);
}
#endregion
bool IsMethod(MethodDefinitionHandle method, string name)
{
var methodDefinition = metadata.GetMethodDefinition(method);
if (metadata.GetString(methodDefinition.Name) == name)
return true;
foreach (var implHandle in method.GetMethodImplementations(metadata))
{
var impl = metadata.GetMethodImplementation(implHandle);
switch (impl.MethodDeclaration.Kind)
{
case HandleKind.MethodDefinition:
var md = metadata.GetMethodDefinition((MethodDefinitionHandle)impl.MethodDeclaration);
if (metadata.GetString(md.Name) != name)
continue;
return true;
case HandleKind.MemberReference:
var mr = metadata.GetMemberReference((MemberReferenceHandle)impl.MethodDeclaration);
if (mr.GetKind() != MemberReferenceKind.Method)
continue;
if (metadata.GetString(mr.Name) != name)
continue;
return true;
default:
continue;
}
}
return false;
}
}
}

7
ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
#nullable enable
#nullable enable
// Copyright (c) 2014 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
@ -98,6 +98,11 @@ namespace ICSharpCode.Decompiler.IL @@ -98,6 +98,11 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
public bool StateMachineCompiledWithMono;
/// <summary>
/// Gets whether the YieldReturnDecompiler determined that the Legacy VB compiler was used to compile this function.
/// </summary>
public bool StateMachineCompiledWithLegacyVisualBasic;
/// <summary>
/// Gets whether this function is async.
/// This flag gets set by the AsyncAwaitDecompiler.

10
ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// Copyright (c) 2019 Siegfried Pammer
// Copyright (c) 2019 Siegfried Pammer
//
// 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
@ -289,7 +289,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -289,7 +289,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return null;
if (!(v.StoreInstructions.SingleOrDefault() is StLoc stloc))
return null;
if (stloc.Value is NewObj newObj && ValidateConstructor(newObj.Method))
if (stloc.Value is NewObj newObj && ValidateConstructor(context, newObj.Method))
{
result = new DisplayClass(v, definition) {
CaptureScope = v.CaptureScope,
@ -393,7 +393,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -393,7 +393,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var definition = newObj.Method.DeclaringType.GetDefinition();
if (!ValidateDisplayClassDefinition(definition))
return null;
if (!ValidateConstructor(newObj.Method))
if (!ValidateConstructor(context, newObj.Method))
return null;
if (!initializerBlock.Parent.MatchStLoc(out var referenceVariable))
return null;
@ -432,7 +432,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -432,7 +432,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
private bool ValidateConstructor(IMethod method)
internal static bool ValidateConstructor(ILTransformContext context, IMethod method)
{
try
{
@ -491,7 +491,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -491,7 +491,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
ILOpCode DecodeOpCodeSkipNop(ref BlobReader reader)
static ILOpCode DecodeOpCodeSkipNop(ref BlobReader reader)
{
ILOpCode code;
do

Loading…
Cancel
Save