diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
index bdf2e36ca..ea56949f0 100644
--- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
+++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
@@ -1804,6 +1804,10 @@ namespace ICSharpCode.Decompiler.CSharp
{
RemoveAttribute(entityDecl, KnownAttribute.DebuggerHidden);
}
+ if (function.StateMachineCompiledWithLegacyVisualBasic)
+ {
+ RemoveAttribute(entityDecl, KnownAttribute.DebuggerStepThrough);
+ }
}
if (function.IsAsync)
{
diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs
index 4bd88cade..88c6f7915 100644
--- a/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs
+++ b/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs
@@ -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
@@ -118,7 +118,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)
{
var left = Eval(bni.Left);
var right = Eval(bni.Right);
diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
index 099247f61..06206e596 100644
--- a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
+++ b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
@@ -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
/// Set in MatchEnumeratorCreationPattern()
bool isCompiledWithMono;
+ /// Set in MatchEnumeratorCreationPattern() or ConstructExceptionTable()
+ bool isCompiledWithVisualBasic;
+
+ /// Set in MatchEnumeratorCreationPattern()
+ /// If this is true, then isCompiledWithVisualBasic is also true.
+ bool isCompiledWithLegacyVisualBasic;
+
/// The dispose method of the compiler-generated enumerator class.
/// Set in ConstructExceptionTable()
MethodDefinitionHandle disposeMethod;
@@ -100,6 +107,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
///
ILVariable skipFinallyBodies;
+ ///
+ /// Local bool variable in MoveNext() that signifies whether to execute finally bodies.
+ ///
+ ILVariable doFinallyBodies;
+
///
/// Set of variables might hold copies of the generated state field.
///
@@ -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
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
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
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())
@@ -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
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
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
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
// 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
// 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
&& (value.MatchLdcI4(-2) || value.MatchLdcI4(0)))
{
stateField = (IField)field.MemberDefinition;
- isCompiledWithMono = true;
pos++;
}
}
@@ -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
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
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
}
}
- // 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
CopyPropagation.Propagate(stloc, context);
}
+ // Copy propagate stack slots holding a 32 bit integer.
+ foreach (var stloc in moveNextFunction.Descendants.OfType().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.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
}
}
+ 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
@@ -690,6 +778,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
var rangeAnalysis = new StateRangeAnalysis(StateRangeAnalysisMode.IteratorMoveNext, stateField);
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
// (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
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
}
}
- // 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
}
}
- 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
}
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
}
#endregion
+ #region Cleanup finally blocks
///
/// Eliminates usage of doFinallyBodies
@@ -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
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())
+ {
+ 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())
+ {
+ 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;
+ }
}
}
diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
index 306539276..bbf0e6441 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
@@ -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
///
public bool StateMachineCompiledWithMono;
+ ///
+ /// Gets whether the YieldReturnDecompiler determined that the Legacy VB compiler was used to compile this function.
+ ///
+ public bool StateMachineCompiledWithLegacyVisualBasic;
+
///
/// Gets whether this function is async.
/// This flag gets set by the AsyncAwaitDecompiler.
diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
index 5f69000d1..a62e16eb2 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
@@ -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
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
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
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
}
}
- ILOpCode DecodeOpCodeSkipNop(ref BlobReader reader)
+ static ILOpCode DecodeOpCodeSkipNop(ref BlobReader reader)
{
ILOpCode code;
do