Browse Source

Add support for ldftn/ldvirtftn.

pull/10/head
Daniel Grunwald 15 years ago
parent
commit
f537bf43fb
  1. 2
      ICSharpCode.Decompiler/Ast/AstBuilder.cs
  2. 172
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  3. 87
      ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
  4. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

2
ICSharpCode.Decompiler/Ast/AstBuilder.cs

@ -17,6 +17,8 @@ namespace Decompiler @@ -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);

172
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -468,87 +468,28 @@ namespace Decompiler @@ -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<Ast.Expression> methodArgs = new List<Ast.Expression>(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 @@ -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 @@ -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 @@ -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 @@ -691,6 +630,79 @@ namespace Decompiler
}
}
static AstNode TransformCall(bool isVirtual, object operand, MethodDefinition methodDef, List<Ast.Expression> args)
{
Cecil.MethodReference cecilMethod = ((MethodReference)operand);
Ast.Expression target;
List<Ast.Expression> methodArgs = new List<Ast.Expression>(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<AstType> 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 };

87
ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs

@ -0,0 +1,87 @@ @@ -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
{
/// <summary>
/// Converts "new Action(obj, ldftn(func))" into "new Action(obj.func)".
/// </summary>
public class DelegateConstruction : DepthFirstAstVisitor<object, object>
{
internal sealed class Annotation
{
/// <summary>
/// ldftn or ldvirtftn?
/// </summary>
public readonly bool IsVirtual;
/// <summary>
/// The method being decompiled.
/// </summary>
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<Annotation>();
if (annotation != null) {
IdentifierExpression methodIdent = (IdentifierExpression)((InvocationExpression)func).Arguments.Single();
MethodReference method = methodIdent.Annotation<MethodReference>();
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<TypeReference>();
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);
}
}
}

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -55,6 +55,7 @@ @@ -55,6 +55,7 @@
<Compile Include="Ast\NRefactoryExtensions.cs" />
<Compile Include="Ast\TextOutputFormatter.cs" />
<Compile Include="Ast\Transforms\ConvertConstructorCallIntoInitializer.cs" />
<Compile Include="Ast\Transforms\DelegateConstruction.cs" />
<Compile Include="Ast\Transforms\ReplaceMethodCallsWithOperators.cs" />
<Compile Include="Ast\Transforms\PushNegation.cs" />
<Compile Include="Ast\Transforms\RemoveDeadLabels.cs" />

Loading…
Cancel
Save