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
Noop("M3", M3); Noop("M3", M3);
#endif #endif
} }
public void Test2()
{
Noop("M3.new", new BaseClass().M3);
Noop("M3.new", new SubClass().M3);
}
private void Noop(string name, Action _) private void Noop(string name, Action _)
{ {

20
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -1232,7 +1232,17 @@ namespace ICSharpCode.Decompiler.CSharp
default: default:
throw new ArgumentException($"Unknown instruction type: {func.OpCode}"); 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; TranslatedExpression target;
IType targetType; IType targetType;
bool requireTarget; bool requireTarget;
@ -1285,7 +1295,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
target = expressionBuilder.TranslateTarget(thisArg, target = expressionBuilder.TranslateTarget(thisArg,
nonVirtualInvocation: func.OpCode == OpCode.LdFtn, nonVirtualInvocation: expectedTargetDetails.CallOpCode == OpCode.Call,
memberStatic: method.IsStatic, memberStatic: method.IsStatic,
memberDeclaringType: method.DeclaringType); memberDeclaringType: method.DeclaringType);
requireTarget = expressionBuilder.HidesVariableWithName(method.Name) requireTarget = expressionBuilder.HidesVariableWithName(method.Name)
@ -1334,12 +1344,12 @@ namespace ICSharpCode.Decompiler.CSharp
ide.WithRR(result); ide.WithRR(result);
targetExpression = ide; targetExpression = ide;
} }
var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(inst.Method.DeclaringType), targetExpression) var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(delegateType), targetExpression)
.WithILInstruction(inst) .WithILInstruction(inst)
.WithRR(new ConversionResolveResult( .WithRR(new ConversionResolveResult(
inst.Method.DeclaringType, delegateType,
result, result,
Conversion.MethodGroupConversion(method, func.OpCode == OpCode.LdVirtFtn, false))); Conversion.MethodGroupConversion(method, expectedTargetDetails.CallOpCode == OpCode.CallVirt, false)));
return oce; return oce;
} }

7
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

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

89
ICSharpCode.Decompiler/IL/Instructions.cs

@ -135,6 +135,8 @@ namespace ICSharpCode.Decompiler.IL
LdFtn, LdFtn,
/// <summary>Load method pointer</summary> /// <summary>Load method pointer</summary>
LdVirtFtn, LdVirtFtn,
/// <summary>Virtual delegate construction</summary>
LdVirtDelegate,
/// <summary>Loads runtime representation of metadata token</summary> /// <summary>Loads runtime representation of metadata token</summary>
LdTypeToken, LdTypeToken,
/// <summary>Loads runtime representation of metadata token</summary> /// <summary>Loads runtime representation of metadata token</summary>
@ -3081,6 +3083,66 @@ namespace ICSharpCode.Decompiler.IL
} }
} }
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> /// <summary>Loads runtime representation of metadata token</summary>
public sealed partial class LdTypeToken : SimpleInstruction public sealed partial class LdTypeToken : SimpleInstruction
@ -6550,6 +6612,10 @@ namespace ICSharpCode.Decompiler.IL
{ {
Default(inst); Default(inst);
} }
protected internal virtual void VisitLdVirtDelegate(LdVirtDelegate inst)
{
Default(inst);
}
protected internal virtual void VisitLdTypeToken(LdTypeToken inst) protected internal virtual void VisitLdTypeToken(LdTypeToken inst)
{ {
Default(inst); Default(inst);
@ -6932,6 +6998,10 @@ namespace ICSharpCode.Decompiler.IL
{ {
return Default(inst); return Default(inst);
} }
protected internal virtual T VisitLdVirtDelegate(LdVirtDelegate inst)
{
return Default(inst);
}
protected internal virtual T VisitLdTypeToken(LdTypeToken inst) protected internal virtual T VisitLdTypeToken(LdTypeToken inst)
{ {
return Default(inst); return Default(inst);
@ -7314,6 +7384,10 @@ namespace ICSharpCode.Decompiler.IL
{ {
return Default(inst, context); 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) protected internal virtual T VisitLdTypeToken(LdTypeToken inst, C context)
{ {
return Default(inst, context); return Default(inst, context);
@ -7544,6 +7618,7 @@ namespace ICSharpCode.Decompiler.IL
"ldnull", "ldnull",
"ldftn", "ldftn",
"ldvirtftn", "ldvirtftn",
"ldvirtdelegate",
"ldtypetoken", "ldtypetoken",
"ldmembertoken", "ldmembertoken",
"localloc", "localloc",
@ -7864,6 +7939,20 @@ namespace ICSharpCode.Decompiler.IL
method = default(IMethod); method = default(IMethod);
return false; 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) public bool MatchLdTypeToken(out IType type)
{ {
var inst = this as LdTypeToken; var inst = this as LdTypeToken;

3
ICSharpCode.Decompiler/IL/Instructions.tt

@ -217,6 +217,9 @@
CustomClassName("LdFtn"), NoArguments, HasMethodOperand, ResultType("I")), CustomClassName("LdFtn"), NoArguments, HasMethodOperand, ResultType("I")),
new OpCode("ldvirtftn", "Load method pointer", new OpCode("ldvirtftn", "Load method pointer",
CustomClassName("LdVirtFtn"), Unary, HasMethodOperand, MayThrow, ResultType("I")), 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", new OpCode("ldtypetoken", "Loads runtime representation of metadata token",
CustomClassName("LdTypeToken"), NoArguments, HasTypeOperand, ResultType("O")), CustomClassName("LdTypeToken"), NoArguments, HasTypeOperand, ResultType("O")),
new OpCode("ldmembertoken", "Loads runtime representation of metadata token", 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
inst.ReplaceWith(block); inst.ReplaceWith(block);
return; 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); 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> /// <summary>
/// newobj Span..ctor(localloc(conv i4->u &lt;zero extend&gt;(ldc.i4 sizeInBytes)), numberOfElementsExpr) /// newobj Span..ctor(localloc(conv i4->u &lt;zero extend&gt;(ldc.i4 sizeInBytes)), numberOfElementsExpr)
/// => /// =>

Loading…
Cancel
Save