diff --git a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs index ebef49e54..6d64b5dd1 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs @@ -222,6 +222,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis this.bottomState = initialState.Clone(); this.bottomState.ReplaceWithBottom(); Debug.Assert(bottomState.IsBottom); + this.stateOnNullableRewrap = bottomState.Clone(); this.currentStateOnException = state.Clone(); } @@ -254,7 +255,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis #endif [Conditional("DEBUG")] - void DebugStartPoint(ILInstruction inst) + protected void DebugStartPoint(ILInstruction inst) { #if DEBUG DebugPoint(debugInputState, inst); @@ -262,7 +263,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis } [Conditional("DEBUG")] - void DebugEndPoint(ILInstruction inst) + protected void DebugEndPoint(ILInstruction inst) { #if DEBUG DebugPoint(debugOutputState, inst); @@ -286,7 +287,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis foreach (var child in inst.Children) { child.AcceptVisitor(this); Debug.Assert(state.IsBottom || !child.HasFlag(InstructionFlags.EndPointUnreachable), - "Unreachable code must be in the bottom state."); + "Unreachable code must be in the bottom state."); } DebugEndPoint(inst); @@ -611,7 +612,53 @@ namespace ICSharpCode.Decompiler.FlowAnalysis state.JoinWith(afterTrueState); DebugEndPoint(inst); } - + + protected internal override void VisitNullCoalescingInstruction(NullCoalescingInstruction inst) + { + HandleBinaryWithOptionalEvaluation(inst, inst.ValueInst, inst.FallbackInst); + } + + protected internal override void VisitDynamicLogicOperatorInstruction(DynamicLogicOperatorInstruction inst) + { + HandleBinaryWithOptionalEvaluation(inst, inst.Left, inst.Right); + } + + protected internal override void VisitUserDefinedLogicOperator(UserDefinedLogicOperator inst) + { + HandleBinaryWithOptionalEvaluation(inst, inst.Left, inst.Right); + } + + void HandleBinaryWithOptionalEvaluation(ILInstruction parent, ILInstruction left, ILInstruction right) + { + DebugStartPoint(parent); + left.AcceptVisitor(this); + State branchState = state.Clone(); + right.AcceptVisitor(this); + state.JoinWith(branchState); + DebugEndPoint(parent); + } + + State stateOnNullableRewrap; + + protected internal override void VisitNullableRewrap(NullableRewrap inst) + { + DebugStartPoint(inst); + var oldState = stateOnNullableRewrap.Clone(); + stateOnNullableRewrap.ReplaceWithBottom(); + inst.Argument.AcceptVisitor(this); + state.JoinWith(stateOnNullableRewrap); + stateOnNullableRewrap = oldState; + DebugEndPoint(inst); + } + + protected internal override void VisitNullableUnwrap(NullableUnwrap inst) + { + DebugStartPoint(inst); + inst.Argument.AcceptVisitor(this); + stateOnNullableRewrap.JoinWith(state); + DebugEndPoint(inst); + } + protected internal override void VisitSwitchInstruction(SwitchInstruction inst) { DebugStartPoint(inst); @@ -635,6 +682,22 @@ namespace ICSharpCode.Decompiler.FlowAnalysis DebugEndPoint(inst); } + protected internal override void VisitUsingInstruction(UsingInstruction inst) + { + DebugStartPoint(inst); + inst.ResourceExpression.AcceptVisitor(this); + inst.Body.AcceptVisitor(this); + DebugEndPoint(inst); + } + + protected internal override void VisitLockInstruction(LockInstruction inst) + { + DebugStartPoint(inst); + inst.OnExpression.AcceptVisitor(this); + inst.Body.AcceptVisitor(this); + DebugEndPoint(inst); + } + protected internal override void VisitILFunction(ILFunction function) { throw new NotImplementedException(); diff --git a/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs b/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs index e05fbaccc..af6006009 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs @@ -20,6 +20,9 @@ using System.Diagnostics; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Util; using System.Threading; +using System; +using System.Collections.Generic; +using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.FlowAnalysis { @@ -117,6 +120,8 @@ namespace ICSharpCode.Decompiler.FlowAnalysis readonly CancellationToken cancellationToken; readonly ILFunction scope; readonly BitSet variablesWithUninitializedUsage; + readonly Dictionary stateOfLocalFunctionUse = new Dictionary(); + readonly HashSet localFunctionsNeedingAnalysis = new HashSet(); public DefiniteAssignmentVisitor(ILFunction scope, CancellationToken cancellationToken) { @@ -203,8 +208,34 @@ namespace ICSharpCode.Decompiler.FlowAnalysis HandleCall(inst); } + protected internal override void VisitILFunction(ILFunction inst) + { + DebugStartPoint(inst); + State stateBeforeFunction = state.Clone(); + State stateOnExceptionBeforeFunction = currentStateOnException.Clone(); + inst.Body.AcceptVisitor(this); + bool changed; + do { + changed = false; + foreach (var nestedFunction in inst.LocalFunctions) { + if (!localFunctionsNeedingAnalysis.Contains(nestedFunction.ReducedMethod)) + continue; + localFunctionsNeedingAnalysis.Remove(nestedFunction.ReducedMethod); + State stateOnEntry = stateOfLocalFunctionUse[nestedFunction.ReducedMethod]; + this.state.ReplaceWith(stateOnEntry); + this.currentStateOnException.ReplaceWithBottom(); + nestedFunction.Body.AcceptVisitor(this); + changed = true; + } + } while (changed); + currentStateOnException = stateOnExceptionBeforeFunction; + state = stateBeforeFunction; + DebugEndPoint(inst); + } + void HandleCall(CallInstruction call) { + DebugStartPoint(call); bool hasOutArgs = false; foreach (var arg in call.Arguments) { if (arg.MatchLdLoca(out var v) && call.GetParameter(arg.ChildIndex)?.IsOut == true) { @@ -223,6 +254,30 @@ namespace ICSharpCode.Decompiler.FlowAnalysis } } } + HandleLocalFunctionUse(call.Method); + DebugEndPoint(call); + } + + void HandleLocalFunctionUse(IMethod method) + { + if (method.IsLocalFunction) { + if (stateOfLocalFunctionUse.TryGetValue(method, out var stateOnEntry)) { + if (!state.LessThanOrEqual(stateOnEntry)) { + stateOnEntry.JoinWith(state); + localFunctionsNeedingAnalysis.Add(method); + } + } else { + stateOfLocalFunctionUse.Add(method, state.Clone()); + localFunctionsNeedingAnalysis.Add(method); + } + } + } + + protected internal override void VisitLdFtn(LdFtn inst) + { + DebugStartPoint(inst); + HandleLocalFunctionUse(inst.Method); + DebugEndPoint(inst); } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs b/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs index a6dab356b..9bdb3af18 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs @@ -35,13 +35,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { public void Run(ILFunction function, ILTransformContext context) { - var visitor = new DefiniteAssignmentVisitor(function, context.CancellationToken); - function.Body.AcceptVisitor(visitor); - foreach (var v in function.Variables) { - if (v.Kind != VariableKind.Parameter && !visitor.IsPotentiallyUsedUninitialized(v)) { - v.HasInitialValue = false; - } - } + ResetHasInitialValueFlag(function, context); // Remove dead stores to variables that are never read from. // If the stored value has some side-effect, the value is unwrapped. // This is necessary to remove useless stores generated by some compilers, e.g., the F# compiler. @@ -94,5 +88,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } } + + internal static void ResetHasInitialValueFlag(ILFunction function, ILTransformContext context) + { + var visitor = new DefiniteAssignmentVisitor(function, context.CancellationToken); + function.AcceptVisitor(visitor); + foreach (var v in function.Variables) { + if (v.Kind != VariableKind.Parameter && !visitor.IsPotentiallyUsedUninitialized(v)) { + v.HasInitialValue = false; + } + } + } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 7a1a31e01..3e34bd295 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -86,6 +86,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms containingBlock.Instructions.Remove(store); } } + RemoveDeadVariableInit.ResetHasInitialValueFlag(function, context); } finally { instructionsToRemove.Clear(); displayClasses.Clear();