|
|
@ -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, |
|
|
|