|
|
|
@ -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
|
|
|
|
@ -690,6 +778,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
@@ -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
@@ -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; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|