Browse Source

Merge pull request #1608 from icsharpcode/ldvirtdelegate

LdVirtDelegate: Implement transformation of delegate construction with ldvirtftn
pull/1633/head
Siegfried Pammer 6 years ago committed by GitHub
parent
commit
6de6714be2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs
  2. 16
      ICSharpCode.Decompiler/CSharp/Annotations.cs
  3. 20
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  4. 7
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  5. 89
      ICSharpCode.Decompiler/IL/Instructions.cs
  6. 3
      ICSharpCode.Decompiler/IL/Instructions.tt
  7. 32
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs

5
ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs

@ -193,6 +193,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -193,6 +193,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Noop("M3", M3);
#endif
}
public void Test2()
{
Noop("M3.new", new BaseClass().M3);
Noop("M3.new", new SubClass().M3);
}
private void Noop(string name, Action _)
{

16
ICSharpCode.Decompiler/CSharp/Annotations.cs

@ -108,11 +108,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -108,11 +108,17 @@ namespace ICSharpCode.Decompiler.CSharp
if (rr is MethodGroupResolveResult) {
// delegate construction?
var newObj = node.Annotation<NewObj>();
var funcptr = newObj?.Arguments.ElementAtOrDefault(1);
if (funcptr is LdFtn ldftn) {
return ldftn.Method;
} else if (funcptr is LdVirtFtn ldVirtFtn) {
return ldVirtFtn.Method;
if (newObj != null) {
var funcptr = newObj.Arguments.ElementAtOrDefault(1);
if (funcptr is LdFtn ldftn) {
return ldftn.Method;
} else if (funcptr is LdVirtFtn ldVirtFtn) {
return ldVirtFtn.Method;
}
}
var ldVirtDelegate = node.Annotation<LdVirtDelegate>();
if (ldVirtDelegate != null) {
return ldVirtDelegate.Method;
}
}
return rr?.GetSymbol();

20
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -1235,7 +1235,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1235,7 +1235,17 @@ namespace ICSharpCode.Decompiler.CSharp
default:
throw new ArgumentException($"Unknown instruction type: {func.OpCode}");
}
var invokeMethod = inst.Method.DeclaringType.GetDelegateInvokeMethod();
return HandleDelegateConstruction(inst.Method.DeclaringType, method, expectedTargetDetails, thisArg, inst);
}
internal TranslatedExpression Build(LdVirtDelegate inst)
{
return HandleDelegateConstruction(inst.Type, inst.Method, new ExpectedTargetDetails { CallOpCode = OpCode.CallVirt }, inst.Argument, inst);
}
TranslatedExpression HandleDelegateConstruction(IType delegateType, IMethod method, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg, ILInstruction inst)
{
var invokeMethod = delegateType.GetDelegateInvokeMethod();
TranslatedExpression target;
IType targetType;
bool requireTarget;
@ -1288,7 +1298,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1288,7 +1298,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
target = expressionBuilder.TranslateTarget(thisArg,
nonVirtualInvocation: func.OpCode == OpCode.LdFtn,
nonVirtualInvocation: expectedTargetDetails.CallOpCode == OpCode.Call,
memberStatic: method.IsStatic,
memberDeclaringType: method.DeclaringType);
requireTarget = expressionBuilder.HidesVariableWithName(method.Name)
@ -1337,12 +1347,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1337,12 +1347,12 @@ namespace ICSharpCode.Decompiler.CSharp
ide.WithRR(result);
targetExpression = ide;
}
var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(inst.Method.DeclaringType), targetExpression)
var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(delegateType), targetExpression)
.WithILInstruction(inst)
.WithRR(new ConversionResolveResult(
inst.Method.DeclaringType,
delegateType,
result,
Conversion.MethodGroupConversion(method, func.OpCode == OpCode.LdVirtFtn, false)));
Conversion.MethodGroupConversion(method, expectedTargetDetails.CallOpCode == OpCode.CallVirt, false)));
return oce;
}

