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;
28 namespace Microsoft.Scripting.Ast.Compiler {
30 namespace System.Linq.Expressions.Compiler {
32 partial class LambdaCompiler {
34 private void EmitQuoteUnaryExpression(Expression expr) {
35 EmitQuote((UnaryExpression)expr);
39 private void EmitQuote(UnaryExpression quote) {
40 // emit the quoted expression as a runtime constant
41 EmitConstant(quote.Operand, quote.Type);
43 // Heuristic: only emit the tree rewrite logic if we have hoisted
45 if (_scope.NearestHoistedLocals != null) {
46 // HoistedLocals is internal so emit as System.Object
47 EmitConstant(_scope.NearestHoistedLocals, typeof(object));
48 _scope.EmitGet(_scope.NearestHoistedLocals.SelfVariable);
49 _ilg.Emit(OpCodes.Call, typeof(RuntimeOps).GetMethod("Quote"));
51 if (quote.Type != typeof(Expression)) {
52 _ilg.Emit(OpCodes.Castclass, quote.Type);
57 private void EmitThrowUnaryExpression(Expression expr) {
58 EmitThrow((UnaryExpression)expr, CompilationFlags.EmitAsDefaultType);
61 private void EmitThrow(UnaryExpression expr, CompilationFlags flags) {
62 if (expr.Operand == null) {
65 _ilg.Emit(OpCodes.Rethrow);
67 EmitExpression(expr.Operand);
68 _ilg.Emit(OpCodes.Throw);
71 EmitUnreachable(expr, flags);
74 private void EmitUnaryExpression(Expression expr, CompilationFlags flags) {
75 EmitUnary((UnaryExpression)expr, flags);
78 private void EmitUnary(UnaryExpression node, CompilationFlags flags) {
79 if (node.Method != null) {
80 EmitUnaryMethod(node, flags);
81 } else if (node.NodeType == ExpressionType.NegateChecked && TypeUtils.IsInteger(node.Operand.Type)) {
82 EmitExpression(node.Operand);
83 LocalBuilder loc = GetLocal(node.Operand.Type);
84 _ilg.Emit(OpCodes.Stloc, loc);
86 _ilg.EmitConvertToType(typeof(int), node.Operand.Type, false);
87 _ilg.Emit(OpCodes.Ldloc, loc);
89 EmitBinaryOperator(ExpressionType.SubtractChecked, node.Operand.Type, node.Operand.Type, node.Type, false);
91 EmitExpression(node.Operand);
92 EmitUnaryOperator(node.NodeType, node.Operand.Type, node.Type);
96 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
97 private void EmitUnaryOperator(ExpressionType op, Type operandType, Type resultType) {
98 bool operandIsNullable = TypeUtils.IsNullableType(operandType);
100 if (op == ExpressionType.ArrayLength) {
101 _ilg.Emit(OpCodes.Ldlen);
105 if (operandIsNullable) {
107 case ExpressionType.Not: {
108 if (operandType != typeof(bool?)) {
109 goto case ExpressionType.Negate;
111 Label labEnd = _ilg.DefineLabel();
112 LocalBuilder loc = GetLocal(operandType);
114 // store values (reverse order since they are already on the stack)
115 _ilg.Emit(OpCodes.Stloc, loc);
118 _ilg.Emit(OpCodes.Ldloca, loc);
119 _ilg.EmitHasValue(operandType);
120 _ilg.Emit(OpCodes.Brfalse_S, labEnd);
122 // do op on non-null value
123 _ilg.Emit(OpCodes.Ldloca, loc);
124 _ilg.EmitGetValueOrDefault(operandType);
125 Type nnOperandType = TypeUtils.GetNonNullableType(operandType);
126 EmitUnaryOperator(op, nnOperandType, typeof(bool));
129 ConstructorInfo ci = resultType.GetConstructor(new Type[] { typeof(bool) });
130 _ilg.Emit(OpCodes.Newobj, ci);
131 _ilg.Emit(OpCodes.Stloc, loc);
133 _ilg.MarkLabel(labEnd);
134 _ilg.Emit(OpCodes.Ldloc, loc);
138 case ExpressionType.UnaryPlus:
139 case ExpressionType.NegateChecked:
140 case ExpressionType.Negate:
141 case ExpressionType.Increment:
142 case ExpressionType.Decrement:
143 case ExpressionType.OnesComplement:
144 case ExpressionType.IsFalse:
145 case ExpressionType.IsTrue: {
146 Debug.Assert(TypeUtils.AreEquivalent(operandType, resultType));
147 Label labIfNull = _ilg.DefineLabel();
148 Label labEnd = _ilg.DefineLabel();
149 LocalBuilder loc = GetLocal(operandType);
152 _ilg.Emit(OpCodes.Stloc, loc);
153 _ilg.Emit(OpCodes.Ldloca, loc);
154 _ilg.EmitHasValue(operandType);
155 _ilg.Emit(OpCodes.Brfalse_S, labIfNull);
157 // apply operator to non-null value
158 _ilg.Emit(OpCodes.Ldloca, loc);
159 _ilg.EmitGetValueOrDefault(operandType);
160 Type nnOperandType = TypeUtils.GetNonNullableType(resultType);
161 EmitUnaryOperator(op, nnOperandType, nnOperandType);
164 ConstructorInfo ci = resultType.GetConstructor(new Type[] { nnOperandType });
165 _ilg.Emit(OpCodes.Newobj, ci);
166 _ilg.Emit(OpCodes.Stloc, loc);
167 _ilg.Emit(OpCodes.Br_S, labEnd);
169 // if null then create a default one
170 _ilg.MarkLabel(labIfNull);
171 _ilg.Emit(OpCodes.Ldloca, loc);
172 _ilg.Emit(OpCodes.Initobj, resultType);
174 _ilg.MarkLabel(labEnd);
175 _ilg.Emit(OpCodes.Ldloc, loc);
179 case ExpressionType.TypeAs:
180 _ilg.Emit(OpCodes.Box, operandType);
181 _ilg.Emit(OpCodes.Isinst, resultType);
182 if (TypeUtils.IsNullableType(resultType)) {
183 _ilg.Emit(OpCodes.Unbox_Any, resultType);
187 throw Error.UnhandledUnary(op);
191 case ExpressionType.Not:
192 if (operandType == typeof(bool)) {
193 _ilg.Emit(OpCodes.Ldc_I4_0);
194 _ilg.Emit(OpCodes.Ceq);
196 _ilg.Emit(OpCodes.Not);
199 case ExpressionType.OnesComplement:
200 _ilg.Emit(OpCodes.Not);
202 case ExpressionType.IsFalse:
203 _ilg.Emit(OpCodes.Ldc_I4_0);
204 _ilg.Emit(OpCodes.Ceq);
205 // Not an arithmetic operation -> no conversion
207 case ExpressionType.IsTrue:
208 _ilg.Emit(OpCodes.Ldc_I4_1);
209 _ilg.Emit(OpCodes.Ceq);
210 // Not an arithmetic operation -> no conversion
212 case ExpressionType.UnaryPlus:
213 _ilg.Emit(OpCodes.Nop);
215 case ExpressionType.Negate:
216 case ExpressionType.NegateChecked:
217 _ilg.Emit(OpCodes.Neg);
219 case ExpressionType.TypeAs:
220 if (operandType.IsValueType) {
221 _ilg.Emit(OpCodes.Box, operandType);
223 _ilg.Emit(OpCodes.Isinst, resultType);
224 if (TypeUtils.IsNullableType(resultType)) {
225 _ilg.Emit(OpCodes.Unbox_Any, resultType);
227 // Not an arithmetic operation -> no conversion
229 case ExpressionType.Increment:
230 EmitConstantOne(resultType);
231 _ilg.Emit(OpCodes.Add);
233 case ExpressionType.Decrement:
234 EmitConstantOne(resultType);
235 _ilg.Emit(OpCodes.Sub);
238 throw Error.UnhandledUnary(op);
241 EmitConvertArithmeticResult(op, resultType);
245 private void EmitConstantOne(Type type) {
246 switch (Type.GetTypeCode(type)) {
247 case TypeCode.UInt16:
248 case TypeCode.UInt32:
251 _ilg.Emit(OpCodes.Ldc_I4_1);
254 case TypeCode.UInt64:
255 _ilg.Emit(OpCodes.Ldc_I8, (long)1);
257 case TypeCode.Single:
258 _ilg.Emit(OpCodes.Ldc_R4, 1.0f);
260 case TypeCode.Double:
261 _ilg.Emit(OpCodes.Ldc_R8, 1.0d);
264 // we only have to worry about aritmetic types, see
265 // TypeUtils.IsArithmetic
266 throw ContractUtils.Unreachable;
270 private void EmitUnboxUnaryExpression(Expression expr) {
271 var node = (UnaryExpression)expr;
272 Debug.Assert(node.Type.IsValueType && !TypeUtils.IsNullableType(node.Type));
274 // Unbox_Any leaves the value on the stack
275 EmitExpression(node.Operand);
276 _ilg.Emit(OpCodes.Unbox_Any, node.Type);
279 private void EmitConvertUnaryExpression(Expression expr, CompilationFlags flags) {
280 EmitConvert((UnaryExpression)expr, flags);
283 private void EmitConvert(UnaryExpression node, CompilationFlags flags) {
284 if (node.Method != null) {
285 // User-defined conversions are only lifted if both source and
286 // destination types are value types. The C# compiler gets this wrong.
287 // In C#, if you have an implicit conversion from int->MyClass and you
288 // "lift" the conversion to int?->MyClass then a null int? goes to a
289 // null MyClass. This is contrary to the specification, which states
290 // that the correct behaviour is to unwrap the int?, throw an exception
291 // if it is null, and then call the conversion.
293 // We cannot fix this in C# but there is no reason why we need to
301 if (node.IsLifted && (!node.Type.IsValueType || !node.Operand.Type.IsValueType)) {
302 ParameterInfo[] pis = node.Method.GetParametersCached();
303 Debug.Assert(pis != null && pis.Length == 1);
304 Type paramType = pis[0].ParameterType;
305 if (paramType.IsByRef) {
306 paramType = paramType.GetElementType();
309 UnaryExpression e = Expression.Convert(
312 Expression.Convert(node.Operand, pis[0].ParameterType)
317 EmitConvert(e, flags);
319 EmitUnaryMethod(node, flags);
321 } else if (node.Type == typeof(void)) {
322 EmitExpressionAsVoid(node.Operand, flags);
324 if (TypeUtils.AreEquivalent(node.Operand.Type, node.Type)) {
325 EmitExpression(node.Operand, flags);
327 // A conversion is emitted after emitting the operand, no tail call is emitted
328 EmitExpression(node.Operand);
329 _ilg.EmitConvertToType(node.Operand.Type, node.Type, node.NodeType == ExpressionType.ConvertChecked);
335 private void EmitUnaryMethod(UnaryExpression node, CompilationFlags flags) {
337 ParameterExpression v = Expression.Variable(TypeUtils.GetNonNullableType(node.Operand.Type), null);
338 MethodCallExpression mc = Expression.Call(node.Method, v);
340 Type resultType = TypeUtils.GetNullableType(mc.Type);
341 EmitLift(node.NodeType, resultType, mc, new ParameterExpression[] { v }, new Expression[] { node.Operand });
342 _ilg.EmitConvertToType(resultType, node.Type, false);
344 EmitMethodCallExpression(Expression.Call(node.Method, node.Operand), flags);