1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
5 * This source code is subject to terms and conditions of the Microsoft Public License. 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 Microsoft Public License, 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 Microsoft Public License.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
15 using System; using Microsoft;
18 using System.Diagnostics;
20 using System.Dynamic.Utils;
22 using Microsoft.Scripting.Utils;
24 using System.Reflection;
25 using System.Reflection.Emit;
26 using System.Runtime.CompilerServices;
28 using Microsoft.Runtime.CompilerServices;
33 namespace System.Linq.Expressions.Compiler {
35 namespace Microsoft.Linq.Expressions.Compiler {
37 partial class LambdaCompiler {
39 private void EmitQuoteUnaryExpression(Expression expr) {
40 EmitQuote((UnaryExpression)expr);
44 private void EmitQuote(UnaryExpression quote) {
45 // emit the quoted expression as a runtime constant
46 EmitConstant(quote.Operand, quote.Type);
48 // Heuristic: only emit the tree rewrite logic if we have hoisted
50 if (_scope.NearestHoistedLocals != null) {
51 // HoistedLocals is internal so emit as System.Object
52 EmitConstant(_scope.NearestHoistedLocals, typeof(object));
53 _scope.EmitGet(_scope.NearestHoistedLocals.SelfVariable);
54 _ilg.Emit(OpCodes.Call, typeof(RuntimeOps).GetMethod("Quote"));
56 if (quote.Type != typeof(Expression)) {
57 _ilg.Emit(OpCodes.Castclass, quote.Type);
62 private void EmitThrowUnaryExpression(Expression expr) {
63 EmitThrow((UnaryExpression)expr, CompilationFlags.EmitAsDefaultType);
66 private void EmitThrow(UnaryExpression expr, CompilationFlags flags) {
67 if (expr.Operand == null) {
70 _ilg.Emit(OpCodes.Rethrow);
72 EmitExpression(expr.Operand);
73 _ilg.Emit(OpCodes.Throw);
76 EmitUnreachable(expr, flags);
79 private void EmitUnaryExpression(Expression expr, CompilationFlags flags) {
80 EmitUnary((UnaryExpression)expr, flags);
83 private void EmitUnary(UnaryExpression node, CompilationFlags flags) {
84 if (node.Method != null) {
85 EmitUnaryMethod(node, flags);
86 } else if (node.NodeType == ExpressionType.NegateChecked && TypeUtils.IsInteger(node.Operand.Type)) {
87 EmitExpression(node.Operand);
88 LocalBuilder loc = GetLocal(node.Operand.Type);
89 _ilg.Emit(OpCodes.Stloc, loc);
91 _ilg.EmitConvertToType(typeof(int), node.Operand.Type, false);
92 _ilg.Emit(OpCodes.Ldloc, loc);
94 EmitBinaryOperator(ExpressionType.SubtractChecked, node.Operand.Type, node.Operand.Type, node.Type, false);
96 EmitExpression(node.Operand);
97 EmitUnaryOperator(node.NodeType, node.Operand.Type, node.Type);
101 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
102 private void EmitUnaryOperator(ExpressionType op, Type operandType, Type resultType) {
103 bool operandIsNullable = TypeUtils.IsNullableType(operandType);
105 if (op == ExpressionType.ArrayLength) {
106 _ilg.Emit(OpCodes.Ldlen);
110 if (operandIsNullable) {
112 case ExpressionType.Not: {
113 if (operandType != typeof(bool?))
114 goto case ExpressionType.Negate;
116 Label labIfNull = _ilg.DefineLabel();
117 Label labEnd = _ilg.DefineLabel();
118 LocalBuilder loc = GetLocal(operandType);
120 // store values (reverse order since they are already on the stack)
121 _ilg.Emit(OpCodes.Stloc, loc);
124 _ilg.Emit(OpCodes.Ldloca, loc);
125 _ilg.EmitHasValue(operandType);
126 _ilg.Emit(OpCodes.Brfalse_S, labEnd);
128 // do op on non-null value
129 _ilg.Emit(OpCodes.Ldloca, loc);
130 _ilg.EmitGetValueOrDefault(operandType);
131 Type nnOperandType = TypeUtils.GetNonNullableType(operandType);
132 EmitUnaryOperator(op, nnOperandType, typeof(bool));
135 ConstructorInfo ci = resultType.GetConstructor(new Type[] { typeof(bool) });
136 _ilg.Emit(OpCodes.Newobj, ci);
137 _ilg.Emit(OpCodes.Stloc, loc);
139 _ilg.MarkLabel(labEnd);
140 _ilg.Emit(OpCodes.Ldloc, loc);
144 case ExpressionType.UnaryPlus:
145 case ExpressionType.NegateChecked:
146 case ExpressionType.Negate:
147 case ExpressionType.Increment:
148 case ExpressionType.Decrement:
149 case ExpressionType.OnesComplement:
150 case ExpressionType.IsFalse:
151 case ExpressionType.IsTrue: {
152 Debug.Assert(TypeUtils.AreEquivalent(operandType, resultType));
153 Label labIfNull = _ilg.DefineLabel();
154 Label labEnd = _ilg.DefineLabel();
155 LocalBuilder loc = GetLocal(operandType);
158 _ilg.Emit(OpCodes.Stloc, loc);
159 _ilg.Emit(OpCodes.Ldloca, loc);
160 _ilg.EmitHasValue(operandType);
161 _ilg.Emit(OpCodes.Brfalse_S, labIfNull);
163 // apply operator to non-null value
164 _ilg.Emit(OpCodes.Ldloca, loc);
165 _ilg.EmitGetValueOrDefault(operandType);
166 Type nnOperandType = TypeUtils.GetNonNullableType(resultType);
167 EmitUnaryOperator(op, nnOperandType, nnOperandType);
170 ConstructorInfo ci = resultType.GetConstructor(new Type[] { nnOperandType });
171 _ilg.Emit(OpCodes.Newobj, ci);
172 _ilg.Emit(OpCodes.Stloc, loc);
173 _ilg.Emit(OpCodes.Br_S, labEnd);
175 // if null then create a default one
176 _ilg.MarkLabel(labIfNull);
177 _ilg.Emit(OpCodes.Ldloca, loc);
178 _ilg.Emit(OpCodes.Initobj, resultType);
180 _ilg.MarkLabel(labEnd);
181 _ilg.Emit(OpCodes.Ldloc, loc);
185 case ExpressionType.TypeAs:
186 _ilg.Emit(OpCodes.Box, operandType);
187 _ilg.Emit(OpCodes.Isinst, resultType);
188 if (TypeUtils.IsNullableType(resultType)) {
189 _ilg.Emit(OpCodes.Unbox_Any, resultType);
193 throw Error.UnhandledUnary(op);
197 case ExpressionType.Not:
198 if (operandType == typeof(bool)) {
199 _ilg.Emit(OpCodes.Ldc_I4_0);
200 _ilg.Emit(OpCodes.Ceq);
202 _ilg.Emit(OpCodes.Not);
205 case ExpressionType.OnesComplement:
206 _ilg.Emit(OpCodes.Not);
208 case ExpressionType.IsFalse:
209 _ilg.Emit(OpCodes.Ldc_I4_0);
210 _ilg.Emit(OpCodes.Ceq);
211 // Not an arithmetic operation -> no conversion
213 case ExpressionType.IsTrue:
214 _ilg.Emit(OpCodes.Ldc_I4_1);
215 _ilg.Emit(OpCodes.Ceq);
216 // Not an arithmetic operation -> no conversion
218 case ExpressionType.UnaryPlus:
219 _ilg.Emit(OpCodes.Nop);
221 case ExpressionType.Negate:
222 case ExpressionType.NegateChecked:
223 _ilg.Emit(OpCodes.Neg);
225 case ExpressionType.TypeAs:
226 if (operandType.IsValueType) {
227 _ilg.Emit(OpCodes.Box, operandType);
229 _ilg.Emit(OpCodes.Isinst, resultType);
230 if (TypeUtils.IsNullableType(resultType)) {
231 _ilg.Emit(OpCodes.Unbox_Any, resultType);
233 // Not an arithmetic operation -> no conversion
235 case ExpressionType.Increment:
236 EmitConstantOne(resultType);
237 _ilg.Emit(OpCodes.Add);
239 case ExpressionType.Decrement:
240 EmitConstantOne(resultType);
241 _ilg.Emit(OpCodes.Sub);
244 throw Error.UnhandledUnary(op);
247 EmitConvertArithmeticResult(op, resultType);
251 private void EmitConstantOne(Type type) {
252 switch (Type.GetTypeCode(type)) {
253 case TypeCode.UInt16:
254 case TypeCode.UInt32:
257 _ilg.Emit(OpCodes.Ldc_I4_1);
260 case TypeCode.UInt64:
261 _ilg.Emit(OpCodes.Ldc_I8, (long)1);
263 case TypeCode.Single:
264 _ilg.Emit(OpCodes.Ldc_R4, 1.0f);
266 case TypeCode.Double:
267 _ilg.Emit(OpCodes.Ldc_R8, 1.0d);
270 // we only have to worry about aritmetic types, see
271 // TypeUtils.IsArithmetic
272 throw ContractUtils.Unreachable;
276 private void EmitUnboxUnaryExpression(Expression expr) {
277 var node = (UnaryExpression)expr;
278 Debug.Assert(node.Type.IsValueType && !TypeUtils.IsNullableType(node.Type));
280 // Unbox_Any leaves the value on the stack
281 EmitExpression(node.Operand);
282 _ilg.Emit(OpCodes.Unbox_Any, node.Type);
285 private void EmitConvertUnaryExpression(Expression expr, CompilationFlags flags) {
286 EmitConvert((UnaryExpression)expr, flags);
289 private void EmitConvert(UnaryExpression node, CompilationFlags flags) {
290 if (node.Method != null) {
291 // User-defined conversions are only lifted if both source and
292 // destination types are value types. The C# compiler gets this wrong.
293 // In C#, if you have an implicit conversion from int->MyClass and you
294 // "lift" the conversion to int?->MyClass then a null int? goes to a
295 // null MyClass. This is contrary to the specification, which states
296 // that the correct behaviour is to unwrap the int?, throw an exception
297 // if it is null, and then call the conversion.
299 // We cannot fix this in C# but there is no reason why we need to
300 // propagate this bug into the expression tree API. Unfortunately
301 // this means that when the C# compiler generates the lambda
302 // (int? i)=>(MyClass)i, we will get different results for converting
303 // that lambda to a delegate directly and converting that lambda to
304 // an expression tree and then compiling it. We can live with this
305 // discrepancy however.
307 if (node.IsLifted && (!node.Type.IsValueType || !node.Operand.Type.IsValueType)) {
308 ParameterInfo[] pis = node.Method.GetParametersCached();
309 Debug.Assert(pis != null && pis.Length == 1);
310 Type paramType = pis[0].ParameterType;
311 if (paramType.IsByRef) {
312 paramType = paramType.GetElementType();
315 UnaryExpression e = Expression.Convert(
318 Expression.Convert(node.Operand, pis[0].ParameterType)
323 EmitConvert(e, flags);
325 EmitUnaryMethod(node, flags);
327 } else if (node.Type == typeof(void)) {
328 EmitExpressionAsVoid(node.Operand, flags);
330 if (TypeUtils.AreEquivalent(node.Operand.Type, node.Type)) {
331 EmitExpression(node.Operand, flags);
333 // A conversion is emitted after emitting the operand, no tail call is emitted
334 EmitExpression(node.Operand);
335 _ilg.EmitConvertToType(node.Operand.Type, node.Type, node.NodeType == ExpressionType.ConvertChecked);
341 private void EmitUnaryMethod(UnaryExpression node, CompilationFlags flags) {
343 ParameterExpression v = Expression.Variable(TypeUtils.GetNonNullableType(node.Operand.Type), null);
344 MethodCallExpression mc = Expression.Call(node.Method, v);
346 Type resultType = TypeUtils.GetNullableType(mc.Type);
347 EmitLift(node.NodeType, resultType, mc, new ParameterExpression[] { v }, new Expression[] { node.Operand });
348 _ilg.EmitConvertToType(resultType, node.Type, false);
350 EmitMethodCallExpression(Expression.Call(node.Method, node.Operand), flags);