// 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.Diagnostics; using ExpressionType = System.Linq.Expressions.ExpressionType; 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; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ICSharpCode.Decompiler.CSharp { /// /// Translates from ILAst to C# expressions. /// class ExpressionBuilder : ILVisitor { internal readonly ICompilation compilation; internal readonly NRefactoryCecilMapper cecilMapper; internal readonly CSharpResolver resolver; internal readonly TypeSystemAstBuilder astBuilder; public ExpressionBuilder(ICompilation compilation, NRefactoryCecilMapper cecilMapper) { Debug.Assert(compilation != null); Debug.Assert(cecilMapper != null); this.compilation = compilation; this.cecilMapper = cecilMapper; this.resolver = new CSharpResolver(compilation); this.astBuilder = new TypeSystemAstBuilder(resolver); } public AstType ConvertType(Mono.Cecil.TypeReference typeReference) { if (typeReference == null) return AstType.Null; var type = cecilMapper.GetType(typeReference); return ConvertType(type); } public AstType ConvertType(IType type) { var astType = astBuilder.ConvertType(type); astType.AddAnnotation(new TypeResolveResult(type)); return astType; } public ConvertedExpression Convert(ILInstruction inst) { Debug.Assert(inst != null); var cexpr = inst.AcceptVisitor(this); Debug.Assert(cexpr.Type.GetStackType() == inst.ResultType || cexpr.Type.Kind == TypeKind.Unknown || inst.ResultType == StackType.Void); return cexpr; } public ConvertedExpression ConvertCondition(ILInstruction condition) { var expr = Convert(condition); return expr.ConvertToBoolean(this); } ExpressionWithResolveResult ConvertVariable(ILVariable variable) { Expression expr; if (variable.Kind == VariableKind.This) expr = new ThisReferenceExpression(); else expr = new IdentifierExpression(variable.Name); if (variable.Type.SkipModifiers().MetadataType == Mono.Cecil.MetadataType.ByReference) { // When loading a by-ref parameter, use 'ref paramName'. // We'll strip away the 'ref' when dereferencing. expr = new DirectionExpression(FieldDirection.Ref, expr); } return expr .WithRR(new ResolveResult(cecilMapper.GetType(variable.Type))); // TODO: use LocalResolveResult instead } ConvertedExpression IsType(IsInst inst) { var arg = Convert(inst.Argument); var type = cecilMapper.GetType(inst.Type); return new IsExpression(arg.Expression, ConvertType(type)) .WithILInstruction(inst) .WithRR(new TypeIsResolveResult(arg.ResolveResult, type, compilation.FindType(TypeCode.Boolean))); } protected internal override ConvertedExpression VisitIsInst(IsInst inst) { var arg = Convert(inst.Argument); var type = cecilMapper.GetType(inst.Type); return new AsExpression(arg.Expression, ConvertType(type)) .WithILInstruction(inst) .WithRR(new ConversionResolveResult(type, arg.ResolveResult, Conversion.TryCast)); } protected internal override ConvertedExpression VisitNewObj(NewObj inst) { return HandleCallInstruction(inst); } protected internal override ConvertedExpression VisitLdcI4(LdcI4 inst) { return new PrimitiveExpression(inst.Value) .WithILInstruction(inst) .WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), inst.Value)); } protected internal override ConvertedExpression VisitLdcI8(LdcI8 inst) { return new PrimitiveExpression(inst.Value) .WithILInstruction(inst) .WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int64), inst.Value)); } protected internal override ConvertedExpression VisitLdcF(LdcF inst) { return new PrimitiveExpression(inst.Value) .WithILInstruction(inst) .WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.Double), inst.Value)); } protected internal override ConvertedExpression VisitLdStr(LdStr inst) { return new PrimitiveExpression(inst.Value) .WithILInstruction(inst) .WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.String), inst.Value)); } protected internal override ConvertedExpression VisitLdNull(LdNull inst) { return new NullReferenceExpression() .WithILInstruction(inst) .WithRR(new ConstantResolveResult(SpecialType.UnknownType, null)); } protected internal override ConvertedExpression VisitLogicNot(LogicNot inst) { return LogicNot(ConvertCondition(inst.Argument)).WithILInstruction(inst); } ExpressionWithResolveResult LogicNot(ConvertedExpression expr) { return new UnaryOperatorExpression(UnaryOperatorType.Not, expr.Expression) .WithRR(new OperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), ExpressionType.Not)); } protected internal override ConvertedExpression VisitLdLoc(LdLoc inst) { return ConvertVariable(inst.Variable).WithILInstruction(inst); } protected internal override ConvertedExpression VisitLdLoca(LdLoca inst) { var expr = ConvertVariable(inst.Variable).WithILInstruction(inst); // Note that we put the instruction on the IdentifierExpression instead of the DirectionExpression, // because the DirectionExpression might get removed by dereferencing instructions such as LdObj return new DirectionExpression(FieldDirection.Ref, expr.Expression) .WithoutILInstruction() .WithRR(new ByReferenceResolveResult(expr.ResolveResult, isOut: false)); } protected internal override ConvertedExpression VisitStLoc(StLoc inst) { return Assignment(ConvertVariable(inst.Variable).WithoutILInstruction(), Convert(inst.Value)).WithILInstruction(inst); } protected internal override ConvertedExpression VisitCeq(Ceq inst) { // Translate 'e as T == null' to 'e is T'. // This is necessary for correctness when T is a value type. if (inst.Left.OpCode == OpCode.IsInst && inst.Right.OpCode == OpCode.LdNull) { return LogicNot(IsType((IsInst)inst.Left)).WithILInstruction(inst); } else if (inst.Right.OpCode == OpCode.IsInst && inst.Left.OpCode == OpCode.LdNull) { return LogicNot(IsType((IsInst)inst.Right)).WithILInstruction(inst); } var left = Convert(inst.Left); var right = Convert(inst.Right); return new BinaryOperatorExpression(left.Expression, BinaryOperatorType.Equality, right.Expression) .WithILInstruction(inst) .WithRR(new OperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), ExpressionType.Equal, left.ResolveResult, right.ResolveResult)); } protected internal override ConvertedExpression VisitClt(Clt inst) { return Comparison(inst, BinaryOperatorType.LessThan); } protected internal override ConvertedExpression VisitCgt(Cgt inst) { return Comparison(inst, BinaryOperatorType.GreaterThan); } protected internal override ConvertedExpression VisitClt_Un(Clt_Un inst) { return Comparison(inst, BinaryOperatorType.LessThan, un: true); } protected internal override ConvertedExpression VisitCgt_Un(Cgt_Un inst) { return Comparison(inst, BinaryOperatorType.GreaterThan, un: true); } ConvertedExpression Comparison(BinaryComparisonInstruction inst, BinaryOperatorType op, bool un = false) { var left = Convert(inst.Left); var right = Convert(inst.Right); // TODO: ensure the arguments are signed // or with _Un: ensure the arguments are unsigned; and that float comparisons are performed unordered return new BinaryOperatorExpression(left.Expression, op, right.Expression) .WithILInstruction(inst) .WithRR(new OperatorResolveResult(compilation.FindType(TypeCode.Boolean), BinaryOperatorExpression.GetLinqNodeType(op, false), left.ResolveResult, right.ResolveResult)); } ExpressionWithResolveResult Assignment(ConvertedExpression left, ConvertedExpression right) { right = right.ConvertTo(left.Type, this); return new AssignmentExpression(left.Expression, right.Expression) .WithRR(new OperatorResolveResult(left.Type, ExpressionType.Assign, left.ResolveResult, right.ResolveResult)); } protected internal override ConvertedExpression VisitAdd(Add inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.Add); } protected internal override ConvertedExpression VisitSub(Sub inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.Subtract); } protected internal override ConvertedExpression VisitMul(Mul inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.Multiply); } protected internal override ConvertedExpression VisitDiv(Div inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.Divide); } protected internal override ConvertedExpression VisitRem(Rem inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.Modulus); } protected internal override ConvertedExpression VisitBitXor(BitXor inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.ExclusiveOr); } protected internal override ConvertedExpression VisitBitAnd(BitAnd inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.BitwiseAnd); } protected internal override ConvertedExpression VisitBitOr(BitOr inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.BitwiseOr); } protected internal override ConvertedExpression VisitShl(Shl inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.ShiftLeft); } protected internal override ConvertedExpression VisitShr(Shr inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.ShiftRight); } ConvertedExpression HandleBinaryNumeric(BinaryNumericInstruction inst, BinaryOperatorType op) { var resolverWithOverflowCheck = resolver.WithCheckForOverflow(inst.CheckForOverflow); var left = Convert(inst.Left); var right = Convert(inst.Right); var rr = resolverWithOverflowCheck.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult); if (rr.IsError || rr.Type.GetStackType() != inst.ResultType || !IsCompatibleWithSign(left.Type, inst.Sign) || !IsCompatibleWithSign(right.Type, inst.Sign)) { // Left and right operands are incompatible, so convert them to a common type IType targetType = compilation.FindType(inst.ResultType.ToKnownTypeCode(inst.Sign)); left = left.ConvertTo(targetType, this); right = right.ConvertTo(targetType, this); rr = resolverWithOverflowCheck.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult); } return new BinaryOperatorExpression(left.Expression, op, right.Expression) .WithILInstruction(inst) .WithRR(rr); } /// /// Gets whether has the specified . /// If is None, always returns true. /// bool IsCompatibleWithSign(IType type, Sign sign) { return sign == Sign.None || type.GetSign() == sign; } protected internal override ConvertedExpression VisitConv(Conv inst) { var arg = Convert(inst.Argument); if (arg.Type.GetSign() != inst.Sign) { // we need to cast the input to a type of appropriate sign var inputType = inst.Argument.ResultType.ToKnownTypeCode(inst.Sign); arg = arg.ConvertTo(compilation.FindType(inputType), this); } var targetType = compilation.FindType(inst.TargetType.ToKnownTypeCode()); var rr = resolver.WithCheckForOverflow(inst.CheckForOverflow).ResolveCast(targetType, arg.ResolveResult); return new CastExpression(ConvertType(targetType), arg.Expression) .WithILInstruction(inst) .WithRR(rr); } protected internal override ConvertedExpression VisitCall(Call inst) { return HandleCallInstruction(inst); } protected internal override ConvertedExpression VisitCallVirt(CallVirt inst) { return HandleCallInstruction(inst); } ConvertedExpression HandleCallInstruction(CallInstruction inst) { // Used for Call, CallVirt and NewObj var method = cecilMapper.GetMethod(inst.Method); ConvertedExpression target; if (inst.OpCode == OpCode.NewObj) { target = default(ConvertedExpression); // no target } else if (inst.Method.HasThis) { var argInstruction = inst.Arguments[0]; if (inst.OpCode == OpCode.Call && argInstruction.MatchLdThis()) { target = new BaseReferenceExpression() .WithILInstruction(argInstruction) .WithRR(new ThisResolveResult(cecilMapper.GetType(inst.Method.DeclaringType), causesNonVirtualInvocation: true)); } else { target = Convert(argInstruction); } } else { var declaringType = cecilMapper.GetType(inst.Method.DeclaringType); target = new TypeReferenceExpression(ConvertType(declaringType)) .WithoutILInstruction() .WithRR(new TypeResolveResult(declaringType)); } var arguments = inst.Arguments.SelectArray(Convert); int firstParamIndex = inst.Method.HasThis && inst.OpCode != OpCode.NewObj ? 1 : 0; Debug.Assert(arguments.Length == firstParamIndex + inst.Method.Parameters.Count); ResolveResult rr; if (method != null) { // Convert arguments to the expected parameter types Debug.Assert(arguments.Length == method.Parameters.Count); for (int i = firstParamIndex; i < arguments.Length; i++) { var parameter = method.Parameters[i - firstParamIndex]; arguments[i] = arguments[i].ConvertTo(parameter.Type, this); } rr = new CSharpInvocationResolveResult(target.ResolveResult, method, arguments.SelectArray(arg => arg.ResolveResult)); } else { // no IMethod found -- determine the target types from the cecil parameter collection instead for (int i = firstParamIndex; i < arguments.Length; i++) { var parameterDefinition = inst.Method.Parameters[i - firstParamIndex]; var parameterType = cecilMapper.GetType(parameterDefinition.ParameterType); arguments[i] = arguments[i].ConvertTo(parameterType, this); } rr = new ResolveResult(cecilMapper.GetType(inst.Method.DeclaringType)); } if (inst.OpCode == OpCode.NewObj) { return new ObjectCreateExpression(ConvertType(inst.Method.DeclaringType), arguments.Select(arg => arg.Expression)) .WithILInstruction(inst).WithRR(rr); } else { var mre = new MemberReferenceExpression(target.Expression, inst.Method.Name); return new InvocationExpression(mre, arguments.Select(arg => arg.Expression)) .WithILInstruction(inst).WithRR(rr); } } protected internal override ConvertedExpression VisitLdObj(LdObj inst) { var target = Convert(inst.Target); var type = cecilMapper.GetType(inst.Type); if (target.Type.Equals(new ByReferenceType(type)) && target.Expression is DirectionExpression) { // we can deference the managed reference by stripping away the 'ref' var result = target.UnwrapChild(((DirectionExpression)target.Expression).Expression); result = result.ConvertTo(type, this); } // Cast pointer type if necessary: target = target.ConvertTo(new PointerType(type), this); return new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) .WithILInstruction(inst) .WithRR(new ResolveResult(type)); } protected internal override ConvertedExpression VisitUnboxAny(UnboxAny inst) { var arg = Convert(inst.Argument); if (arg.Type.IsReferenceType != true) { // ensure we treat the input as a reference type arg = arg.ConvertTo(compilation.FindType(KnownTypeCode.Object), this); } return new CastExpression(ConvertType(inst.Type), arg.Expression) .WithILInstruction(inst) .WithRR(new ConversionResolveResult(cecilMapper.GetType(inst.Type), arg.ResolveResult, Conversion.UnboxingConversion)); } protected override ConvertedExpression Default(ILInstruction inst) { return ErrorExpression("OpCode not supported: " + inst.OpCode); } static ConvertedExpression ErrorExpression(string message) { var e = new ErrorExpression(); e.AddChild(new Comment(message, CommentType.MultiLine), Roles.Comment); return e.WithoutILInstruction().WithRR(ErrorResolveResult.UnknownError); } } }