From f537bf43fba555e551b19bf8e241762fbb1e1c2c Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 15 Feb 2011 14:01:54 +0100 Subject: [PATCH] Add support for ldftn/ldvirtftn. --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 2 + .../Ast/AstMethodBodyBuilder.cs | 172 ++++++++++-------- .../Ast/Transforms/DelegateConstruction.cs | 87 +++++++++ .../ICSharpCode.Decompiler.csproj | 1 + 4 files changed, 182 insertions(+), 80 deletions(-) create mode 100644 ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 494d8ec40..1c80fe193 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -17,6 +17,8 @@ namespace Decompiler public void GenerateCode(ITextOutput output) { + astCompileUnit.AcceptVisitor(new Transforms.DelegateConstruction(), null); + for (int i = 0; i < 4; i++) { if (Options.ReduceAstJumps) { astCompileUnit.AcceptVisitor(new Transforms.Ast.RemoveGotos(), null); diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 51332c5f5..4ad579b74 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -468,87 +468,28 @@ namespace Decompiler case Code.Box: throw new NotImplementedException(); case Code.Break: throw new NotImplementedException(); case Code.Call: + return TransformCall(false, operand, methodDef, args); case Code.Callvirt: - // TODO: Diferentiate virtual and non-vitual dispach - Cecil.MethodReference cecilMethod = ((MethodReference)operand); - Ast.Expression target; - List methodArgs = new List(args); - if (cecilMethod.HasThis) { - target = methodArgs[0]; - methodArgs.RemoveAt(0); - - // Unpack any DirectionExpression that is used as target for the call - // (calling methods on value types implicitly passes the first argument by reference) - if (target is DirectionExpression) { - target = ((DirectionExpression)target).Expression; - target.Remove(); // detach from DirectionExpression - } - } else { - target = new TypeReferenceExpression { Type = AstBuilder.ConvertType(cecilMethod.DeclaringType)}; - } - - if (target is ThisReferenceExpression && opCode.Code == Code.Call) { - // a non-virtual call on "this" might be a "base"-call. - if (cecilMethod.DeclaringType != methodDef.DeclaringType) { - // If we're not calling a method in the current class; we must be calling one in the base class. - target = new BaseReferenceExpression(); - } + return TransformCall(true, operand, methodDef, args); + case Code.Ldftn: + { + Cecil.MethodReference cecilMethod = ((MethodReference)operand); + var expr = new Ast.IdentifierExpression(cecilMethod.Name); + expr.TypeArguments = ConvertTypeArguments(cecilMethod); + expr.AddAnnotation(cecilMethod); + return new IdentifierExpression("ldftn").Invoke(expr) + .WithAnnotation(new Transforms.DelegateConstruction.Annotation(false, methodDef.DeclaringType)); } - - // Resolve the method to figure out whether it is an accessor: - Cecil.MethodDefinition cecilMethodDef = cecilMethod.Resolve(); - if (cecilMethodDef != null) { - if (cecilMethodDef.IsGetter && methodArgs.Count == 0) { - foreach (var prop in cecilMethodDef.DeclaringType.Properties) { - if (prop.GetMethod == cecilMethodDef) - return target.Member(prop.Name).WithAnnotation(prop); - } - } else if (cecilMethodDef.IsSetter && methodArgs.Count == 1) { - foreach (var prop in cecilMethodDef.DeclaringType.Properties) { - if (prop.SetMethod == cecilMethodDef) - return new Ast.AssignmentExpression( - target.Member(prop.Name).WithAnnotation(prop), - methodArgs[0]); - } - } else if (cecilMethodDef.IsAddOn && methodArgs.Count == 1) { - foreach (var ev in cecilMethodDef.DeclaringType.Events) { - if (ev.AddMethod == cecilMethodDef) { - return new Ast.AssignmentExpression { - Left = target.Member(ev.Name).WithAnnotation(ev), - Operator = AssignmentOperatorType.Add, - Right = methodArgs[0] - }; - } - } - } else if (cecilMethodDef.IsRemoveOn && methodArgs.Count == 1) { - foreach (var ev in cecilMethodDef.DeclaringType.Events) { - if (ev.RemoveMethod == cecilMethodDef) { - return new Ast.AssignmentExpression { - Left = target.Member(ev.Name).WithAnnotation(ev), - Operator = AssignmentOperatorType.Subtract, - Right = methodArgs[0] - }; - } - } - } + case Code.Ldvirtftn: + { + Cecil.MethodReference cecilMethod = ((MethodReference)operand); + var expr = new Ast.IdentifierExpression(cecilMethod.Name); + expr.TypeArguments = ConvertTypeArguments(cecilMethod); + expr.AddAnnotation(cecilMethod); + return new IdentifierExpression("ldvirtftn").Invoke(expr) + .WithAnnotation(new Transforms.DelegateConstruction.Annotation(true, methodDef.DeclaringType)); } - // Multi-dimensional array acces // TODO: do properly - /* - if (cecilMethod.Name == "Get") { - return new Ast.IndexerExpression(target, methodArgs); - } else if (cecilMethod.Name == "Set") { - Expression val = methodArgs[methodArgs.Count - 1]; - methodArgs.RemoveAt(methodArgs.Count - 1); - return new Ast.AssignmentExpression( - new Ast.IndexerExpression(target, methodArgs), - AssignmentOperatorType.Assign, - Convert(val, ((Cecil.ArrayType)target.UserData["Type"]).ElementType) - ); - }*/ - - // Default invocation - return target.Invoke(cecilMethod.Name, methodArgs).WithAnnotation(cecilMethod); case Code.Calli: throw new NotImplementedException(); case Code.Castclass: return arg1.CastTo(operandAsTypeRef); case Code.Ckfinite: throw new NotImplementedException(); @@ -597,7 +538,6 @@ namespace Decompiler return MakeRef( AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) .Member(((FieldReference)operand).Name).WithAnnotation(operand)); - case Code.Ldftn: throw new NotImplementedException(); case Code.Ldloc: if (operand is ILVariable) { return new Ast.IdentifierExpression(((ILVariable)operand).Name); @@ -610,7 +550,7 @@ namespace Decompiler } else { return MakeRef(new Ast.IdentifierExpression(((VariableDefinition)operand).Name)); } - case Code.Ldnull: return new Ast.PrimitiveExpression(null); + case Code.Ldnull: return new Ast.NullReferenceExpression(); case Code.Ldobj: throw new NotImplementedException(); case Code.Ldstr: return new Ast.PrimitiveExpression(operand); case Code.Ldtoken: @@ -619,7 +559,6 @@ namespace Decompiler } else { throw new NotImplementedException(); } - case Code.Ldvirtftn: throw new NotImplementedException(); case Code.Leave: return null; case Code.Localloc: throw new NotImplementedException(); case Code.Mkrefany: throw new NotImplementedException(); @@ -691,6 +630,79 @@ namespace Decompiler } } + static AstNode TransformCall(bool isVirtual, object operand, MethodDefinition methodDef, List args) + { + Cecil.MethodReference cecilMethod = ((MethodReference)operand); + Ast.Expression target; + List methodArgs = new List(args); + if (cecilMethod.HasThis) { + target = methodArgs[0]; + methodArgs.RemoveAt(0); + + // Unpack any DirectionExpression that is used as target for the call + // (calling methods on value types implicitly passes the first argument by reference) + if (target is DirectionExpression) { + target = ((DirectionExpression)target).Expression; + target.Remove(); // detach from DirectionExpression + } + } else { + target = new TypeReferenceExpression { Type = AstBuilder.ConvertType(cecilMethod.DeclaringType) }; + } + if (target is ThisReferenceExpression && !isVirtual) { + // a non-virtual call on "this" might be a "base"-call. + if (cecilMethod.DeclaringType != methodDef.DeclaringType) { + // If we're not calling a method in the current class; we must be calling one in the base class. + target = new BaseReferenceExpression(); + } + } + + // Resolve the method to figure out whether it is an accessor: + Cecil.MethodDefinition cecilMethodDef = cecilMethod.Resolve(); + if (cecilMethodDef != null) { + if (cecilMethodDef.IsGetter && methodArgs.Count == 0) { + foreach (var prop in cecilMethodDef.DeclaringType.Properties) { + if (prop.GetMethod == cecilMethodDef) + return target.Member(prop.Name).WithAnnotation(prop); + } + } else if (cecilMethodDef.IsSetter && methodArgs.Count == 1) { + foreach (var prop in cecilMethodDef.DeclaringType.Properties) { + if (prop.SetMethod == cecilMethodDef) + return new Ast.AssignmentExpression(target.Member(prop.Name).WithAnnotation(prop), methodArgs[0]); + } + } else if (cecilMethodDef.IsAddOn && methodArgs.Count == 1) { + foreach (var ev in cecilMethodDef.DeclaringType.Events) { + if (ev.AddMethod == cecilMethodDef) { + return new Ast.AssignmentExpression { + Left = target.Member(ev.Name).WithAnnotation(ev), + Operator = AssignmentOperatorType.Add, + Right = methodArgs[0] + }; + } + } + } else if (cecilMethodDef.IsRemoveOn && methodArgs.Count == 1) { + foreach (var ev in cecilMethodDef.DeclaringType.Events) { + if (ev.RemoveMethod == cecilMethodDef) { + return new Ast.AssignmentExpression { + Left = target.Member(ev.Name).WithAnnotation(ev), + Operator = AssignmentOperatorType.Subtract, + Right = methodArgs[0] + }; + } + } + } + } + // Default invocation + return target.Invoke(cecilMethod.Name, ConvertTypeArguments(cecilMethod), methodArgs).WithAnnotation(cecilMethod); + } + + static IEnumerable ConvertTypeArguments(MethodReference cecilMethod) + { + GenericInstanceMethod g = cecilMethod as GenericInstanceMethod; + if (g == null) + return null; + return g.GenericArguments.Select(t => AstBuilder.ConvertType(t)); + } + static Ast.DirectionExpression MakeRef(Ast.Expression expr) { return new DirectionExpression { Expression = expr, FieldDirection = FieldDirection.Ref }; diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs new file mode 100644 index 000000000..59ce084c1 --- /dev/null +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -0,0 +1,87 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Linq; +using ICSharpCode.NRefactory.CSharp; +using Mono.Cecil; + +namespace Decompiler.Transforms +{ + /// + /// Converts "new Action(obj, ldftn(func))" into "new Action(obj.func)". + /// + public class DelegateConstruction : DepthFirstAstVisitor + { + internal sealed class Annotation + { + /// + /// ldftn or ldvirtftn? + /// + public readonly bool IsVirtual; + + /// + /// The method being decompiled. + /// + public readonly TypeDefinition ContainingType; + + public Annotation(bool isVirtual, TypeDefinition containingType) + { + this.IsVirtual = isVirtual; + this.ContainingType = containingType; + } + } + + public override object VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, object data) + { + if (objectCreateExpression.Arguments.Count() == 2) { + Expression obj = objectCreateExpression.Arguments.First(); + Expression func = objectCreateExpression.Arguments.Last(); + Annotation annotation = func.Annotation(); + if (annotation != null) { + IdentifierExpression methodIdent = (IdentifierExpression)((InvocationExpression)func).Arguments.Single(); + MethodReference method = methodIdent.Annotation(); + if (method != null) { + // Perform the transformation: + obj.Remove(); + methodIdent.Remove(); + if (!annotation.IsVirtual && obj is ThisReferenceExpression) { + // maybe it's getting the pointer of a base method? + if (method.DeclaringType != annotation.ContainingType) { + obj = new BaseReferenceExpression(); + } + } + if (!annotation.IsVirtual && obj is NullReferenceExpression && !method.HasThis) { + // We're loading a static method. + // However it is possible to load extension methods with an instance, so we compare the number of arguments: + bool isExtensionMethod = false; + TypeReference delegateType = objectCreateExpression.Type.Annotation(); + if (delegateType != null) { + TypeDefinition delegateTypeDef = delegateType.Resolve(); + if (delegateTypeDef != null) { + MethodDefinition invokeMethod = delegateTypeDef.Methods.FirstOrDefault(m => m.Name == "Invoke"); + if (invokeMethod != null) { + isExtensionMethod = (invokeMethod.Parameters.Count + 1 == method.Parameters.Count); + } + } + } + if (!isExtensionMethod) { + obj = new TypeReferenceExpression { Type = AstBuilder.ConvertType(method.DeclaringType) }; + } + } + // now transform the identifier into a member reference + MemberReferenceExpression mre = new MemberReferenceExpression { + Target = obj, + MemberName = methodIdent.Identifier, + TypeArguments = methodIdent.TypeArguments + }; + mre.AddAnnotation(method); + objectCreateExpression.Arguments = new [] { mre }; + return null; + } + } + } + return base.VisitObjectCreateExpression(objectCreateExpression, data); + } + } +} diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 830d69e9d..25cd96826 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -55,6 +55,7 @@ +