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)
/// =>