Skip to content

Commit

Permalink
Fix #1388: Async await on dynamic expression is not detected.
Browse files Browse the repository at this point in the history
Fix #1928: dynamic JObject() not decompile correctly
  • Loading branch information
siegfriedpammer committed Jun 27, 2022
1 parent 7f0f728 commit dbd4f3d
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,7 @@ void AnalyzeStateMachine(ILFunction function)
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
Expand Down
114 changes: 66 additions & 48 deletions ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,48 +45,32 @@ public void Run(ILFunction function, ILTransformContext context)
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;
Expand Down Expand Up @@ -141,12 +125,46 @@ public void Run(ILFunction function, ILTransformContext context)
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)
{
Expand Down Expand Up @@ -272,7 +290,7 @@ ILInstruction MakeDynamicInstruction(CallSiteInfo callsite, CallVirt targetInvok
}
}

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;
Expand Down Expand Up @@ -397,7 +415,7 @@ bool ScanCallSiteInitBlock(Block callSiteInitBlock, IField callSiteCacheField, I
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":
Expand Down Expand Up @@ -436,7 +454,7 @@ bool ScanCallSiteInitBlock(Block callSiteInitBlock, IField callSiteCacheField, I
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":
Expand Down Expand Up @@ -484,7 +502,7 @@ bool ScanCallSiteInitBlock(Block callSiteInitBlock, IField callSiteCacheField, I
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":
Expand Down Expand Up @@ -523,15 +541,15 @@ bool ScanCallSiteInitBlock(Block callSiteInitBlock, IField callSiteCacheField, I
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:
return false;
}
}

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;
Expand Down Expand Up @@ -560,7 +578,7 @@ bool ExtractArgumentInfo(ILInstruction value, ref CallSiteInfo callSiteInfo, int
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;
Expand All @@ -579,7 +597,7 @@ bool MatchCallSiteCacheNullCheck(ILInstruction condition, out IField callSiteCac
return true;
}

struct CallSiteInfo
internal struct CallSiteInfo
{
public bool Inverted;
public ILInstruction BranchAfterInit;
Expand All @@ -596,7 +614,7 @@ struct CallSiteInfo
public string MemberName;
}

enum BinderMethodKind
internal enum BinderMethodKind
{
BinaryOperation,
Convert,
Expand Down

0 comments on commit dbd4f3d

Please sign in to comment.