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. 8
      ICSharpCode.Decompiler/CSharp/Annotations.cs
  3. 20
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  4. 5
      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
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 _)
{ {

8
ICSharpCode.Decompiler/CSharp/Annotations.cs

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

20
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -1235,7 +1235,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;
@ -1288,7 +1298,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)
@ -1337,12 +1347,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;
} }

5
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -348,6 +348,11 @@ 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",

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

@ -297,9 +297,37 @@ 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 (!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> /// <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)
/// => /// =>
@ -503,10 +531,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
if (!(dynamicCompoundAssign.Target is DynamicGetMemberInstruction getMember)) if (!(dynamicCompoundAssign.Target is DynamicGetMemberInstruction getMember))
return false; return false;
if (!isEvent.Argument.Match(getMember.Target).Success)
return false;
if (!SemanticHelper.IsPure(isEvent.Argument.Flags)) if (!SemanticHelper.IsPure(isEvent.Argument.Flags))
return false; return false;
if (!isEvent.Argument.Match(getMember.Target).Success)
return false;
if (!(trueInst is DynamicInvokeMemberInstruction invokeMember)) if (!(trueInst is DynamicInvokeMemberInstruction invokeMember))
return false; return false;
if (!(invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.InvokeSpecialName) && invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.ResultDiscarded))) if (!(invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.InvokeSpecialName) && invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.ResultDiscarded)))

Loading…
Cancel
Save