7
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -347,7 +347,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -347,7 +347,12 @@ namespace ICSharpCode.Decompiler.CSharp
}
return new CallBuilder(this, typeSystem, settings).Build(inst);
}
protected internal override TranslatedExpression VisitLdVirtDelegate(LdVirtDelegate inst, TranslationContext context)
{
return new CallBuilder(this, typeSystem, settings).Build(inst);
}
protected internal override TranslatedExpression VisitNewArr(NewArr inst, TranslationContext context)
{
var dimensions = inst.Indices.Count;

89
ICSharpCode.Decompiler/IL/Instructions.cs

@ -135,6 +135,8 @@ namespace ICSharpCode.Decompiler.IL @@ -135,6 +135,8 @@ namespace ICSharpCode.Decompiler.IL
LdFtn,
/// <summary>Load method pointer</summary>
LdVirtFtn,
/// <summary>Virtual delegate construction</summary>
LdVirtDelegate,
/// <summary>Loads runtime representation of metadata token</summary>
LdTypeToken,
/// <summary>Loads runtime representation of metadata token</summary>
@ -3081,6 +3083,66 @@ namespace ICSharpCode.Decompiler.IL @@ -3081,6 +3083,66 @@ namespace ICSharpCode.Decompiler.IL
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Virtual delegate construction</summary>
public sealed partial class LdVirtDelegate : UnaryInstruction, IInstructionWithMethodOperand
{
public LdVirtDelegate(ILInstruction argument, IType type, IMethod method) : base(OpCode.LdVirtDelegate, argument)
{
this.type = type;
this.method = method;
}
IType type;
/// <summary>Returns the type operand.</summary>
public IType Type {
get { return type; }
set { type = value; InvalidateFlags(); }
}
readonly IMethod method;
/// <summary>Returns the method operand.</summary>
public IMethod Method { get { return method; } }
public override StackType ResultType { get { return StackType.O; } }
protected override InstructionFlags ComputeFlags()
{
return base.ComputeFlags() | InstructionFlags.MayThrow;
}
public override InstructionFlags DirectFlags {
get {
return base.DirectFlags | InstructionFlags.MayThrow;
}
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
WriteILRange(output, options);
output.Write(OpCode);
output.Write(' ');
type.WriteTo(output);
output.Write(' ');
method.WriteTo(output);
output.Write('(');
Argument.WriteTo(output, options);
output.Write(')');
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitLdVirtDelegate(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitLdVirtDelegate(this);
}
public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
{
return visitor.VisitLdVirtDelegate(this, context);
}
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as LdVirtDelegate;
return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type) && method.Equals(o.method);
}
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Loads runtime representation of metadata token</summary>
public sealed partial class LdTypeToken : SimpleInstruction
@ -6550,6 +6612,10 @@ namespace ICSharpCode.Decompiler.IL @@ -6550,6 +6612,10 @@ namespace ICSharpCode.Decompiler.IL
{
Default(inst);
}
protected internal virtual void VisitLdVirtDelegate(LdVirtDelegate inst)
{
Default(inst);
}
protected internal virtual void VisitLdTypeToken(LdTypeToken inst)
{
Default(inst);
@ -6932,6 +6998,10 @@ namespace ICSharpCode.Decompiler.IL @@ -6932,6 +6998,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst);
}
protected internal virtual T VisitLdVirtDelegate(LdVirtDelegate inst)
{
return Default(inst);
}
protected internal virtual T VisitLdTypeToken(LdTypeToken inst)
{
return Default(inst);
@ -7314,6 +7384,10 @@ namespace ICSharpCode.Decompiler.IL @@ -7314,6 +7384,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst, context);
}
protected internal virtual T VisitLdVirtDelegate(LdVirtDelegate inst, C context)
{
return Default(inst, context);
}
protected internal virtual T VisitLdTypeToken(LdTypeToken inst, C context)
{
return Default(inst, context);
@ -7544,6 +7618,7 @@ namespace ICSharpCode.Decompiler.IL @@ -7544,6 +7618,7 @@ namespace ICSharpCode.Decompiler.IL
"ldnull",
"ldftn",
"ldvirtftn",
"ldvirtdelegate",
"ldtypetoken",
"ldmembertoken",
"localloc",
@ -7864,6 +7939,20 @@ namespace ICSharpCode.Decompiler.IL @@ -7864,6 +7939,20 @@ namespace ICSharpCode.Decompiler.IL
method = default(IMethod);
return false;
}
public bool MatchLdVirtDelegate(out ILInstruction argument, out IType type, out IMethod method)
{
var inst = this as LdVirtDelegate;
if (inst != null) {
argument = inst.Argument;
type = inst.Type;
method = inst.Method;
return true;
}
argument = default(ILInstruction);
type = default(IType);
method = default(IMethod);
return false;
}
public bool MatchLdTypeToken(out IType type)
{
var inst = this as LdTypeToken;

3
ICSharpCode.Decompiler/IL/Instructions.tt

@ -217,6 +217,9 @@ @@ -217,6 +217,9 @@
CustomClassName("LdFtn"), NoArguments, HasMethodOperand, ResultType("I")),
new OpCode("ldvirtftn", "Load method pointer",
CustomClassName("LdVirtFtn"), Unary, HasMethodOperand, MayThrow, ResultType("I")),
new OpCode("ldvirtdelegate", "Virtual delegate construction",
CustomClassName("LdVirtDelegate"), Unary, HasTypeOperand, HasMethodOperand,
MayThrow, ResultType("O")),
new OpCode("ldtypetoken", "Loads runtime representation of metadata token",
CustomClassName("LdTypeToken"), NoArguments, HasTypeOperand, ResultType("O")),
new OpCode("ldmembertoken", "Loads runtime representation of metadata token",

32
ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs

@ -297,9 +297,37 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -297,9 +297,37 @@ namespace ICSharpCode.Decompiler.IL.Transforms
inst.ReplaceWith(block);
return;
}
if (TransformDelegateCtorLdVirtFtnToLdVirtDelegate(inst, out LdVirtDelegate ldVirtDelegate)) {
context.Step("new Delegate(target, ldvirtftn Method) -> ldvirtdelegate Delegate Method(target)", inst);
inst.ReplaceWith(ldVirtDelegate);
return;
}
base.VisitNewObj(inst);
}
/// <summary>
/// newobj Delegate..ctor(target, ldvirtftn TargetMethod(target))
/// =>
/// ldvirtdelegate System.Delegate TargetMethod(target)
/// </summary>
bool TransformDelegateCtorLdVirtFtnToLdVirtDelegate(NewObj inst, out LdVirtDelegate ldVirtDelegate)
{
ldVirtDelegate = null;
if (inst.Method.DeclaringType.Kind != TypeKind.Delegate)
return false;
if (inst.Arguments.Count != 2)
return false;
if (!(inst.Arguments[1] is LdVirtFtn ldVirtFtn))
return false;
if (!SemanticHelper.IsPure(inst.Arguments[0].Flags))
return false;
if (!inst.Arguments[0].Match(ldVirtFtn.Argument).Success)
return false;
ldVirtDelegate = new LdVirtDelegate(inst.Arguments[0], inst.Method.DeclaringType, ldVirtFtn.Method)
.WithILRange(inst).WithILRange(ldVirtFtn).WithILRange(ldVirtFtn.Argument);
return true;
}
/// <summary>
/// newobj Span..ctor(localloc(conv i4->u &lt;zero extend&gt;(ldc.i4 sizeInBytes)), numberOfElementsExpr)
/// =>
@ -503,10 +531,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -503,10 +531,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
if (!(dynamicCompoundAssign.Target is DynamicGetMemberInstruction getMember))
return false;
if (!isEvent.Argument.Match(getMember.Target).Success)
return false;
if (!SemanticHelper.IsPure(isEvent.Argument.Flags))
return false;
if (!isEvent.Argument.Match(getMember.Target).Success)
return false;
if (!(trueInst is DynamicInvokeMemberInstruction invokeMember))
return false;
if (!(invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.InvokeSpecialName) && invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.ResultDiscarded)))

Loading…
Cancel
Save