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
foreach (var block in container.Blocks) foreach (var block in container.Blocks)
{ {
context.CancellationToken.ThrowIfCancellationRequested(); context.CancellationToken.ThrowIfCancellationRequested();
DynamicCallSiteTransform.RunOnBasicBlock(block, context);
if (block.Instructions.Last() is Leave leave && moveNextLeaves.Contains(leave)) if (block.Instructions.Last() is Leave leave && moveNextLeaves.Contains(leave))
{ {
// This is likely an 'await' block // This is likely an 'await' block

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

@ -45,48 +45,32 @@ namespace ICSharpCode.Decompiler.IL.Transforms
this.context = context; this.context = context;
Dictionary<IField, CallSiteInfo> callsites = new Dictionary<IField, CallSiteInfo>(); Dictionary<IField, CallSiteInfo> callsites = new Dictionary<IField, CallSiteInfo>();
HashSet<BlockContainer> modifiedContainers = new HashSet<BlockContainer>();
foreach (var block in function.Descendants.OfType<Block>()) foreach (var block in function.Descendants.OfType<Block>())
{ {
if (block.Instructions.Count < 2) FindDynamicCallSitesInBlock(block, callsites, context);
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);
} }
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) if (invokeCall.Method.DeclaringType.Kind != TypeKind.Delegate || invokeCall.Method.Name != "Invoke" || invokeCall.Arguments.Count == 0)
continue; continue;
@ -141,12 +125,46 @@ namespace ICSharpCode.Decompiler.IL.Transforms
Block parentBlock = (Block)inst.Parent; Block parentBlock = (Block)inst.Parent;
parentBlock.Instructions.RemoveAt(inst.ChildIndex); parentBlock.Instructions.RemoveAt(inst.ChildIndex);
} }
}
foreach (var container in modifiedContainers) static void FindDynamicCallSitesInBlock(Block block, Dictionary<IField, CallSiteInfo> callsites, ILTransformContext context)
container.SortBlocks(deleteUnreachableBlocks: true); {
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) 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); callSiteInfo = default(CallSiteInfo);
blockAfterInit = null; blockAfterInit = null;
@ -397,7 +415,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
if (!callSiteInitBlock.Instructions[4 + typeArgumentsOffset].MatchStLoc(variable, out value)) if (!callSiteInitBlock.Instructions[4 + typeArgumentsOffset].MatchStLoc(variable, out value))
return false; return false;
if (!ExtractArgumentInfo(value, ref callSiteInfo, 5 + typeArgumentsOffset, variable)) if (!ExtractArgumentInfo(value, ref callSiteInfo, 5 + typeArgumentsOffset, variable, context))
return false; return false;
return true; return true;
case "GetMember": case "GetMember":
@ -436,7 +454,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
if (!callSiteInitBlock.Instructions[3].MatchStLoc(variable, out value)) if (!callSiteInitBlock.Instructions[3].MatchStLoc(variable, out value))
return false; return false;
if (!ExtractArgumentInfo(value, ref callSiteInfo, 4, variable)) if (!ExtractArgumentInfo(value, ref callSiteInfo, 4, variable, context))
return false; return false;
return true; return true;
case "GetIndex": case "GetIndex":
@ -484,7 +502,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
if (!callSiteInitBlock.Instructions[2].MatchStLoc(variable, out value)) if (!callSiteInitBlock.Instructions[2].MatchStLoc(variable, out value))
return false; return false;
if (!ExtractArgumentInfo(value, ref callSiteInfo, 3, variable)) if (!ExtractArgumentInfo(value, ref callSiteInfo, 3, variable, context))
return false; return false;
return true; return true;
case "UnaryOperation": case "UnaryOperation":
@ -523,7 +541,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
if (!callSiteInitBlock.Instructions[3].MatchStLoc(variable, out value)) if (!callSiteInitBlock.Instructions[3].MatchStLoc(variable, out value))
return false; return false;
if (!ExtractArgumentInfo(value, ref callSiteInfo, 4, variable)) if (!ExtractArgumentInfo(value, ref callSiteInfo, 4, variable, context))
return false; return false;
return true; return true;
default: 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))) 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; return false;
@ -560,7 +578,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; 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; callSiteCacheField = null;
callSiteDelegate = null; callSiteDelegate = null;
@ -579,7 +597,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; return true;
} }
struct CallSiteInfo internal struct CallSiteInfo
{ {
public bool Inverted; public bool Inverted;
public ILInstruction BranchAfterInit; public ILInstruction BranchAfterInit;
@ -596,7 +614,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public string MemberName; public string MemberName;
} }
enum BinderMethodKind internal enum BinderMethodKind
{ {
BinaryOperation, BinaryOperation,
Convert, Convert,

Loading…
Cancel
Save