diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs index 335dffa36..1e7ee2550 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs @@ -1107,6 +1107,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow foreach (var block in container.Blocks) { context.CancellationToken.ThrowIfCancellationRequested(); + DynamicCallSiteTransform.RunOnBasicBlock(block, context); if (block.Instructions.Last() is Leave leave && moveNextLeaves.Contains(leave)) { // This is likely an 'await' block diff --git a/ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs index 2c78d42f9..4bd7ff814 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs @@ -45,48 +45,32 @@ namespace ICSharpCode.Decompiler.IL.Transforms this.context = context; Dictionary callsites = new Dictionary(); - HashSet modifiedContainers = new HashSet(); foreach (var block in function.Descendants.OfType()) { - if (block.Instructions.Count < 2) - continue; - // Check if, we deal with a callsite cache field null check: - // if (comp(ldsfld <>p__3 == ldnull)) br IL_000c - // br IL_002b - if (!(block.Instructions.SecondToLastOrDefault() is IfInstruction ifInst)) - continue; - if (!(block.Instructions.LastOrDefault() is Branch branchAfterInit)) - continue; - if (!MatchCallSiteCacheNullCheck(ifInst.Condition, out var callSiteCacheField, out var callSiteDelegate, out bool invertBranches)) - continue; - if (!ifInst.TrueInst.MatchBranch(out var trueBlock)) - continue; - Block callSiteInitBlock, targetBlockAfterInit; - if (invertBranches) - { - callSiteInitBlock = branchAfterInit.TargetBlock; - targetBlockAfterInit = trueBlock; - } - else - { - callSiteInitBlock = trueBlock; - targetBlockAfterInit = branchAfterInit.TargetBlock; - } - if (!ScanCallSiteInitBlock(callSiteInitBlock, callSiteCacheField, callSiteDelegate, out var callSiteInfo, out var blockAfterInit)) - continue; - if (targetBlockAfterInit != blockAfterInit) - continue; - callSiteInfo.DelegateType = callSiteDelegate; - callSiteInfo.ConditionalJumpToInit = ifInst; - callSiteInfo.Inverted = invertBranches; - callSiteInfo.BranchAfterInit = branchAfterInit; - callsites.Add(callSiteCacheField, callSiteInfo); + FindDynamicCallSitesInBlock(block, callsites, context); } - var storesToRemove = new List(); + TransformCallSites((BlockContainer)function.Body, callsites, context); + } - foreach (var invokeCall in function.Descendants.OfType()) + internal static void RunOnBasicBlock(Block block, ILTransformContext context) + { + if (!context.Settings.Dynamic) + return; + + Dictionary callsites = new Dictionary(); + FindDynamicCallSitesInBlock(block, callsites, context); + TransformCallSites((BlockContainer)block.Parent, callsites, context); + } + + private static void TransformCallSites(BlockContainer parent, Dictionary callsites, + ILTransformContext context) + { + List storesToRemove = new(); + HashSet modifiedContainers = new(); + + foreach (var invokeCall in parent.Descendants.OfType()) { if (invokeCall.Method.DeclaringType.Kind != TypeKind.Delegate || invokeCall.Method.Name != "Invoke" || invokeCall.Arguments.Count == 0) continue; @@ -141,12 +125,46 @@ namespace ICSharpCode.Decompiler.IL.Transforms Block parentBlock = (Block)inst.Parent; parentBlock.Instructions.RemoveAt(inst.ChildIndex); } + } - foreach (var container in modifiedContainers) - container.SortBlocks(deleteUnreachableBlocks: true); + static void FindDynamicCallSitesInBlock(Block block, Dictionary callsites, ILTransformContext context) + { + if (block.Instructions.Count < 2) + return; + // Check if, we deal with a callsite cache field null check: + // if (comp(ldsfld <>p__3 == ldnull)) br IL_000c + // br IL_002b + if (!(block.Instructions.SecondToLastOrDefault() is IfInstruction ifInst)) + return; + if (!(block.Instructions.LastOrDefault() is Branch branchAfterInit)) + return; + if (!MatchCallSiteCacheNullCheck(ifInst.Condition, out var callSiteCacheField, out var callSiteDelegate, out bool invertBranches)) + return; + if (!ifInst.TrueInst.MatchBranch(out var trueBlock)) + return; + Block callSiteInitBlock, targetBlockAfterInit; + if (invertBranches) + { + callSiteInitBlock = branchAfterInit.TargetBlock; + targetBlockAfterInit = trueBlock; + } + else + { + callSiteInitBlock = trueBlock; + targetBlockAfterInit = branchAfterInit.TargetBlock; + } + if (!ScanCallSiteInitBlock(callSiteInitBlock, callSiteCacheField, callSiteDelegate, out var callSiteInfo, out var blockAfterInit, context)) + return; + if (targetBlockAfterInit != blockAfterInit) + return; + callSiteInfo.DelegateType = callSiteDelegate; + callSiteInfo.ConditionalJumpToInit = ifInst; + callSiteInfo.Inverted = invertBranches; + callSiteInfo.BranchAfterInit = branchAfterInit; + callsites.Add(callSiteCacheField, callSiteInfo); } - ILInstruction MakeDynamicInstruction(CallSiteInfo callsite, CallVirt targetInvokeCall, List deadArguments) + static ILInstruction MakeDynamicInstruction(CallSiteInfo callsite, CallVirt targetInvokeCall, List deadArguments) { switch (callsite.Kind) { @@ -272,7 +290,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - bool ScanCallSiteInitBlock(Block callSiteInitBlock, IField callSiteCacheField, IType callSiteDelegateType, out CallSiteInfo callSiteInfo, out Block blockAfterInit) + static bool ScanCallSiteInitBlock(Block callSiteInitBlock, IField callSiteCacheField, IType callSiteDelegateType, out CallSiteInfo callSiteInfo, out Block blockAfterInit, ILTransformContext context) { callSiteInfo = default(CallSiteInfo); blockAfterInit = null; @@ -397,7 +415,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!callSiteInitBlock.Instructions[4 + typeArgumentsOffset].MatchStLoc(variable, out value)) return false; - if (!ExtractArgumentInfo(value, ref callSiteInfo, 5 + typeArgumentsOffset, variable)) + if (!ExtractArgumentInfo(value, ref callSiteInfo, 5 + typeArgumentsOffset, variable, context)) return false; return true; case "GetMember": @@ -436,7 +454,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!callSiteInitBlock.Instructions[3].MatchStLoc(variable, out value)) return false; - if (!ExtractArgumentInfo(value, ref callSiteInfo, 4, variable)) + if (!ExtractArgumentInfo(value, ref callSiteInfo, 4, variable, context)) return false; return true; case "GetIndex": @@ -484,7 +502,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!callSiteInitBlock.Instructions[2].MatchStLoc(variable, out value)) return false; - if (!ExtractArgumentInfo(value, ref callSiteInfo, 3, variable)) + if (!ExtractArgumentInfo(value, ref callSiteInfo, 3, variable, context)) return false; return true; case "UnaryOperation": @@ -523,7 +541,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!callSiteInitBlock.Instructions[3].MatchStLoc(variable, out value)) return false; - if (!ExtractArgumentInfo(value, ref callSiteInfo, 4, variable)) + if (!ExtractArgumentInfo(value, ref callSiteInfo, 4, variable, context)) return false; return true; default: @@ -531,7 +549,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - bool ExtractArgumentInfo(ILInstruction value, ref CallSiteInfo callSiteInfo, int instructionOffset, ILVariable variable) + static bool ExtractArgumentInfo(ILInstruction value, ref CallSiteInfo callSiteInfo, int instructionOffset, ILVariable variable, ILTransformContext context) { if (!(value is NewArr newArr2 && newArr2.Type.FullName == "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" && newArr2.Indices.Count == 1 && newArr2.Indices[0].MatchLdcI4(out var numberOfArguments))) return false; @@ -560,7 +578,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - bool MatchCallSiteCacheNullCheck(ILInstruction condition, out IField callSiteCacheField, out IType callSiteDelegate, out bool invertBranches) + static bool MatchCallSiteCacheNullCheck(ILInstruction condition, out IField callSiteCacheField, out IType callSiteDelegate, out bool invertBranches) { callSiteCacheField = null; callSiteDelegate = null; @@ -579,7 +597,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - struct CallSiteInfo + internal struct CallSiteInfo { public bool Inverted; public ILInstruction BranchAfterInit; @@ -596,7 +614,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms public string MemberName; } - enum BinderMethodKind + internal enum BinderMethodKind { BinaryOperation, Convert,