|
/* ****************************************************************************
*
* 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);
}
}
}
}
|