From ec18094c65fab1eed25d046cf482e20692b007f4 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 3 Aug 2019 21:49:01 +0200 Subject: [PATCH] Implement transformation of delegate construction with ldvirtftn. --- .../TestCases/Pretty/DelegateConstruction.cs | 5 ++ ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 20 +++-- .../CSharp/ExpressionBuilder.cs | 7 +- ICSharpCode.Decompiler/IL/Instructions.cs | 89 +++++++++++++++++++ ICSharpCode.Decompiler/IL/Instructions.tt | 3 + .../IL/Transforms/ExpressionTransforms.cs | 26 ++++++ 6 files changed, 144 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs index faa0a7d5c..4f3ed04a4 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs @@ -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 _) { diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 020a402b5..9c5ee34c2 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -1232,7 +1232,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; @@ -1285,7 +1295,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) @@ -1334,12 +1344,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; } diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index fefd35111..0eee44f79 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -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; diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index cfa1c9f0a..5788e8901 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -135,6 +135,8 @@ namespace ICSharpCode.Decompiler.IL LdFtn, /// Load method pointer LdVirtFtn, + /// Virtual delegate construction + LdVirtDelegate, /// Loads runtime representation of metadata token LdTypeToken, /// Loads runtime representation of metadata token @@ -3081,6 +3083,66 @@ namespace ICSharpCode.Decompiler.IL } } namespace ICSharpCode.Decompiler.IL +{ + /// Virtual delegate construction + 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; + /// Returns the type operand. + public IType Type { + get { return type; } + set { type = value; InvalidateFlags(); } + } + readonly IMethod method; + /// Returns the method operand. + 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(ILVisitor visitor) + { + return visitor.VisitLdVirtDelegate(this); + } + public override T AcceptVisitor(ILVisitor 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 { /// Loads runtime representation of metadata token public sealed partial class LdTypeToken : SimpleInstruction @@ -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 { 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 { 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 "ldnull", "ldftn", "ldvirtftn", + "ldvirtdelegate", "ldtypetoken", "ldmembertoken", "localloc", @@ -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; diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 31407d783..1890424b7 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -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", diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 826cb7f05..b16fb6545 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -297,9 +297,35 @@ 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); } + /// + /// newobj Delegate..ctor(target, ldvirtftn TargetMethod(target)) + /// => + /// ldvirtdelegate System.Delegate TargetMethod(target) + /// + 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 (!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; + } + /// /// newobj Span..ctor(localloc(conv i4->u <zero extend>(ldc.i4 sizeInBytes)), numberOfElementsExpr) /// =>