Browse Source

Implement transformation of delegate construction with ldvirtftn.

pull/1608/head
Siegfried Pammer 6 years ago
parent
commit
ec18094c65
  1. 5
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs
  2. 20
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  3. 7
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  4. 89
      ICSharpCode.Decompiler/IL/Instructions.cs
  5. 3
      ICSharpCode.Decompiler/IL/Instructions.tt
  6. 26
      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 _)
{

20
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -1232,7 +1232,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -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 @@ -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 @@ -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;
}

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

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

@ -297,9 +297,35 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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);
}
/// <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 (!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)
/// =>

Loading…
Cancel
Save