From bf917c4352192e25830ffec59156fd116ccc6acc Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 26 Jul 2014 15:02:05 +0200 Subject: [PATCH] Decompile call instructions --- .../CSharp/CSharpDecompiler.cs | 31 ++----- .../CSharp/ExpressionBuilder.cs | 81 +++++++++++++++---- .../CSharp/NRefactoryCecilMapper.cs | 79 ++++++++++++++++++ .../CSharp/StatementBuilder.cs | 8 +- .../ICSharpCode.Decompiler.csproj | 1 + ICSharpCode.Decompiler/IL/ILVariable.cs | 31 +++---- .../IL/Instructions/PatternMatching.cs | 23 ++++++ 7 files changed, 197 insertions(+), 57 deletions(-) create mode 100644 ICSharpCode.Decompiler/CSharp/NRefactoryCecilMapper.cs diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index c22bd6be8..c6cc5acef 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -36,10 +36,9 @@ namespace ICSharpCode.Decompiler.CSharp { CecilLoader cecilLoader = new CecilLoader { IncludeInternalMembers = true, LazyLoad = true }; Dictionary entityDict = new Dictionary(); + NRefactoryCecilMapper cecilMapper; ICompilation compilation; - ITypeResolveContext mainAssemblyTypeResolveContext; TypeSystemAstBuilder typeSystemAstBuilder; - StatementBuilder statementBuilder; public CancellationToken CancellationToken { get; set; } @@ -59,44 +58,28 @@ namespace ICSharpCode.Decompiler.CSharp referencedAssemblies.Add(cecilLoader.LoadAssembly(asm)); } compilation = new SimpleCompilation(mainAssembly, referencedAssemblies); - mainAssemblyTypeResolveContext = new SimpleTypeResolveContext(compilation.MainAssembly); + cecilMapper = new NRefactoryCecilMapper(compilation, module, GetMemberReference); typeSystemAstBuilder = new TypeSystemAstBuilder(); typeSystemAstBuilder.AlwaysUseShortTypeNames = true; typeSystemAstBuilder.AddAnnotations = true; - - statementBuilder = new StatementBuilder(compilation); } - - MemberReference GetMemberReference(IMember member) + + MemberReference GetMemberReference(IUnresolvedEntity member) { - var unresolved = member.UnresolvedMember; lock (entityDict) { MemberReference mr; - if (unresolved != null && entityDict.TryGetValue(unresolved, out mr)) + if (member != null && entityDict.TryGetValue(member, out mr)) return mr; } return null; } - ITypeDefinition GetTypeDefinition(TypeDefinition typeDef) - { - return compilation.MainAssembly.GetTypeDefinition(typeDef.GetFullTypeName()); - } - - IMethod GetMethod(MethodDefinition methodDef) - { - ITypeDefinition typeDef = GetTypeDefinition(methodDef.DeclaringType); - if (typeDef == null) - return null; - return typeDef.Methods.FirstOrDefault(m => GetMemberReference(m) == methodDef); - } - public EntityDeclaration Decompile(MethodDefinition methodDefinition) { if (methodDefinition == null) throw new ArgumentNullException("methodDefinition"); - var method = GetMethod(methodDefinition); + var method = cecilMapper.GetMethod(methodDefinition); if (method == null) throw new InvalidOperationException("Could not find method in NR type system"); var entityDecl = typeSystemAstBuilder.ConvertEntity(method); @@ -104,6 +87,8 @@ namespace ICSharpCode.Decompiler.CSharp var ilReader = new ILReader(); var function = ilReader.ReadIL(methodDefinition.Body, CancellationToken); function.Body = function.Body.AcceptVisitor(new TransformingVisitor()); + + var statementBuilder = new StatementBuilder(method, cecilMapper); var body = statementBuilder.ConvertAsBlock(function.Body); body.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); entityDecl.AddChild(body, Roles.Body); diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 2044594f7..c41db8776 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -16,6 +16,9 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.Semantics; using ICSharpCode.Decompiler.IL; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.TypeSystem; @@ -33,10 +36,16 @@ namespace ICSharpCode.Decompiler.CSharp class ExpressionBuilder : ILVisitor { private readonly ICompilation compilation; + readonly NRefactoryCecilMapper cecilMapper; + readonly CSharpResolver resolver; + readonly TypeSystemAstBuilder astBuilder; - public ExpressionBuilder(ICompilation compilation) + public ExpressionBuilder(ICompilation compilation, NRefactoryCecilMapper cecilMapper) { this.compilation = compilation; + this.cecilMapper = cecilMapper; + this.resolver = new CSharpResolver(compilation); + this.astBuilder = new TypeSystemAstBuilder(resolver); } internal struct ConvertedExpression @@ -50,11 +59,21 @@ namespace ICSharpCode.Decompiler.CSharp this.Type = type; } - public Expression ConvertTo(IType targetType) + public Expression ConvertTo(IType targetType, ExpressionBuilder expressionBuilder) { if (targetType.IsKnownType(KnownTypeCode.Boolean)) return ConvertToBoolean(); - return Expression; + if (Type.Equals(targetType)) + return Expression; + if (Expression is PrimitiveExpression) { + object value = ((PrimitiveExpression)Expression).Value; + var rr = expressionBuilder.resolver.ResolveCast(targetType, new ConstantResolveResult(Type, value)); + if (rr.IsCompileTimeConstant && !rr.IsError) + return expressionBuilder.astBuilder.ConvertConstantValue(rr); + } + return new CastExpression( + expressionBuilder.astBuilder.ConvertType(targetType), + Expression); } public Expression ConvertToBoolean() @@ -75,23 +94,25 @@ namespace ICSharpCode.Decompiler.CSharp return expr; } - public AstType ConvertType(Mono.Cecil.TypeReference type) + public Expression Convert(ILInstruction inst, IType expectedType) { - if (type == null) - return null; - return new SimpleType(type.Name); + var expr = inst.AcceptVisitor(this); + expr.Expression.AddAnnotation(inst); + return expr.ConvertTo(expectedType, this); } - IType GetType(Mono.Cecil.TypeReference type) + public AstType ConvertType(Mono.Cecil.TypeReference type) { - return SpecialType.UnknownType; + if (type == null) + return null; + return astBuilder.ConvertType(cecilMapper.GetType(type)); } ConvertedExpression ConvertVariable(ILVariable variable) { var expr = new IdentifierExpression(variable.Name); expr.AddAnnotation(variable); - return new ConvertedExpression(expr, GetType(variable.Type)); + return new ConvertedExpression(expr, cecilMapper.GetType(variable.Type)); } ConvertedExpression ConvertArgument(ILInstruction inst) @@ -114,14 +135,14 @@ namespace ICSharpCode.Decompiler.CSharp var arg = ConvertArgument(inst.Argument); return new ConvertedExpression( new AsExpression(arg.Expression, ConvertType(inst.Type)), - GetType(inst.Type)); + cecilMapper.GetType(inst.Type)); } protected internal override ConvertedExpression VisitNewObj(NewObj inst) { var oce = new ObjectCreateExpression(ConvertType(inst.Method.DeclaringType)); oce.Arguments.AddRange(inst.Arguments.Select(i => ConvertArgument(i).Expression)); - return new ConvertedExpression(oce, GetType(inst.Method.DeclaringType)); + return new ConvertedExpression(oce, cecilMapper.GetType(inst.Method.DeclaringType)); } protected internal override ConvertedExpression VisitLdcI4(LdcI4 inst) @@ -237,15 +258,43 @@ namespace ICSharpCode.Decompiler.CSharp ConvertedExpression Assignment(ConvertedExpression left, ConvertedExpression right) { return new ConvertedExpression( - new AssignmentExpression(left.Expression, right.ConvertTo(left.Type)), + new AssignmentExpression(left.Expression, right.ConvertTo(left.Type, this)), left.Type); } - - Expression AddConversion(Expression expression, IType type) + + protected internal override ConvertedExpression VisitCall(Call inst) { - return expression; + return HandleCallInstruction(inst); } + protected internal override ConvertedExpression VisitCallVirt(CallVirt inst) + { + return HandleCallInstruction(inst); + } + + ConvertedExpression HandleCallInstruction(CallInstruction inst) + { + Expression target; + if (inst.Method.HasThis) { + var argInstruction = inst.Arguments[0]; + if (inst.OpCode == OpCode.Call && argInstruction.MatchLdThis()) + target = new BaseReferenceExpression().WithAnnotation(argInstruction); + else + target = Convert(argInstruction); + } else { + target = new TypeReferenceExpression(ConvertType(inst.Method.DeclaringType)); + } + InvocationExpression invocation = target.Invoke(inst.Method.Name); + int firstParamIndex = inst.Method.HasThis ? 1 : 0; + for (int i = firstParamIndex; i < inst.Arguments.Count; i++) { + var p = inst.Method.Parameters[i - firstParamIndex]; + var arg = ConvertArgument(inst.Arguments[i]); + var type = cecilMapper.GetType(p.ParameterType); + invocation.Arguments.Add(arg.ConvertTo(type, this)); + } + return new ConvertedExpression(invocation, cecilMapper.GetType(inst.Method.ReturnType)); + } + protected override ConvertedExpression Default(ILInstruction inst) { return ErrorExpression("OpCode not supported: " + inst.OpCode); diff --git a/ICSharpCode.Decompiler/CSharp/NRefactoryCecilMapper.cs b/ICSharpCode.Decompiler/CSharp/NRefactoryCecilMapper.cs new file mode 100644 index 000000000..fcabdbfd3 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/NRefactoryCecilMapper.cs @@ -0,0 +1,79 @@ +// Copyright (c) 2014 Daniel Grunwald +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.NRefactory.TypeSystem; +using Mono.Cecil; + +namespace ICSharpCode.Decompiler.CSharp +{ + /// + /// Maps from cecil types to NRefactory and vice versa. + /// + class NRefactoryCecilMapper + { + readonly ITypeResolveContext context; + readonly CecilLoader cecilLoader = new CecilLoader(); + readonly Func nr2cecilLookup; + + /// Compilation to use for Cecil->NRefactory lookups + /// Compilation to use for Cecil->NRefactory lookups + /// NRefactory->Cecil lookup function + internal NRefactoryCecilMapper(ICompilation compilation, ModuleDefinition module, Func nr2cecilLookup) + { + this.nr2cecilLookup = nr2cecilLookup; + this.context = new SimpleTypeResolveContext(compilation.MainAssembly); + this.cecilLoader.SetCurrentModule(module); + } + + public MemberReference GetCecil(IMember member) + { + if (member == null) + return null; + return nr2cecilLookup(member.UnresolvedMember); + } + + public MemberReference GetCecil(ITypeDefinition typeDefinition) + { + if (typeDefinition == null) + return null; + return nr2cecilLookup(typeDefinition.Parts[0]); + } + + /// + /// Retrieves a type definition for a type defined in the compilation's main assembly. + /// + public IType GetType(TypeReference typeReference) + { + if (typeReference == null) + return SpecialType.UnknownType; + var typeRef = cecilLoader.ReadTypeReference(typeReference); + return typeRef.Resolve(context); + } + + public IMethod GetMethod(MethodReference methodReference) + { + ITypeDefinition typeDef = GetType(methodReference.DeclaringType).GetDefinition(); + if (typeDef == null) + return null; + return typeDef.Methods.FirstOrDefault(m => GetCecil(m) == methodReference); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index ea137448d..cad2e879d 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -30,10 +30,12 @@ namespace ICSharpCode.Decompiler.CSharp class StatementBuilder : ILVisitor { readonly ExpressionBuilder exprBuilder; + readonly IMethod currentMethod; - public StatementBuilder(ICompilation compilation) + public StatementBuilder(IMethod method, NRefactoryCecilMapper cecilMapper) { - this.exprBuilder = new ExpressionBuilder(compilation); + this.exprBuilder = new ExpressionBuilder(method.Compilation, cecilMapper); + this.currentMethod = method; } public Statement Convert(ILInstruction inst) @@ -79,7 +81,7 @@ namespace ICSharpCode.Decompiler.CSharp { if (inst.ReturnValue == null) return new ReturnStatement(); - return new ReturnStatement(exprBuilder.Convert(inst.ReturnValue)); + return new ReturnStatement(exprBuilder.Convert(inst.ReturnValue, currentMethod.ReturnType)); } TryCatchStatement MakeTryCatch(ILInstruction tryBlock) diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index de850a213..9df7331ec 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -62,6 +62,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/ILVariable.cs b/ICSharpCode.Decompiler/IL/ILVariable.cs index 895597cd1..5eac14377 100644 --- a/ICSharpCode.Decompiler/IL/ILVariable.cs +++ b/ICSharpCode.Decompiler/IL/ILVariable.cs @@ -32,6 +32,10 @@ namespace ICSharpCode.Decompiler.IL Local, Parameter, /// + /// The 'this' parameter + /// + This, + /// /// Variable created for exception handler /// Exception @@ -42,6 +46,8 @@ namespace ICSharpCode.Decompiler.IL public readonly VariableKind Kind; public readonly TypeReference Type; public readonly int Index; + + public string Name; /// /// Number of ldloc instructions referencing this variable. @@ -73,31 +79,26 @@ namespace ICSharpCode.Decompiler.IL : this(VariableKind.Local, v.VariableType, v.Index) { this.CecilObject = v; + if (string.IsNullOrEmpty(v.Name)) + this.Name = "V_" + v.Index; + else + this.Name = v.Name; } public ILVariable(ParameterDefinition p) - : this(VariableKind.Parameter, p.ParameterType, p.Index) + : this(p.Index == -1 ? VariableKind.This : VariableKind.Parameter, p.ParameterType, p.Index) { this.CecilObject = p; this.StoreCount = 1; // count the initial store when the method is called with an argument + if (string.IsNullOrEmpty(p.Name)) + this.Name = "P_" + this.Index; + else + this.Name = p.Name; } - public string Name { - get { return ToString(); } - } - public override string ToString() { - switch (Kind) { - case VariableKind.Local: - return "V_" + Index.ToString(); - case VariableKind.Parameter: - if (Index == -1) - return "this"; - return "P_" + Index.ToString(); - default: - return Kind.ToString(); - } + return Name; } internal void WriteDefinitionTo(ITextOutput output) diff --git a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs index 2e6b93ad1..5b531e939 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs @@ -37,5 +37,28 @@ namespace ICSharpCode.Decompiler.IL val = 0; return false; } + + public bool MatchLdLoc(ILVariable variable) + { + var inst = this as LdLoc; + return inst != null && inst.Variable == variable; + } + + public bool MatchLdLoc(out ILVariable variable) + { + var inst = this as LdLoc; + if (inst != null) { + variable = inst.Variable; + return true; + } + variable = null; + return false; + } + + public bool MatchLdThis() + { + var inst = this as LdLoc; + return inst != null && inst.Variable.Kind == VariableKind.This; + } } }