mirror of https://github.com/icsharpcode/ILSpy.git
8 changed files with 742 additions and 5 deletions
@ -0,0 +1,593 @@
@@ -0,0 +1,593 @@
|
||||
// Copyright (c) 2018 Siegfried Pammer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
using System.Linq.Expressions; |
||||
using ICSharpCode.Decompiler.TypeSystem; |
||||
|
||||
namespace ICSharpCode.Decompiler.IL.Transforms |
||||
{ |
||||
/// <summary>
|
||||
/// Transforms the "callsite initialization pattern" into DynamicInstructions.
|
||||
/// </summary>
|
||||
public class DynamicCallSiteTransform : IILTransform |
||||
{ |
||||
ILTransformContext context; |
||||
|
||||
const string CallSiteTypeName = "System.Runtime.CompilerServices.CallSite"; |
||||
const string CSharpBinderTypeName = "Microsoft.CSharp.RuntimeBinder.Binder"; |
||||
|
||||
public void Run(ILFunction function, ILTransformContext context) |
||||
{ |
||||
if (!context.Settings.DynamicExpressions) |
||||
return; |
||||
|
||||
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 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, 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); |
||||
} |
||||
|
||||
foreach (var invokeCall in function.Descendants.OfType<CallVirt>()) { |
||||
if (invokeCall.Method.DeclaringType.Kind != TypeKind.Delegate || invokeCall.Method.Name != "Invoke" || invokeCall.Arguments.Count == 0) |
||||
continue; |
||||
var firstArgument = invokeCall.Arguments[0]; |
||||
if (firstArgument.MatchLdLoc(out var stackSlot) && stackSlot.Kind == VariableKind.StackSlot && stackSlot.IsSingleDefinition) { |
||||
firstArgument = ((StLoc)stackSlot.StoreInstructions[0]).Value; |
||||
} |
||||
if (!firstArgument.MatchLdFld(out var cacheFieldLoad, out var targetField)) |
||||
continue; |
||||
if (!cacheFieldLoad.MatchLdsFld(out var cacheField)) |
||||
continue; |
||||
if (!callsites.TryGetValue(cacheField, out var callsite)) |
||||
continue; |
||||
context.Stepper.Step("Transform callsite for " + callsite.MemberName); |
||||
var deadArguments = new List<ILInstruction>(); |
||||
ILInstruction replacement = MakeDynamicInstruction(callsite, invokeCall, deadArguments); |
||||
if (replacement == null) |
||||
continue; |
||||
invokeCall.ReplaceWith(replacement); |
||||
Debug.Assert(callsite.ConditionalJumpToInit?.Parent is Block); |
||||
var block = ((Block)callsite.ConditionalJumpToInit.Parent); |
||||
if (callsite.Inverted) { |
||||
block.Instructions.Remove(callsite.ConditionalJumpToInit); |
||||
callsite.BranchAfterInit.ReplaceWith(callsite.ConditionalJumpToInit.TrueInst); |
||||
} else { |
||||
block.Instructions.Remove(callsite.ConditionalJumpToInit); |
||||
} |
||||
foreach (var arg in deadArguments) { |
||||
if (arg.MatchLdLoc(out var temporary) && temporary.Kind == VariableKind.StackSlot && temporary.IsSingleDefinition && temporary.LoadCount == 0) { |
||||
StLoc stLoc = (StLoc)temporary.StoreInstructions[0]; |
||||
if (stLoc.Parent is Block storeParentBlock) { |
||||
var value = stLoc.Value; |
||||
if (value.MatchLdsFld(out var cacheFieldCopy) && cacheFieldCopy.Equals(cacheField)) |
||||
storeParentBlock.Instructions.RemoveAt(stLoc.ChildIndex); |
||||
if (value.MatchLdFld(out cacheFieldLoad, out var targetFieldCopy) && cacheFieldLoad.MatchLdsFld(out cacheFieldCopy) && cacheField.Equals(cacheFieldCopy) && targetField.Equals(targetFieldCopy)) |
||||
storeParentBlock.Instructions.RemoveAt(stLoc.ChildIndex); |
||||
} |
||||
} |
||||
} |
||||
modifiedContainers.Add((BlockContainer)block.Parent); |
||||
} |
||||
|
||||
foreach (var container in modifiedContainers) |
||||
container.SortBlocks(deleteUnreachableBlocks: true); |
||||
} |
||||
|
||||
ILInstruction MakeDynamicInstruction(CallSiteInfo callsite, CallVirt targetInvokeCall, List<ILInstruction> deadArguments) |
||||
{ |
||||
switch (callsite.Kind) { |
||||
case BinderMethodKind.BinaryOperation: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicBinaryOperatorInstruction( |
||||
binderFlags: callsite.Flags, |
||||
operation: callsite.Operation, |
||||
context: callsite.Context, |
||||
leftArgumentInfo: callsite.ArgumentInfos[0], |
||||
left: targetInvokeCall.Arguments[2], |
||||
rightArgumentInfo: callsite.ArgumentInfos[1], |
||||
right: targetInvokeCall.Arguments[3] |
||||
); |
||||
case BinderMethodKind.Convert: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicConvertInstruction( |
||||
binderFlags: callsite.Flags, |
||||
context: callsite.Context, |
||||
type: callsite.ConvertTargetType, |
||||
argument: targetInvokeCall.Arguments[2] |
||||
); |
||||
case BinderMethodKind.GetIndex: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicGetIndexInstruction( |
||||
binderFlags: callsite.Flags, |
||||
context: callsite.Context, |
||||
argumentInfo: callsite.ArgumentInfos, |
||||
arguments: targetInvokeCall.Arguments.Skip(2).ToArray() |
||||
); |
||||
case BinderMethodKind.GetMember: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicGetMemberInstruction( |
||||
binderFlags: callsite.Flags, |
||||
name: callsite.MemberName, |
||||
context: callsite.Context, |
||||
targetArgumentInfo: callsite.ArgumentInfos[0], |
||||
target: targetInvokeCall.Arguments[2] |
||||
); |
||||
case BinderMethodKind.Invoke: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicInvokeInstruction( |
||||
binderFlags: callsite.Flags, |
||||
context: callsite.Context, |
||||
argumentInfo: callsite.ArgumentInfos, |
||||
arguments: targetInvokeCall.Arguments.Skip(2).ToArray() |
||||
); |
||||
case BinderMethodKind.InvokeConstructor: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicInvokeConstructorInstruction( |
||||
binderFlags: callsite.Flags, |
||||
context: callsite.Context, |
||||
argumentInfo: callsite.ArgumentInfos, |
||||
arguments: targetInvokeCall.Arguments.Skip(2).ToArray() |
||||
); |
||||
case BinderMethodKind.InvokeMember: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicInvokeMemberInstruction( |
||||
binderFlags: callsite.Flags, |
||||
name: callsite.MemberName, |
||||
typeArguments: callsite.TypeArguments, |
||||
context: callsite.Context, |
||||
argumentInfo: callsite.ArgumentInfos, |
||||
arguments: targetInvokeCall.Arguments.Skip(2).ToArray() |
||||
); |
||||
case BinderMethodKind.IsEvent: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicIsEventInstruction( |
||||
binderFlags: callsite.Flags, |
||||
name: callsite.MemberName, |
||||
context: callsite.Context, |
||||
argument: targetInvokeCall.Arguments[2] |
||||
); |
||||
case BinderMethodKind.SetIndex: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicSetIndexInstruction( |
||||
binderFlags: callsite.Flags, |
||||
context: callsite.Context, |
||||
argumentInfo: callsite.ArgumentInfos, |
||||
arguments: targetInvokeCall.Arguments.Skip(2).ToArray() |
||||
); |
||||
case BinderMethodKind.SetMember: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicSetMemberInstruction( |
||||
binderFlags: callsite.Flags, |
||||
name: callsite.MemberName, |
||||
context: callsite.Context, |
||||
targetArgumentInfo: callsite.ArgumentInfos[0], |
||||
target: targetInvokeCall.Arguments[2], |
||||
valueArgumentInfo: callsite.ArgumentInfos[1], |
||||
value: targetInvokeCall.Arguments[3] |
||||
); |
||||
case BinderMethodKind.UnaryOperation: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicUnaryOperatorInstruction( |
||||
binderFlags: callsite.Flags, |
||||
operation: callsite.Operation, |
||||
context: callsite.Context, |
||||
operandArgumentInfo: callsite.ArgumentInfos[0], |
||||
operand: targetInvokeCall.Arguments[2] |
||||
); |
||||
default: |
||||
throw new NotSupportedException(); |
||||
} |
||||
throw new NotImplementedException(); |
||||
} |
||||
|
||||
bool ScanCallSiteInitBlock(Block callSiteInitBlock, IField callSiteCacheField, out CallSiteInfo callSiteInfo, out Block blockAfterInit) |
||||
{ |
||||
callSiteInfo = default(CallSiteInfo); |
||||
blockAfterInit = null; |
||||
int instCount = callSiteInitBlock.Instructions.Count; |
||||
if (callSiteInitBlock.IncomingEdgeCount != 1 || instCount < 2) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[instCount - 1].MatchBranch(out blockAfterInit)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[instCount - 2].MatchStsFld(out var field, out var value) || field != callSiteCacheField) |
||||
return false; |
||||
if (!(value is Call createBinderCall) || createBinderCall.Method.TypeArguments.Count != 0 || createBinderCall.Arguments.Count != 1 || createBinderCall.Method.Name != "Create" || createBinderCall.Method.DeclaringType.FullName != CallSiteTypeName || createBinderCall.Method.DeclaringType.TypeArguments.Count != 1) |
||||
return false; |
||||
if (!(createBinderCall.Arguments[0] is Call binderCall) || binderCall.Method.DeclaringType.FullName != CSharpBinderTypeName || binderCall.Method.DeclaringType.TypeParameterCount != 0) |
||||
return false; |
||||
callSiteInfo.InitBlock = callSiteInitBlock; |
||||
switch (binderCall.Method.Name) { |
||||
case "IsEvent": |
||||
callSiteInfo.Kind = BinderMethodKind.IsEvent; |
||||
// In the case of Binder.IsEvent all arguments should already be properly inlined, as there is no array initializer:
|
||||
// Scan arguments: binder flags, member name, context type
|
||||
if (binderCall.Arguments.Count != 3) |
||||
return false; |
||||
if (!binderCall.Arguments[0].MatchLdcI4(out int binderFlagsInteger)) |
||||
return false; |
||||
callSiteInfo.Flags = (CSharpBinderFlags)binderFlagsInteger; |
||||
if (!binderCall.Arguments[1].MatchLdStr(out string name)) |
||||
return false; |
||||
callSiteInfo.MemberName = name; |
||||
if (!TransformExpressionTrees.MatchGetTypeFromHandle(binderCall.Arguments[2], out var contextType)) |
||||
return false; |
||||
callSiteInfo.Context = contextType; |
||||
return true; |
||||
case "Convert": |
||||
callSiteInfo.Kind = BinderMethodKind.Convert; |
||||
// In the case of Binder.Convert all arguments should already be properly inlined, as there is no array initializer:
|
||||
// Scan arguments: binder flags, target type, context type
|
||||
if (binderCall.Arguments.Count != 3) |
||||
return false; |
||||
if (!binderCall.Arguments[0].MatchLdcI4(out binderFlagsInteger)) |
||||
return false; |
||||
callSiteInfo.Flags = (CSharpBinderFlags)binderFlagsInteger; |
||||
if (!TransformExpressionTrees.MatchGetTypeFromHandle(binderCall.Arguments[1], out var targetType)) |
||||
return false; |
||||
callSiteInfo.ConvertTargetType = targetType; |
||||
if (!TransformExpressionTrees.MatchGetTypeFromHandle(binderCall.Arguments[2], out contextType)) |
||||
return false; |
||||
callSiteInfo.Context = contextType; |
||||
return true; |
||||
case "InvokeMember": |
||||
callSiteInfo.Kind = BinderMethodKind.InvokeMember; |
||||
if (binderCall.Arguments.Count != 5) |
||||
return false; |
||||
// First argument: binder flags
|
||||
// The value must be a single ldc.i4 instruction.
|
||||
if (!binderCall.Arguments[0].MatchLdLoc(out var variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[0].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!value.MatchLdcI4(out binderFlagsInteger)) |
||||
return false; |
||||
callSiteInfo.Flags = (CSharpBinderFlags)binderFlagsInteger; |
||||
// Second argument: method name
|
||||
// The value must be a single ldstr instruction.
|
||||
if (!binderCall.Arguments[1].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[1].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!value.MatchLdStr(out name)) |
||||
return false; |
||||
callSiteInfo.MemberName = name; |
||||
// Third argument: type arguments
|
||||
// The value must be either ldnull (no type arguments) or an array initializer pattern.
|
||||
if (!binderCall.Arguments[2].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[2].MatchStLoc(variable, out value)) |
||||
return false; |
||||
int numberOfTypeArguments = 0; |
||||
if (!value.MatchLdNull()) { |
||||
if (value is NewArr typeArgsNewArr && typeArgsNewArr.Type.IsKnownType(KnownTypeCode.Type) && typeArgsNewArr.Indices.Count == 1 && typeArgsNewArr.Indices[0].MatchLdcI4(out numberOfTypeArguments)) { |
||||
if (!TransformArrayInitializers.HandleSimpleArrayInitializer(callSiteInitBlock, 3, variable, typeArgsNewArr.Type, numberOfTypeArguments, out var typeArguments, out _)) |
||||
return false; |
||||
int i = 0; |
||||
callSiteInfo.TypeArguments = new IType[numberOfTypeArguments]; |
||||
foreach (var typeArg in typeArguments) { |
||||
if (!TransformExpressionTrees.MatchGetTypeFromHandle(typeArg, out var type)) |
||||
return false; |
||||
callSiteInfo.TypeArguments[i] = type; |
||||
i++; |
||||
} |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
// Fourth argument: context type
|
||||
if (!binderCall.Arguments[3].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[3 + numberOfTypeArguments].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!TransformExpressionTrees.MatchGetTypeFromHandle(value, out contextType)) |
||||
return false; |
||||
callSiteInfo.Context = contextType; |
||||
// Fifth argument: call parameter info
|
||||
if (!binderCall.Arguments[4].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[4 + numberOfTypeArguments].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (value is NewArr newArr && newArr.Type.FullName == "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" && newArr.Indices.Count == 1 && newArr.Indices[0].MatchLdcI4(out int numberOfArguments)) { |
||||
if (!TransformArrayInitializers.HandleSimpleArrayInitializer(callSiteInitBlock, 5 + numberOfTypeArguments, variable, newArr.Type, numberOfArguments, out var arguments, out _)) |
||||
return false; |
||||
int i = 0; |
||||
callSiteInfo.ArgumentInfos = new CSharpArgumentInfo[numberOfArguments]; |
||||
foreach (var arg in arguments) { |
||||
if (!(arg is Call createCall)) |
||||
return false; |
||||
if (!(createCall.Method.Name == "Create" && createCall.Method.DeclaringType.FullName == "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" && createCall.Arguments.Count == 2)) |
||||
return false; |
||||
if (!createCall.Arguments[0].MatchLdcI4(out var argumentInfoFlags)) |
||||
return false; |
||||
string argumentName = null; |
||||
if (!createCall.Arguments[1].MatchLdStr(out argumentName)) |
||||
if (!createCall.Arguments[1].MatchLdNull()) |
||||
return false; |
||||
callSiteInfo.ArgumentInfos[i] = new CSharpArgumentInfo { Flags = (CSharpArgumentInfoFlags)argumentInfoFlags, Name = argumentName }; |
||||
i++; |
||||
} |
||||
} |
||||
return true; |
||||
case "GetMember": |
||||
case "SetMember": |
||||
callSiteInfo.Kind = binderCall.Method.Name == "GetMember" ? BinderMethodKind.GetMember : BinderMethodKind.SetMember; |
||||
if (binderCall.Arguments.Count != 4) |
||||
return false; |
||||
// First argument: binder flags
|
||||
// The value must be a single ldc.i4 instruction.
|
||||
if (!binderCall.Arguments[0].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[0].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!value.MatchLdcI4(out binderFlagsInteger)) |
||||
return false; |
||||
callSiteInfo.Flags = (CSharpBinderFlags)binderFlagsInteger; |
||||
// Second argument: method name
|
||||
// The value must be a single ldstr instruction.
|
||||
if (!binderCall.Arguments[1].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[1].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!value.MatchLdStr(out name)) |
||||
return false; |
||||
callSiteInfo.MemberName = name; |
||||
// Third argument: context type
|
||||
if (!binderCall.Arguments[2].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[2].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!TransformExpressionTrees.MatchGetTypeFromHandle(value, out contextType)) |
||||
return false; |
||||
callSiteInfo.Context = contextType; |
||||
// Fourth argument: call parameter info
|
||||
if (!binderCall.Arguments[3].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[3].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (value is NewArr newArr2 && newArr2.Type.FullName == "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" && newArr2.Indices.Count == 1 && newArr2.Indices[0].MatchLdcI4(out numberOfArguments)) { |
||||
if (!TransformArrayInitializers.HandleSimpleArrayInitializer(callSiteInitBlock, 4, variable, newArr2.Type, numberOfArguments, out var arguments, out _)) |
||||
return false; |
||||
int i = 0; |
||||
callSiteInfo.ArgumentInfos = new CSharpArgumentInfo[numberOfArguments]; |
||||
foreach (var arg in arguments) { |
||||
if (!(arg is Call createCall)) |
||||
return false; |
||||
if (!(createCall.Method.Name == "Create" && createCall.Method.DeclaringType.FullName == "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" && createCall.Arguments.Count == 2)) |
||||
return false; |
||||
if (!createCall.Arguments[0].MatchLdcI4(out var argumentInfoFlags)) |
||||
return false; |
||||
string argumentName = null; |
||||
if (!createCall.Arguments[1].MatchLdStr(out argumentName)) |
||||
if (!createCall.Arguments[1].MatchLdNull()) |
||||
return false; |
||||
callSiteInfo.ArgumentInfos[i] = new CSharpArgumentInfo { Flags = (CSharpArgumentInfoFlags)argumentInfoFlags, Name = argumentName }; |
||||
i++; |
||||
} |
||||
} |
||||
return true; |
||||
case "GetIndex": |
||||
case "SetIndex": |
||||
case "InvokeConstructor": |
||||
case "Invoke": |
||||
switch (binderCall.Method.Name) { |
||||
case "GetIndex": |
||||
callSiteInfo.Kind = BinderMethodKind.GetIndex; |
||||
break; |
||||
case "SetIndex": |
||||
callSiteInfo.Kind = BinderMethodKind.SetIndex; |
||||
break; |
||||
case "InvokeConstructor": |
||||
callSiteInfo.Kind = BinderMethodKind.InvokeConstructor; |
||||
break; |
||||
case "Invoke": |
||||
callSiteInfo.Kind = BinderMethodKind.Invoke; |
||||
break; |
||||
default: |
||||
throw new NotSupportedException(); |
||||
} |
||||
if (binderCall.Arguments.Count != 3) |
||||
return false; |
||||
// First argument: binder flags
|
||||
// The value must be a single ldc.i4 instruction.
|
||||
if (!binderCall.Arguments[0].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[0].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!value.MatchLdcI4(out binderFlagsInteger)) |
||||
return false; |
||||
callSiteInfo.Flags = (CSharpBinderFlags)binderFlagsInteger; |
||||
// Second argument: context type
|
||||
if (!binderCall.Arguments[1].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[1].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!TransformExpressionTrees.MatchGetTypeFromHandle(value, out contextType)) |
||||
return false; |
||||
callSiteInfo.Context = contextType; |
||||
// Third argument: call parameter info
|
||||
if (!binderCall.Arguments[2].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[2].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (value is NewArr newArr3 && newArr3.Type.FullName == "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" && newArr3.Indices.Count == 1 && newArr3.Indices[0].MatchLdcI4(out numberOfArguments)) { |
||||
if (!TransformArrayInitializers.HandleSimpleArrayInitializer(callSiteInitBlock, 3, variable, newArr3.Type, numberOfArguments, out var arguments, out _)) |
||||
return false; |
||||
int i = 0; |
||||
callSiteInfo.ArgumentInfos = new CSharpArgumentInfo[numberOfArguments]; |
||||
foreach (var arg in arguments) { |
||||
if (!(arg is Call createCall)) |
||||
return false; |
||||
if (!(createCall.Method.Name == "Create" && createCall.Method.DeclaringType.FullName == "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" && createCall.Arguments.Count == 2)) |
||||
return false; |
||||
if (!createCall.Arguments[0].MatchLdcI4(out var argumentInfoFlags)) |
||||
return false; |
||||
string argumentName = null; |
||||
if (!createCall.Arguments[1].MatchLdStr(out argumentName)) |
||||
if (!createCall.Arguments[1].MatchLdNull()) |
||||
return false; |
||||
callSiteInfo.ArgumentInfos[i] = new CSharpArgumentInfo { Flags = (CSharpArgumentInfoFlags)argumentInfoFlags, Name = argumentName }; |
||||
i++; |
||||
} |
||||
} |
||||
return true; |
||||
case "UnaryOperation": |
||||
case "BinaryOperation": |
||||
callSiteInfo.Kind = binderCall.Method.Name == "BinaryOperation" ? BinderMethodKind.BinaryOperation : BinderMethodKind.UnaryOperation; |
||||
if (binderCall.Arguments.Count != 4) |
||||
return false; |
||||
// First argument: binder flags
|
||||
// The value must be a single ldc.i4 instruction.
|
||||
if (!binderCall.Arguments[0].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[0].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!value.MatchLdcI4(out binderFlagsInteger)) |
||||
return false; |
||||
callSiteInfo.Flags = (CSharpBinderFlags)binderFlagsInteger; |
||||
// Second argument: operation
|
||||
// The value must be a single ldc.i4 instruction.
|
||||
if (!binderCall.Arguments[1].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[1].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!value.MatchLdcI4(out int operation)) |
||||
return false; |
||||
callSiteInfo.Operation = (ExpressionType)operation; |
||||
// Third argument: context type
|
||||
if (!binderCall.Arguments[2].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[2].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!TransformExpressionTrees.MatchGetTypeFromHandle(value, out contextType)) |
||||
return false; |
||||
callSiteInfo.Context = contextType; |
||||
// Fourth argument: call parameter info
|
||||
if (!binderCall.Arguments[3].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[3].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (value is NewArr newArr4 && newArr4.Type.FullName == "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" && newArr4.Indices.Count == 1 && newArr4.Indices[0].MatchLdcI4(out numberOfArguments)) { |
||||
if (!TransformArrayInitializers.HandleSimpleArrayInitializer(callSiteInitBlock, 4, variable, newArr4.Type, numberOfArguments, out var arguments, out _)) |
||||
return false; |
||||
int i = 0; |
||||
callSiteInfo.ArgumentInfos = new CSharpArgumentInfo[numberOfArguments]; |
||||
foreach (var arg in arguments) { |
||||
if (!(arg is Call createCall)) |
||||
return false; |
||||
if (!(createCall.Method.Name == "Create" && createCall.Method.DeclaringType.FullName == "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" && createCall.Arguments.Count == 2)) |
||||
return false; |
||||
if (!createCall.Arguments[0].MatchLdcI4(out var argumentInfoFlags)) |
||||
return false; |
||||
string argumentName = null; |
||||
if (!createCall.Arguments[1].MatchLdStr(out argumentName)) |
||||
if (!createCall.Arguments[1].MatchLdNull()) |
||||
return false; |
||||
callSiteInfo.ArgumentInfos[i] = new CSharpArgumentInfo { Flags = (CSharpArgumentInfoFlags)argumentInfoFlags, Name = argumentName }; |
||||
i++; |
||||
} |
||||
} |
||||
return true; |
||||
default: |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
bool MatchCallSiteCacheNullCheck(ILInstruction condition, out IField callSiteCacheField, out IType callSiteDelegate, out bool invertBranches) |
||||
{ |
||||
callSiteCacheField = null; |
||||
callSiteDelegate = null; |
||||
invertBranches = false; |
||||
if (!condition.MatchCompEqualsNull(out var argument)) { |
||||
if (!condition.MatchCompNotEqualsNull(out argument)) |
||||
return false; |
||||
invertBranches = true; |
||||
} |
||||
if (!argument.MatchLdsFld(out callSiteCacheField) || callSiteCacheField.ReturnType.TypeArguments.Count != 1 || callSiteCacheField.ReturnType.FullName != CallSiteTypeName) |
||||
return false; |
||||
callSiteDelegate = callSiteCacheField.ReturnType.TypeArguments[0]; |
||||
if (callSiteDelegate.Kind != TypeKind.Delegate) |
||||
return false; |
||||
return true; |
||||
} |
||||
|
||||
struct CallSiteInfo |
||||
{ |
||||
public bool Inverted; |
||||
public ILInstruction BranchAfterInit; |
||||
public IfInstruction ConditionalJumpToInit; |
||||
public Block InitBlock; |
||||
public IType DelegateType; |
||||
public BinderMethodKind Kind; |
||||
public CSharpBinderFlags Flags; |
||||
public ExpressionType Operation; |
||||
public IType Context; |
||||
public IType ConvertTargetType; |
||||
public IType[] TypeArguments; |
||||
public CSharpArgumentInfo[] ArgumentInfos; |
||||
public string MemberName; |
||||
} |
||||
|
||||
enum BinderMethodKind |
||||
{ |
||||
BinaryOperation, |
||||
Convert, |
||||
GetIndex, |
||||
GetMember, |
||||
Invoke, |
||||
InvokeConstructor, |
||||
InvokeMember, |
||||
IsEvent, |
||||
SetIndex, |
||||
SetMember, |
||||
UnaryOperation |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue