/* **************************************************************************** * * Copyright (c) Microsoft Corporation. * * This source code is subject to terms and conditions of the Apache License, Version 2.0. A * copy of the license can be found in the License.html file at the root of this distribution. If * you cannot locate the Apache License, Version 2.0, please send an email to * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound * by the terms of the Apache License, Version 2.0. * * You must not remove this notice, or any other, from this software. * * * ***************************************************************************/ using System; using System.Diagnostics; using System.Dynamic.Utils; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; #if SILVERLIGHT using System.Core; #endif #if CLR2 namespace Microsoft.Scripting.Ast.Compiler { #else namespace System.Linq.Expressions.Compiler { #endif partial class LambdaCompiler { private void EmitQuoteUnaryExpression(Expression expr) { EmitQuote((UnaryExpression)expr); } private void EmitQuote(UnaryExpression quote) { // emit the quoted expression as a runtime constant EmitConstant(quote.Operand, quote.Type); // Heuristic: only emit the tree rewrite logic if we have hoisted // locals. if (_scope.NearestHoistedLocals != null) { // HoistedLocals is internal so emit as System.Object EmitConstant(_scope.NearestHoistedLocals, typeof(object)); _scope.EmitGet(_scope.NearestHoistedLocals.SelfVariable); _ilg.Emit(OpCodes.Call, typeof(RuntimeOps).GetMethod("Quote")); if (quote.Type != typeof(Expression)) { _ilg.Emit(OpCodes.Castclass, quote.Type); } } } private void EmitThrowUnaryExpression(Expression expr) { EmitThrow((UnaryExpression)expr, CompilationFlags.EmitAsDefaultType); } private void EmitThrow(UnaryExpression expr, CompilationFlags flags) { if (expr.Operand == null) { CheckRethrow(); _ilg.Emit(OpCodes.Rethrow); } else { EmitExpression(expr.Operand); _ilg.Emit(OpCodes.Throw); } EmitUnreachable(expr, flags); } private void EmitUnaryExpression(Expression expr, CompilationFlags flags) { EmitUnary((UnaryExpression)expr, flags); } private void EmitUnary(UnaryExpression node, CompilationFlags flags) { if (node.Method != null) { EmitUnaryMethod(node, flags); } else if (node.NodeType == ExpressionType.NegateChecked && TypeUtils.IsInteger(node.Operand.Type)) { EmitExpression(node.Operand); LocalBuilder loc = GetLocal(node.Operand.Type); _ilg.Emit(OpCodes.Stloc, loc); _ilg.EmitInt(0); _ilg.EmitConvertToType(typeof(int), node.Operand.Type, false); _ilg.Emit(OpCodes.Ldloc, loc); FreeLocal(loc); EmitBinaryOperator(ExpressionType.SubtractChecked, node.Operand.Type, node.Operand.Type, node.Type, false); } else { EmitExpression(node.Operand); EmitUnaryOperator(node.NodeType, node.Operand.Type, node.Type); } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] private void EmitUnaryOperator(ExpressionType op, Type operandType, Type resultType) { bool operandIsNullable = TypeUtils.IsNullableType(operandType); if (op == ExpressionType.ArrayLength) { _ilg.Emit(OpCodes.Ldlen); return; } if (operandIsNullable) { switch (op) { case ExpressionType.Not: { if (operandType != typeof(bool?)) { goto case ExpressionType.Negate; } Label labEnd = _ilg.DefineLabel(); LocalBuilder loc = GetLocal(operandType); // store values (reverse order since they are already on the stack) _ilg.Emit(OpCodes.Stloc, loc); // test for null _ilg.Emit(OpCodes.Ldloca, loc); _ilg.EmitHasValue(operandType); _ilg.Emit(OpCodes.Brfalse_S, labEnd); // do op on non-null value _ilg.Emit(OpCodes.Ldloca, loc); _ilg.EmitGetValueOrDefault(operandType); Type nnOperandType = TypeUtils.GetNonNullableType(operandType); EmitUnaryOperator(op, nnOperandType, typeof(bool)); // construct result ConstructorInfo ci = resultType.GetConstructor(new Type[] { typeof(bool) }); _ilg.Emit(OpCodes.Newobj, ci); _ilg.Emit(OpCodes.Stloc, loc); _ilg.MarkLabel(labEnd); _ilg.Emit(OpCodes.Ldloc, loc); FreeLocal(loc); return; } case ExpressionType.UnaryPlus: case ExpressionType.NegateChecked: case ExpressionType.Negate: case ExpressionType.Increment: case ExpressionType.Decrement: case ExpressionType.OnesComplement: case ExpressionType.IsFalse: case ExpressionType.IsTrue: { Debug.Assert(TypeUtils.AreEquivalent(operandType, resultType)); Label labIfNull = _ilg.DefineLabel(); Label labEnd = _ilg.DefineLabel(); LocalBuilder loc = GetLocal(operandType); // check for null _ilg.Emit(OpCodes.Stloc, loc); _ilg.Emit(OpCodes.Ldloca, loc); _ilg.EmitHasValue(operandType); _ilg.Emit(OpCodes.Brfalse_S, labIfNull); // apply operator to non-null value _ilg.Emit(OpCodes.Ldloca, loc); _ilg.EmitGetValueOrDefault(operandType); Type nnOperandType = TypeUtils.GetNonNullableType(resultType); EmitUnaryOperator(op, nnOperandType, nnOperandType); // construct result ConstructorInfo ci = resultType.GetConstructor(new Type[] { nnOperandType }); _ilg.Emit(OpCodes.Newobj, ci); _ilg.Emit(OpCodes.Stloc, loc); _ilg.Emit(OpCodes.Br_S, labEnd); // if null then create a default one _ilg.MarkLabel(labIfNull); _ilg.Emit(OpCodes.Ldloca, loc); _ilg.Emit(OpCodes.Initobj, resultType); _ilg.MarkLabel(labEnd); _ilg.Emit(OpCodes.Ldloc, loc); FreeLocal(loc); return; } case ExpressionType.TypeAs: _ilg.Emit(OpCodes.Box, operandType); _ilg.Emit(OpCodes.Isinst, resultType); if (TypeUtils.IsNullableType(resultType)) { _ilg.Emit(OpCodes.Unbox_Any, resultType); } return; default: throw Error.UnhandledUnary(op); } } else { switch (op) { case ExpressionType.Not: if (operandType == typeof(bool)) { _ilg.Emit(OpCodes.Ldc_I4_0); _ilg.Emit(OpCodes.Ceq); } else { _ilg.Emit(OpCodes.Not); } break; case ExpressionType.OnesComplement: _ilg.Emit(OpCodes.Not); break; case ExpressionType.IsFalse: _ilg.Emit(OpCodes.Ldc_I4_0); _ilg.Emit(OpCodes.Ceq); // Not an arithmetic operation -> no conversion return; case ExpressionType.IsTrue: _ilg.Emit(OpCodes.Ldc_I4_1); _ilg.Emit(OpCodes.Ceq); // Not an arithmetic operation -> no conversion return; case ExpressionType.UnaryPlus: _ilg.Emit(OpCodes.Nop); break; case ExpressionType.Negate: case ExpressionType.NegateChecked: _ilg.Emit(OpCodes.Neg); break; case ExpressionType.TypeAs: if (operandType.IsValueType) { _ilg.Emit(OpCodes.Box, operandType); } _ilg.Emit(OpCodes.Isinst, resultType); if (TypeUtils.IsNullableType(resultType)) { _ilg.Emit(OpCodes.Unbox_Any, resultType); } // Not an arithmetic operation -> no conversion return; case ExpressionType.Increment: EmitConstantOne(resultType); _ilg.Emit(OpCodes.Add); break; case ExpressionType.Decrement: EmitConstantOne(resultType); _ilg.Emit(OpCodes.Sub); break; default: throw Error.UnhandledUnary(op); } EmitConvertArithmeticResult(op, resultType); } } private void EmitConstantOne(Type type) { switch (Type.GetTypeCode(type)) { case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.Int16: case TypeCode.Int32: _ilg.Emit(OpCodes.Ldc_I4_1); break; case TypeCode.Int64: case TypeCode.UInt64: _ilg.Emit(OpCodes.Ldc_I8, (long)1); break; case TypeCode.Single: _ilg.Emit(OpCodes.Ldc_R4, 1.0f); break; case TypeCode.Double: _ilg.Emit(OpCodes.Ldc_R8, 1.0d); break; default: // we only have to worry about aritmetic types, see // TypeUtils.IsArithmetic throw ContractUtils.Unreachable; } } private void EmitUnboxUnaryExpression(Expression expr) { var node = (UnaryExpression)expr; Debug.Assert(node.Type.IsValueType && !TypeUtils.IsNullableType(node.Type)); // Unbox_Any leaves the value on the stack EmitExpression(node.Operand); _ilg.Emit(OpCodes.Unbox_Any, node.Type); } private void EmitConvertUnaryExpression(Expression expr, CompilationFlags flags) { EmitConvert((UnaryExpression)expr, flags); } private void EmitConvert(UnaryExpression node, CompilationFlags flags) { if (node.Method != null) { // User-defined conversions are only lifted if both source and // destination types are value types. The C# compiler gets this wrong. // In C#, if you have an implicit conversion from int->MyClass and you // "lift" the conversion to int?->MyClass then a null int? goes to a // null MyClass. This is contrary to the specification, which states // that the correct behaviour is to unwrap the int?, throw an exception // if it is null, and then call the conversion. // // We cannot fix this in C# but there is no reason why we need to // propagate this bug into the expression tree API. Unfortunately // this means that when the C# compiler generates the lambda // (int? i)=>(MyClass)i, we will get different results for converting // that lambda to a delegate directly and converting that lambda to // an expression tree and then compiling it. We can live with this // discrepancy however. if (node.IsLifted && (!node.Type.IsValueType || !node.Operand.Type.IsValueType)) { ParameterInfo[] pis = node.Method.GetParametersCached(); Debug.Assert(pis != null && pis.Length == 1); Type paramType = pis[0].ParameterType; if (paramType.IsByRef) { paramType = paramType.GetElementType(); } UnaryExpression e = Expression.Convert( Expression.Call( node.Method, Expression.Convert(node.Operand, pis[0].ParameterType) ), node.Type ); EmitConvert(e, flags); } else { EmitUnaryMethod(node, flags); } } else if (node.Type == typeof(void)) { EmitExpressionAsVoid(node.Operand, flags); } else { if (TypeUtils.AreEquivalent(node.Operand.Type, node.Type)) { EmitExpression(node.Operand, flags); } else { // A conversion is emitted after emitting the operand, no tail call is emitted EmitExpression(node.Operand); _ilg.EmitConvertToType(node.Operand.Type, node.Type, node.NodeType == ExpressionType.ConvertChecked); } } } private void EmitUnaryMethod(UnaryExpression node, CompilationFlags flags) { if (node.IsLifted) { ParameterExpression v = Expression.Variable(TypeUtils.GetNonNullableType(node.Operand.Type), null); MethodCallExpression mc = Expression.Call(node.Method, v); Type resultType = TypeUtils.GetNullableType(mc.Type); EmitLift(node.NodeType, resultType, mc, new ParameterExpression[] { v }, new Expression[] { node.Operand }); _ilg.EmitConvertToType(resultType, node.Type, false); } else { EmitMethodCallExpression(Expression.Call(node.Method, node.Operand), flags); } } } }