// 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 ICSharpCode.NRefactory.CSharp.TypeSystem; 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 CSharpResolver resolver; readonly TypeSystemAstBuilder astBuilder; public ExpressionBuilder(ITypeResolveContext decompilationContext) { Debug.Assert(decompilationContext != null); this.compilation = decompilationContext.Compilation; this.resolver = new CSharpResolver(new CSharpTypeResolveContext(compilation.MainAssembly, null, decompilationContext.CurrentTypeDefinition, decompilationContext.CurrentMember)); this.astBuilder = new TypeSystemAstBuilder(resolver); this.astBuilder.AlwaysUseShortTypeNames = true; this.astBuilder.AddResolveResultAnnotations = true; } public AstType ConvertType(IType type) { var astType = astBuilder.ConvertType(type); Debug.Assert(astType.Annotation() != null); return astType; } public ExpressionWithResolveResult ConvertConstantValue(ResolveResult rr) { var expr = astBuilder.ConvertConstantValue(rr); var exprRR = expr.Annotation(); if (exprRR == null) { exprRR = rr; expr.AddAnnotation(rr); } return new ExpressionWithResolveResult(expr, exprRR); } public TranslatedExpression Translate(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 TranslatedExpression TranslateCondition(ILInstruction condition) { var expr = Translate(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); // TODO: use LocalResolveResult instead if (variable.Type.Kind == TypeKind.ByReference) { // When loading a by-ref parameter, use 'ref paramName'. // We'll strip away the 'ref' when dereferencing. // Ensure that the IdentifierExpression itself also gets a resolve result, as that might // get used after the 'ref' is stripped away: var elementType = ((ByReferenceType)variable.Type).ElementType; expr.WithRR(new ResolveResult(elementType)); expr = new DirectionExpression(FieldDirection.Ref, expr); } return expr.WithRR(new ResolveResult(variable.Type)); } ExpressionWithResolveResult ConvertField(IField field, ILInstruction target = null) { return new MemberReferenceExpression(TranslateTarget(field, target, true), field.Name) .WithRR(new ResolveResult(field.ReturnType)); } TranslatedExpression IsType(IsInst inst) { var arg = Translate(inst.Argument); return new IsExpression(arg.Expression, ConvertType(inst.Type)) .WithILInstruction(inst) .WithRR(new TypeIsResolveResult(arg.ResolveResult, inst.Type, compilation.FindType(TypeCode.Boolean))); } protected internal override TranslatedExpression VisitIsInst(IsInst inst) { var arg = Translate(inst.Argument); return new AsExpression(arg.Expression, ConvertType(inst.Type)) .WithILInstruction(inst) .WithRR(new ConversionResolveResult(inst.Type, arg.ResolveResult, Conversion.TryCast)); } protected internal override TranslatedExpression VisitNewObj(NewObj inst) { return HandleCallInstruction(inst); } protected internal override TranslatedExpression VisitNewArr(NewArr inst) { var arg = Translate(inst.Size); return new ArrayCreateExpression { Type = ConvertType(inst.Type), Arguments = { arg } } .WithILInstruction(inst) .WithRR(new ArrayCreateResolveResult(new ArrayType(compilation, inst.Type, 1), new [] { arg.ResolveResult }, new ResolveResult[0])); } protected internal override TranslatedExpression VisitLdcI4(LdcI4 inst) { return new PrimitiveExpression(inst.Value) .WithILInstruction(inst) .WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), inst.Value)); } protected internal override TranslatedExpression VisitLdcI8(LdcI8 inst) { return new PrimitiveExpression(inst.Value) .WithILInstruction(inst) .WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int64), inst.Value)); } protected internal override TranslatedExpression VisitLdcF(LdcF inst) { return new PrimitiveExpression(inst.Value) .WithILInstruction(inst) .WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.Double), inst.Value)); } protected internal override TranslatedExpression VisitLdStr(LdStr inst) { return new PrimitiveExpression(inst.Value) .WithILInstruction(inst) .WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.String), inst.Value)); } protected internal override TranslatedExpression VisitLdNull(LdNull inst) { return new NullReferenceExpression() .WithILInstruction(inst) .WithRR(new ConstantResolveResult(SpecialType.NullType, null)); } protected internal override TranslatedExpression VisitLdTypeToken(LdTypeToken inst) { return new TypeOfExpression(ConvertType(inst.Type)) .WithILInstruction(inst) .WithRR(new TypeOfResolveResult(compilation.FindType(KnownTypeCode.Type), inst.Type)); } protected internal override TranslatedExpression VisitLogicNot(LogicNot inst) { return LogicNot(TranslateCondition(inst.Argument)).WithILInstruction(inst); } ExpressionWithResolveResult LogicNot(TranslatedExpression expr) { return new UnaryOperatorExpression(UnaryOperatorType.Not, expr.Expression) .WithRR(new OperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), ExpressionType.Not)); } protected internal override TranslatedExpression VisitLdLoc(LdLoc inst) { return ConvertVariable(inst.Variable).WithILInstruction(inst); } protected internal override TranslatedExpression 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 TranslatedExpression VisitStLoc(StLoc inst) { return Assignment(ConvertVariable(inst.Variable).WithoutILInstruction(), Translate(inst.Value)).WithILInstruction(inst); } protected internal override TranslatedExpression 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 = Translate(inst.Left); var right = Translate(inst.Right); // Remove redundant bool comparisons if (left.Type.IsKnownType(KnownTypeCode.Boolean)) { if (inst.Right.MatchLdcI4(0)) return LogicNot(left).WithILInstruction(inst); // 'b == 0' => '!b' if (inst.Right.MatchLdcI4(1)) return left; // 'b == 1' => 'b' } else if (right.Type.IsKnownType(KnownTypeCode.Boolean)) { if (inst.Left.MatchLdcI4(0)) return LogicNot(right).WithILInstruction(inst); // '0 == b' => '!b' if (inst.Left.MatchLdcI4(1)) return right; // '1 == b' => 'b' } var rr = resolver.ResolveBinaryOperator(BinaryOperatorType.Equality, left.ResolveResult, right.ResolveResult); if (rr.IsError) { // TODO: insert casts to the wider type of the two input types } return new BinaryOperatorExpression(left.Expression, BinaryOperatorType.Equality, right.Expression) .WithILInstruction(inst) .WithRR(rr); } protected internal override TranslatedExpression VisitClt(Clt inst) { return Comparison(inst, BinaryOperatorType.LessThan); } protected internal override TranslatedExpression VisitCgt(Cgt inst) { return Comparison(inst, BinaryOperatorType.GreaterThan); } protected internal override TranslatedExpression VisitClt_Un(Clt_Un inst) { return Comparison(inst, BinaryOperatorType.LessThan, un: true); } protected internal override TranslatedExpression VisitCgt_Un(Cgt_Un inst) { return Comparison(inst, BinaryOperatorType.GreaterThan, un: true); } TranslatedExpression Comparison(BinaryComparisonInstruction inst, BinaryOperatorType op, bool un = false) { var left = Translate(inst.Left); var right = Translate(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(TranslatedExpression left, TranslatedExpression 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 TranslatedExpression VisitAdd(Add inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.Add); } protected internal override TranslatedExpression VisitSub(Sub inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.Subtract); } protected internal override TranslatedExpression VisitMul(Mul inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.Multiply); } protected internal override TranslatedExpression VisitDiv(Div inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.Divide); } protected internal override TranslatedExpression VisitRem(Rem inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.Modulus); } protected internal override TranslatedExpression VisitBitXor(BitXor inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.ExclusiveOr); } protected internal override TranslatedExpression VisitBitAnd(BitAnd inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.BitwiseAnd); } protected internal override TranslatedExpression VisitBitOr(BitOr inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.BitwiseOr); } protected internal override TranslatedExpression VisitShl(Shl inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.ShiftLeft); } protected internal override TranslatedExpression VisitShr(Shr inst) { return HandleBinaryNumeric(inst, BinaryOperatorType.ShiftRight); } TranslatedExpression HandleBinaryNumeric(BinaryNumericInstruction inst, BinaryOperatorType op) { var resolverWithOverflowCheck = resolver.WithCheckForOverflow(inst.CheckForOverflow); var left = Translate(inst.Left); var right = Translate(inst.Right); ResolveResult rr; if (left.Type.IsKnownType(KnownTypeCode.IntPtr) || left.Type.IsKnownType(KnownTypeCode.UIntPtr) || right.Type.IsKnownType(KnownTypeCode.IntPtr) || right.Type.IsKnownType(KnownTypeCode.UIntPtr)) { IType targetType; if (inst.Sign == Sign.Unsigned) { targetType = compilation.FindType(KnownTypeCode.UInt64); } else { targetType = compilation.FindType(KnownTypeCode.Int64); } left = left.ConvertTo(targetType, this); right = right.ConvertTo(targetType, this); rr = new OperatorResolveResult(targetType, BinaryOperatorExpression.GetLinqNodeType(op, inst.CheckForOverflow), left.ResolveResult, right.ResolveResult); var resultExpr = new BinaryOperatorExpression(left.Expression, op, right.Expression) .WithILInstruction(inst) .WithRR(rr); return resultExpr.ConvertTo(compilation.FindType(inst.ResultType.ToKnownTypeCode()), this); } else { 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 TranslatedExpression VisitConv(Conv inst) { var arg = Translate(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 TranslatedExpression VisitCall(Call inst) { return HandleCallInstruction(inst); } protected internal override TranslatedExpression VisitCallVirt(CallVirt inst) { return HandleCallInstruction(inst); } static bool IsDelegateConstruction(CallInstruction inst) { return inst.Arguments.Count == 2 && (inst.Arguments[1].OpCode == OpCode.LdFtn || inst.Arguments[1].OpCode == OpCode.LdVirtFtn) && inst.Method.DeclaringType.Kind == TypeKind.Delegate; } TranslatedExpression HandleDelegateConstruction(CallInstruction inst) { var func = (LdFtn)inst.Arguments[1]; var target = TranslateTarget(func.Method, inst.Arguments[0], func.OpCode == OpCode.LdFtn); return new ObjectCreateExpression(ConvertType(inst.Method.DeclaringType), new MemberReferenceExpression(target, func.Method.Name)) .WithILInstruction(inst) .WithRR(new ConversionResolveResult(func.Method.DeclaringType, new MemberResolveResult(target.ResolveResult, func.Method), Conversion.MethodGroupConversion(func.Method, func.OpCode == OpCode.LdVirtFtn, false))); // TODO handle extension methods capturing the first argument } TranslatedExpression TranslateTarget(IMember member, ILInstruction target, bool nonVirtualInvocation) { if (!member.IsStatic) { if (nonVirtualInvocation && target.MatchLdThis() && member.DeclaringTypeDefinition != resolver.CurrentTypeDefinition) { return new BaseReferenceExpression() .WithILInstruction(target) .WithRR(new ThisResolveResult(member.DeclaringType, nonVirtualInvocation)); } else { return Translate(target).ConvertTo(member.DeclaringType, this); } } else { return new TypeReferenceExpression(ConvertType(member.DeclaringType)) .WithoutILInstruction() .WithRR(new TypeResolveResult(member.DeclaringType)); } } TranslatedExpression HandleCallInstruction(CallInstruction inst) { // Used for Call, CallVirt and NewObj TranslatedExpression target; if (inst.OpCode == OpCode.NewObj) { if (IsDelegateConstruction(inst)) { return HandleDelegateConstruction(inst); } target = default(TranslatedExpression); // no target } else { target = TranslateTarget(inst.Method, inst.Arguments.FirstOrDefault(), inst.OpCode == OpCode.Call); } var arguments = inst.Arguments.SelectArray(Translate); int firstParamIndex = (inst.Method.IsStatic || inst.OpCode == OpCode.NewObj) ? 0 : 1; // Translate arguments to the expected parameter types Debug.Assert(arguments.Length == firstParamIndex + inst.Method.Parameters.Count); for (int i = firstParamIndex; i < arguments.Length; i++) { var parameter = inst.Method.Parameters[i - firstParamIndex]; arguments[i] = arguments[i].ConvertTo(parameter.Type, this); if (parameter.IsOut && arguments[i].Expression is DirectionExpression) { ((DirectionExpression)arguments[i].Expression).FieldDirection = FieldDirection.Out; } } var argumentResolveResults = arguments.Skip(firstParamIndex).Select(arg => arg.ResolveResult).ToList(); ResolveResult rr; if (inst.Method.IsAccessor) rr = new MemberResolveResult(target.ResolveResult, inst.Method.AccessorOwner); else rr = new CSharpInvocationResolveResult(target.ResolveResult, inst.Method, argumentResolveResults); var argumentExpressions = arguments.Skip(firstParamIndex).Select(arg => arg.Expression).ToList(); if (inst.OpCode == OpCode.NewObj) { return new ObjectCreateExpression(ConvertType(inst.Method.DeclaringType), argumentExpressions) .WithILInstruction(inst).WithRR(rr); } else { Expression expr; IMethod method = inst.Method; if (method.IsAccessor) { if (method.ReturnType.IsKnownType(KnownTypeCode.Void)) { var value = argumentExpressions.Last(); argumentExpressions.Remove(value); if (argumentExpressions.Count == 0) expr = new MemberReferenceExpression(target.Expression, method.AccessorOwner.Name); else expr = new IndexerExpression(target.Expression, argumentExpressions); expr = new AssignmentExpression(expr, value); } else { if (argumentExpressions.Count == 0) expr = new MemberReferenceExpression(target.Expression, method.AccessorOwner.Name); else expr = new IndexerExpression(target.Expression, argumentExpressions); } } else { var mre = new MemberReferenceExpression(target.Expression, inst.Method.Name); expr = new InvocationExpression(mre, argumentExpressions); } return expr.WithILInstruction(inst).WithRR(rr); } } protected internal override TranslatedExpression VisitLdObj(LdObj inst) { var target = Translate(inst.Target); if (target.Expression is DirectionExpression && IsCompatibleTypeForMemoryAccess(target.Type, inst.Type, isWrite: false)) { // we can deference the managed reference by stripping away the 'ref' var result = target.UnwrapChild(((DirectionExpression)target.Expression).Expression); result = result.ConvertTo(inst.Type, this); result.Expression.AddAnnotation(inst); // add LdObj in addition to the existing ILInstruction annotation return result; } else { // Cast pointer type if necessary: target = target.ConvertTo(new PointerType(inst.Type), this); return new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) .WithILInstruction(inst) .WithRR(new ResolveResult(inst.Type)); } } protected internal override TranslatedExpression VisitStObj(StObj inst) { var target = Translate(inst.Target); var value = Translate(inst.Value); TranslatedExpression result; if (target.Expression is DirectionExpression && IsCompatibleTypeForMemoryAccess(target.Type, inst.Type, isWrite: true)) { // we can deference the managed reference by stripping away the 'ref' result = target.UnwrapChild(((DirectionExpression)target.Expression).Expression); } else { // Cast pointer type if necessary: target = target.ConvertTo(new PointerType(inst.Type), this); result = new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) .WithoutILInstruction() .WithRR(new ResolveResult(inst.Type)); } return Assignment(result, value).WithILInstruction(inst); } /// /// Gets whether reading/writing an element of accessType from the pointer /// is equivalent to reading/writing an element of the pointer's element type. /// bool IsCompatibleTypeForMemoryAccess(IType pointerType, IType accessType, bool isWrite) { IType memoryType; if (pointerType is PointerType) memoryType = ((PointerType)pointerType).ElementType; else if (pointerType is ByReferenceType) memoryType = ((ByReferenceType)pointerType).ElementType; else return false; if (memoryType.Equals(accessType)) return true; // If the types are not equal, the access still might produce equal results: if (memoryType.IsReferenceType == true && accessType.IsReferenceType == true) return true; ITypeDefinition memoryTypeDef = memoryType.GetDefinition(); if (memoryTypeDef != null) { switch (memoryTypeDef.KnownTypeCode) { case KnownTypeCode.Byte: case KnownTypeCode.SByte: // Reading small integers of different signs is not equivalent due to sign extension, // but writes are equivalent (truncation works the same for signed and unsigned) return isWrite && (accessType.IsKnownType(KnownTypeCode.Byte) || accessType.IsKnownType(KnownTypeCode.SByte)); case KnownTypeCode.Int16: case KnownTypeCode.UInt16: return isWrite && (accessType.IsKnownType(KnownTypeCode.Int16) || accessType.IsKnownType(KnownTypeCode.UInt16)); case KnownTypeCode.Int32: case KnownTypeCode.UInt32: return isWrite && (accessType.IsKnownType(KnownTypeCode.Int32) || accessType.IsKnownType(KnownTypeCode.UInt32)); case KnownTypeCode.IntPtr: case KnownTypeCode.UIntPtr: return accessType.IsKnownType(KnownTypeCode.IntPtr) || accessType.IsKnownType(KnownTypeCode.UIntPtr); case KnownTypeCode.Int64: case KnownTypeCode.UInt64: return accessType.IsKnownType(KnownTypeCode.Int64) || accessType.IsKnownType(KnownTypeCode.UInt64); } } return false; } protected internal override TranslatedExpression VisitLdFld(LdFld inst) { return ConvertField(inst.Field, inst.Target).WithILInstruction(inst); } protected internal override TranslatedExpression VisitStFld(StFld inst) { return Assignment(ConvertField(inst.Field, inst.Target).WithoutILInstruction(), Translate(inst.Value)).WithILInstruction(inst); } protected internal override TranslatedExpression VisitLdsFld(LdsFld inst) { return ConvertField(inst.Field).WithILInstruction(inst); } protected internal override TranslatedExpression VisitStsFld(StsFld inst) { return Assignment(ConvertField(inst.Field).WithoutILInstruction(), Translate(inst.Value)).WithILInstruction(inst); } protected internal override TranslatedExpression VisitLdLen(LdLen inst) { TranslatedExpression arrayExpr = Translate(inst.Array); // TODO: what if arrayExpr is not an array type? var lenExpr = arrayExpr.Expression.Member("LongLength") .WithILInstruction(inst) .WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Int64))); return lenExpr.ConvertTo(compilation.FindType(KnownTypeCode.IntPtr), this); } protected internal override TranslatedExpression VisitLdElema(LdElema inst) { TranslatedExpression arrayExpr = Translate(inst.Array); var arrayType = arrayExpr.Type as ArrayType; // TODO: what if arrayExpr is not an array type? // TODO: what if the type of the ldelema instruction does not match the array type? TranslatedExpression expr = new IndexerExpression(arrayExpr, Translate(inst.Index)) .WithILInstruction(inst).WithRR(new ResolveResult(arrayType != null ? arrayType.ElementType : SpecialType.UnknownType)); return new DirectionExpression(FieldDirection.Ref, expr) .WithoutILInstruction().WithRR(new ResolveResult(new ByReferenceType(expr.Type))); } protected internal override TranslatedExpression VisitUnboxAny(UnboxAny inst) { var arg = Translate(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(inst.Type, arg.ResolveResult, Conversion.UnboxingConversion)); } protected internal override TranslatedExpression VisitBox(Box inst) { var obj = compilation.FindType(KnownTypeCode.Object); var arg = Translate(inst.Argument).ConvertTo(inst.Type, this); return new CastExpression(ConvertType(obj), arg.Expression) .WithILInstruction(inst) .WithRR(new ConversionResolveResult(obj, arg.ResolveResult, Conversion.BoxingConversion)); } protected override TranslatedExpression Default(ILInstruction inst) { return ErrorExpression("OpCode not supported: " + inst.OpCode); } static TranslatedExpression ErrorExpression(string message) { var e = new ErrorExpression(); e.AddChild(new Comment(message, CommentType.MultiLine), Roles.Comment); return e.WithoutILInstruction().WithRR(ErrorResolveResult.UnknownError); } } }