Browse Source

Fix #1388: Async await on dynamic expression is not detected.

Fix #1928: dynamic JObject() not decompile correctly
stash/dynamic-await
Siegfried Pammer 3 years ago
parent
commit
dbd4f3d2e1
  1. 1
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  2. 114
      ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs

1
ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

@ -1107,6 +1107,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -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

114
ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs

@ -45,48 +45,32 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -45,48 +45,32 @@ namespace ICSharpCode.Decompiler.IL.Transforms
this.context = context;
Dictionary<IField, CallSiteInfo> callsites = new Dictionary<IField, CallSiteInfo>();
HashSet<BlockContainer> modifiedContainers = new HashSet<BlockContainer>();
foreach (var block in function.Descendants.OfType<Block>())
{
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<StLoc>();
TransformCallSites((BlockContainer)function.Body, callsites, context);
}
foreach (var invokeCall in function.Descendants.OfType<CallVirt>())
internal static void RunOnBasicBlock(Block block, ILTransformContext context)
{
if (!context.Settings.Dynamic)
return;
Dictionary<IField, CallSiteInfo> callsites = new Dictionary<IField, CallSiteInfo>();
FindDynamicCallSitesInBlock(block, callsites, context);
TransformCallSites((BlockContainer)block.Parent, callsites, context);
}
private static void TransformCallSites(BlockContainer parent, Dictionary<IField, CallSiteInfo> callsites,
ILTransformContext context)
{
List<StLoc> storesToRemove = new();
HashSet<BlockContainer> modifiedContainers = new();
foreach (var invokeCall in parent.Descendants.OfType<CallVirt>())
{
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 @@ -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<IField, CallSiteInfo> 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<ILInstruction> deadArguments)
static ILInstruction MakeDynamicInstruction(CallSiteInfo callsite, CallVirt targetInvokeCall, List<ILInstruction> deadArguments)
{
switch (callsite.Kind)
{
@ -272,7 +290,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -596,7 +614,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public string MemberName;
}
enum BinderMethodKind
internal enum BinderMethodKind
{
BinaryOperation,
Convert,

Loading…
Cancel
Save