1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
6 * copy of the license can be found in the License.html file at the root of this distribution. If
7 * you cannot locate the Apache License, Version 2.0, please send an email to
8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
9 * by the terms of the Apache License, Version 2.0.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
17 using System.Diagnostics;
18 using System.Dynamic.Utils;
19 using System.Reflection;
20 using System.Reflection.Emit;
21 using System.Runtime.CompilerServices;
24 namespace Microsoft.Scripting.Ast.Compiler {
26 namespace System.Linq.Expressions.Compiler {
28 partial class LambdaCompiler {
30 private void EmitQuoteUnaryExpression(Expression expr) {
31 EmitQuote((UnaryExpression)expr);
35 private void EmitQuote(UnaryExpression quote) {
36 // emit the quoted expression as a runtime constant
37 EmitConstant(quote.Operand, quote.Type);
39 // Heuristic: only emit the tree rewrite logic if we have hoisted
41 if (_scope.NearestHoistedLocals != null) {
42 // HoistedLocals is internal so emit as System.Object
43 EmitConstant(_scope.NearestHoistedLocals, typeof(object));
44 _scope.EmitGet(_scope.NearestHoistedLocals.SelfVariable);
45 _ilg.Emit(OpCodes.Call, typeof(RuntimeOps).GetMethod("Quote"));
47 if (quote.Type != typeof(Expression)) {
48 _ilg.Emit(OpCodes.Castclass, quote.Type);
53 private void EmitThrowUnaryExpression(Expression expr) {
54 EmitThrow((UnaryExpression)expr, CompilationFlags.EmitAsDefaultType);
57 private void EmitThrow(UnaryExpression expr, CompilationFlags flags) {
58 if (expr.Operand == null) {
61 _ilg.Emit(OpCodes.Rethrow);
63 EmitExpression(expr.Operand);
64 _ilg.Emit(OpCodes.Throw);
67 EmitUnreachable(expr, flags);
70 private void EmitUnaryExpression(Expression expr, CompilationFlags flags) {
71 EmitUnary((UnaryExpression)expr, flags);
74 private void EmitUnary(UnaryExpression node, CompilationFlags flags) {
75 if (node.Method != null) {
76 EmitUnaryMethod(node, flags);
77 } else if (node.NodeType == ExpressionType.NegateChecked && TypeUtils.IsInteger(node.Operand.Type)) {
78 EmitExpression(node.Operand);
79 LocalBuilder loc = GetLocal(node.Operand.Type);
80 _ilg.Emit(OpCodes.Stloc, loc);
82 _ilg.EmitConvertToType(typeof(int), node.Operand.Type, false);
83 _ilg.Emit(OpCodes.Ldloc, loc);
85 EmitBinaryOperator(ExpressionType.SubtractChecked, node.Operand.Type, node.Operand.Type, node.Type, false);
87 EmitExpression(node.Operand);
88 EmitUnaryOperator(node.NodeType, node.Operand.Type, node.Type);
92 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
93 private void EmitUnaryOperator(ExpressionType op, Type operandType, Type resultType) {
94 bool operandIsNullable = TypeUtils.IsNullableType(operandType);
96 if (op == ExpressionType.ArrayLength) {
97 _ilg.Emit(OpCodes.Ldlen);
101 if (operandIsNullable) {
103 case ExpressionType.Not: {
104 if (operandType != typeof(bool?)) {
105 goto case ExpressionType.Negate;
107 Label labEnd = _ilg.DefineLabel();
108 LocalBuilder loc = GetLocal(operandType);
110 // store values (reverse order since they are already on the stack)
111 _ilg.Emit(OpCodes.Stloc, loc);
114 _ilg.Emit(OpCodes.Ldloca, loc);
115 _ilg.EmitHasValue(operandType);
116 _ilg.Emit(OpCodes.Brfalse_S, labEnd);
118 // do op on non-null value
119 _ilg.Emit(OpCodes.Ldloca, loc);
120 _ilg.EmitGetValueOrDefault(operandType);
121 Type nnOperandType = TypeUtils.GetNonNullableType(operandType);
122 EmitUnaryOperator(op, nnOperandType, typeof(bool));
125 ConstructorInfo ci = resultType.GetConstructor(new Type[] { typeof(bool) });
126 _ilg.Emit(OpCodes.Newobj, ci);
127 _ilg.Emit(OpCodes.Stloc, loc);
129 _ilg.MarkLabel(labEnd);
130 _ilg.Emit(OpCodes.Ldloc, loc);
134 case ExpressionType.UnaryPlus:
135 case ExpressionType.NegateChecked:
136 case ExpressionType.Negate:
137 case ExpressionType.Increment:
138 case ExpressionType.Decrement:
139 case ExpressionType.OnesComplement:
140 case ExpressionType.IsFalse:
141 case ExpressionType.IsTrue: {
142 Debug.Assert(TypeUtils.AreEquivalent(operandType, resultType));
143 Label labIfNull = _ilg.DefineLabel();
144 Label labEnd = _ilg.DefineLabel();
145 LocalBuilder loc = GetLocal(operandType);
148 _ilg.Emit(OpCodes.Stloc, loc);
149 _ilg.Emit(OpCodes.Ldloca, loc);
150 _ilg.EmitHasValue(operandType);
151 _ilg.Emit(OpCodes.Brfalse_S, labIfNull);
153 // apply operator to non-null value
154 _ilg.Emit(OpCodes.Ldloca, loc);
155 _ilg.EmitGetValueOrDefault(operandType);
156 Type nnOperandType = TypeUtils.GetNonNullableType(resultType);
157 EmitUnaryOperator(op, nnOperandType, nnOperandType);
160 ConstructorInfo ci = resultType.GetConstructor(new Type[] { nnOperandType });
161 _ilg.Emit(OpCodes.Newobj, ci);
162 _ilg.Emit(OpCodes.Stloc, loc);
163 _ilg.Emit(OpCodes.Br_S, labEnd);
165 // if null then create a default one
166 _ilg.MarkLabel(labIfNull);
167 _ilg.Emit(OpCodes.Ldloca, loc);
168 _ilg.Emit(OpCodes.Initobj, resultType);
170 _ilg.MarkLabel(labEnd);
171 _ilg.Emit(OpCodes.Ldloc, loc);
175 case ExpressionType.TypeAs:
176 _ilg.Emit(OpCodes.Box, operandType);
177 _ilg.Emit(OpCodes.Isinst, resultType);
178 if (TypeUtils.IsNullableType(resultType)) {
179 _ilg.Emit(OpCodes.Unbox_Any, resultType);
183 throw Error.UnhandledUnary(op);
187 case ExpressionType.Not:
188 if (operandType == typeof(bool)) {
189 _ilg.Emit(OpCodes.Ldc_I4_0);
190 _ilg.Emit(OpCodes.Ceq);
192 _ilg.Emit(OpCodes.Not);
195 case ExpressionType.OnesComplement:
196 _ilg.Emit(OpCodes.Not);
198 case ExpressionType.IsFalse:
199 _ilg.Emit(OpCodes.Ldc_I4_0);
200 _ilg.Emit(OpCodes.Ceq);
201 // Not an arithmetic operation -> no conversion
203 case ExpressionType.IsTrue:
204 _ilg.Emit(OpCodes.Ldc_I4_1);
205 _ilg.Emit(OpCodes.Ceq);
206 // Not an arithmetic operation -> no conversion
208 case ExpressionType.UnaryPlus:
209 _ilg.Emit(OpCodes.Nop);
211 case ExpressionType.Negate:
212 case ExpressionType.NegateChecked:
213 _ilg.Emit(OpCodes.Neg);
215 case ExpressionType.TypeAs:
216 if (operandType.IsValueType) {
217 _ilg.Emit(OpCodes.Box, operandType);
219 _ilg.Emit(OpCodes.Isinst, resultType);
220 if (TypeUtils.IsNullableType(resultType)) {
221 _ilg.Emit(OpCodes.Unbox_Any, resultType);
223 // Not an arithmetic operation -> no conversion
225 case ExpressionType.Increment:
226 EmitConstantOne(resultType);
227 _ilg.Emit(OpCodes.Add);
229 case ExpressionType.Decrement:
230 EmitConstantOne(resultType);
231 _ilg.Emit(OpCodes.Sub);
234 throw Error.UnhandledUnary(op);
237 EmitConvertArithmeticResult(op, resultType);
241 private void EmitConstantOne(Type type) {
242 switch (Type.GetTypeCode(type)) {
243 case TypeCode.UInt16:
244 case TypeCode.UInt32:
247 _ilg.Emit(OpCodes.Ldc_I4_1);
250 case TypeCode.UInt64:
251 _ilg.Emit(OpCodes.Ldc_I8, (long)1);
253 case TypeCode.Single:
254 _ilg.Emit(OpCodes.Ldc_R4, 1.0f);
256 case TypeCode.Double:
257 _ilg.Emit(OpCodes.Ldc_R8, 1.0d);
260 // we only have to worry about aritmetic types, see
261 // TypeUtils.IsArithmetic
262 throw ContractUtils.Unreachable;
266 private void EmitUnboxUnaryExpression(Expression expr) {
267 var node = (UnaryExpression)expr;
268 Debug.Assert(node.Type.IsValueType && !TypeUtils.IsNullableType(node.Type));
270 // Unbox_Any leaves the value on the stack
271 EmitExpression(node.Operand);
272 _ilg.Emit(OpCodes.Unbox_Any, node.Type);
275 private void EmitConvertUnaryExpression(Expression expr, CompilationFlags flags) {
276 EmitConvert((UnaryExpression)expr, flags);
279 private void EmitConvert(UnaryExpression node, CompilationFlags flags) {
280 if (node.Method != null) {
281 // User-defined conversions are only lifted if both source and
282 // destination types are value types. The C# compiler gets this wrong.
283 // In C#, if you have an implicit conversion from int->MyClass and you
284 // "lift" the conversion to int?->MyClass then a null int? goes to a
285 // null MyClass. This is contrary to the specification, which states
286 // that the correct behaviour is to unwrap the int?, throw an exception
287 // if it is null, and then call the conversion.
289 // We cannot fix this in C# but there is no reason why we need to
290 // propagate this bug into the expression tree API. Unfortunately
291 // this means that when the C# compiler generates the lambda
292 // (int? i)=>(MyClass)i, we will get different results for converting
293 // that lambda to a delegate directly and converting that lambda to
294 // an expression tree and then compiling it. We can live with this
295 // discrepancy however.
297 if (node.IsLifted && (!node.Type.IsValueType || !node.Operand.Type.IsValueType)) {
298 ParameterInfo[] pis = node.Method.GetParametersCached();
299 Debug.Assert(pis != null && pis.Length == 1);
300 Type paramType = pis[0].ParameterType;
301 if (paramType.IsByRef) {
302 paramType = paramType.GetElementType();
305 UnaryExpression e = Expression.Convert(
308 Expression.Convert(node.Operand, pis[0].ParameterType)
313 EmitConvert(e, flags);
315 EmitUnaryMethod(node, flags);
317 } else if (node.Type == typeof(void)) {
318 EmitExpressionAsVoid(node.Operand, flags);
320 if (TypeUtils.AreEquivalent(node.Operand.Type, node.Type)) {
321 EmitExpression(node.Operand, flags);
323 // A conversion is emitted after emitting the operand, no tail call is emitted
324 EmitExpression(node.Operand);
325 _ilg.EmitConvertToType(node.Operand.Type, node.Type, node.NodeType == ExpressionType.ConvertChecked);
331 private void EmitUnaryMethod(UnaryExpression node, CompilationFlags flags) {
333 ParameterExpression v = Expression.Variable(TypeUtils.GetNonNullableType(node.Operand.Type), null);
334 MethodCallExpression mc = Expression.Call(node.Method, v);
336 Type resultType = TypeUtils.GetNullableType(mc.Type);
337 EmitLift(node.NodeType, resultType, mc, new ParameterExpression[] { v }, new Expression[] { node.Operand });
338 _ilg.EmitConvertToType(resultType, node.Type, false);
340 EmitMethodCallExpression(Expression.Call(node.Method, node.Operand), flags);