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.Collections.Generic;
18 using System.Diagnostics;
19 using System.Dynamic.Utils;
20 using System.Reflection;
21 using System.Reflection.Emit;
28 namespace Microsoft.Scripting.Ast.Compiler {
30 namespace System.Linq.Expressions.Compiler {
32 partial class LambdaCompiler {
34 private void EmitBinaryExpression(Expression expr) {
35 EmitBinaryExpression(expr, CompilationFlags.EmitAsNoTail);
38 private void EmitBinaryExpression(Expression expr, CompilationFlags flags) {
39 BinaryExpression b = (BinaryExpression)expr;
41 Debug.Assert(b.NodeType != ExpressionType.AndAlso && b.NodeType != ExpressionType.OrElse && b.NodeType != ExpressionType.Coalesce);
43 if (b.Method != null) {
44 EmitBinaryMethod(b, flags);
48 // For EQ and NE, if there is a user-specified method, use it.
49 // Otherwise implement the C# semantics that allow equality
50 // comparisons on non-primitive nullable structs that don't
52 if ((b.NodeType == ExpressionType.Equal || b.NodeType == ExpressionType.NotEqual) &&
53 (b.Type == typeof(bool) || b.Type == typeof(bool?))) {
55 // If we have x==null, x!=null, null==x or null!=x where x is
56 // nullable but not null, then generate a call to x.HasValue.
57 Debug.Assert(!b.IsLiftedToNull || b.Type == typeof(bool?));
58 if (ConstantCheck.IsNull(b.Left) && !ConstantCheck.IsNull(b.Right) && TypeUtils.IsNullableType(b.Right.Type)) {
59 EmitNullEquality(b.NodeType, b.Right, b.IsLiftedToNull);
62 if (ConstantCheck.IsNull(b.Right) && !ConstantCheck.IsNull(b.Left) && TypeUtils.IsNullableType(b.Left.Type)) {
63 EmitNullEquality(b.NodeType, b.Left, b.IsLiftedToNull);
67 // For EQ and NE, we can avoid some conversions if we're
68 // ultimately just comparing two managed pointers.
69 EmitExpression(GetEqualityOperand(b.Left));
70 EmitExpression(GetEqualityOperand(b.Right));
72 // Otherwise generate it normally
73 EmitExpression(b.Left);
74 EmitExpression(b.Right);
77 EmitBinaryOperator(b.NodeType, b.Left.Type, b.Right.Type, b.Type, b.IsLiftedToNull);
81 private void EmitNullEquality(ExpressionType op, Expression e, bool isLiftedToNull) {
82 Debug.Assert(TypeUtils.IsNullableType(e.Type));
83 Debug.Assert(op == ExpressionType.Equal || op == ExpressionType.NotEqual);
84 // If we are lifted to null then just evaluate the expression for its side effects, discard,
85 // and generate null. If we are not lifted to null then generate a call to HasValue.
87 EmitExpressionAsVoid(e);
88 _ilg.EmitDefault(typeof(bool?));
90 EmitAddress(e, e.Type);
91 _ilg.EmitHasValue(e.Type);
92 if (op == ExpressionType.Equal) {
93 _ilg.Emit(OpCodes.Ldc_I4_0);
94 _ilg.Emit(OpCodes.Ceq);
100 private void EmitBinaryMethod(BinaryExpression b, CompilationFlags flags) {
102 ParameterExpression p1 = Expression.Variable(TypeUtils.GetNonNullableType(b.Left.Type), null);
103 ParameterExpression p2 = Expression.Variable(TypeUtils.GetNonNullableType(b.Right.Type), null);
104 MethodCallExpression mc = Expression.Call(null, b.Method, p1, p2);
105 Type resultType = null;
106 if (b.IsLiftedToNull) {
107 resultType = TypeUtils.GetNullableType(mc.Type);
109 switch (b.NodeType) {
110 case ExpressionType.Equal:
111 case ExpressionType.NotEqual:
112 case ExpressionType.LessThan:
113 case ExpressionType.LessThanOrEqual:
114 case ExpressionType.GreaterThan:
115 case ExpressionType.GreaterThanOrEqual:
116 if (mc.Type != typeof(bool)) {
117 throw Error.ArgumentMustBeBoolean();
119 resultType = typeof(bool);
122 resultType = TypeUtils.GetNullableType(mc.Type);
126 var variables = new ParameterExpression[] { p1, p2 };
127 var arguments = new Expression[] { b.Left, b.Right };
128 ValidateLift(variables, arguments);
129 EmitLift(b.NodeType, resultType, mc, variables, arguments);
131 EmitMethodCallExpression(Expression.Call(null, b.Method, b.Left, b.Right), flags);
136 private void EmitBinaryOperator(ExpressionType op, Type leftType, Type rightType, Type resultType, bool liftedToNull) {
137 bool leftIsNullable = TypeUtils.IsNullableType(leftType);
138 bool rightIsNullable = TypeUtils.IsNullableType(rightType);
141 case ExpressionType.ArrayIndex:
142 if (rightType != typeof(int)) {
143 throw ContractUtils.Unreachable;
145 _ilg.EmitLoadElement(leftType.GetElementType());
147 case ExpressionType.Coalesce:
148 throw Error.UnexpectedCoalesceOperator();
151 if (leftIsNullable || rightIsNullable) {
152 EmitLiftedBinaryOp(op, leftType, rightType, resultType, liftedToNull);
154 EmitUnliftedBinaryOp(op, leftType, rightType);
155 EmitConvertArithmeticResult(op, resultType);
160 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
161 private void EmitUnliftedBinaryOp(ExpressionType op, Type leftType, Type rightType) {
162 Debug.Assert(!TypeUtils.IsNullableType(leftType));
163 Debug.Assert(!TypeUtils.IsNullableType(rightType));
165 if (op == ExpressionType.Equal || op == ExpressionType.NotEqual) {
166 EmitUnliftedEquality(op, leftType);
169 if (!leftType.IsPrimitive) {
170 throw Error.OperatorNotImplementedForType(op, leftType);
173 case ExpressionType.Add:
174 _ilg.Emit(OpCodes.Add);
176 case ExpressionType.AddChecked:
177 if (TypeUtils.IsFloatingPoint(leftType)) {
178 _ilg.Emit(OpCodes.Add);
179 } else if (TypeUtils.IsUnsigned(leftType)) {
180 _ilg.Emit(OpCodes.Add_Ovf_Un);
182 _ilg.Emit(OpCodes.Add_Ovf);
185 case ExpressionType.Subtract:
186 _ilg.Emit(OpCodes.Sub);
188 case ExpressionType.SubtractChecked:
189 if (TypeUtils.IsFloatingPoint(leftType)) {
190 _ilg.Emit(OpCodes.Sub);
191 } else if (TypeUtils.IsUnsigned(leftType)) {
192 _ilg.Emit(OpCodes.Sub_Ovf_Un);
194 _ilg.Emit(OpCodes.Sub_Ovf);
197 case ExpressionType.Multiply:
198 _ilg.Emit(OpCodes.Mul);
200 case ExpressionType.MultiplyChecked:
201 if (TypeUtils.IsFloatingPoint(leftType)) {
202 _ilg.Emit(OpCodes.Mul);
203 } else if (TypeUtils.IsUnsigned(leftType)) {
204 _ilg.Emit(OpCodes.Mul_Ovf_Un);
206 _ilg.Emit(OpCodes.Mul_Ovf);
209 case ExpressionType.Divide:
210 if (TypeUtils.IsUnsigned(leftType)) {
211 _ilg.Emit(OpCodes.Div_Un);
213 _ilg.Emit(OpCodes.Div);
216 case ExpressionType.Modulo:
217 if (TypeUtils.IsUnsigned(leftType)) {
218 _ilg.Emit(OpCodes.Rem_Un);
220 _ilg.Emit(OpCodes.Rem);
223 case ExpressionType.And:
224 case ExpressionType.AndAlso:
225 _ilg.Emit(OpCodes.And);
227 case ExpressionType.Or:
228 case ExpressionType.OrElse:
229 _ilg.Emit(OpCodes.Or);
231 case ExpressionType.LessThan:
232 if (TypeUtils.IsUnsigned(leftType)) {
233 _ilg.Emit(OpCodes.Clt_Un);
235 _ilg.Emit(OpCodes.Clt);
238 case ExpressionType.LessThanOrEqual: {
239 Label labFalse = _ilg.DefineLabel();
240 Label labEnd = _ilg.DefineLabel();
241 if (TypeUtils.IsUnsigned(leftType)) {
242 _ilg.Emit(OpCodes.Ble_Un_S, labFalse);
244 _ilg.Emit(OpCodes.Ble_S, labFalse);
246 _ilg.Emit(OpCodes.Ldc_I4_0);
247 _ilg.Emit(OpCodes.Br_S, labEnd);
248 _ilg.MarkLabel(labFalse);
249 _ilg.Emit(OpCodes.Ldc_I4_1);
250 _ilg.MarkLabel(labEnd);
253 case ExpressionType.GreaterThan:
254 if (TypeUtils.IsUnsigned(leftType)) {
255 _ilg.Emit(OpCodes.Cgt_Un);
257 _ilg.Emit(OpCodes.Cgt);
260 case ExpressionType.GreaterThanOrEqual: {
261 Label labFalse = _ilg.DefineLabel();
262 Label labEnd = _ilg.DefineLabel();
263 if (TypeUtils.IsUnsigned(leftType)) {
264 _ilg.Emit(OpCodes.Bge_Un_S, labFalse);
266 _ilg.Emit(OpCodes.Bge_S, labFalse);
268 _ilg.Emit(OpCodes.Ldc_I4_0);
269 _ilg.Emit(OpCodes.Br_S, labEnd);
270 _ilg.MarkLabel(labFalse);
271 _ilg.Emit(OpCodes.Ldc_I4_1);
272 _ilg.MarkLabel(labEnd);
275 case ExpressionType.ExclusiveOr:
276 _ilg.Emit(OpCodes.Xor);
278 case ExpressionType.LeftShift:
279 if (rightType != typeof(int)) {
280 throw ContractUtils.Unreachable;
282 _ilg.Emit(OpCodes.Shl);
284 case ExpressionType.RightShift:
285 if (rightType != typeof(int)) {
286 throw ContractUtils.Unreachable;
288 if (TypeUtils.IsUnsigned(leftType)) {
289 _ilg.Emit(OpCodes.Shr_Un);
291 _ilg.Emit(OpCodes.Shr);
295 throw Error.UnhandledBinary(op);
299 // Binary/unary operations on 8 and 16 bit operand types will leave a
300 // 32-bit value on the stack, because that's how IL works. For these
301 // cases, we need to cast it back to the resultType, possibly using a
302 // checked conversion if the original operator was convert
303 private void EmitConvertArithmeticResult(ExpressionType op, Type resultType) {
304 Debug.Assert(!resultType.IsNullableType());
306 switch (Type.GetTypeCode(resultType)) {
308 _ilg.Emit(IsChecked(op) ? OpCodes.Conv_Ovf_U1 : OpCodes.Conv_U1);
311 _ilg.Emit(IsChecked(op) ? OpCodes.Conv_Ovf_I1 : OpCodes.Conv_I1);
313 case TypeCode.UInt16:
314 _ilg.Emit(IsChecked(op) ? OpCodes.Conv_Ovf_U2 : OpCodes.Conv_U2);
317 _ilg.Emit(IsChecked(op) ? OpCodes.Conv_Ovf_I2 : OpCodes.Conv_I2);
322 private void EmitUnliftedEquality(ExpressionType op, Type type) {
323 Debug.Assert(op == ExpressionType.Equal || op == ExpressionType.NotEqual);
324 if (!type.IsPrimitive && type.IsValueType && !type.IsEnum) {
325 throw Error.OperatorNotImplementedForType(op, type);
327 _ilg.Emit(OpCodes.Ceq);
328 if (op == ExpressionType.NotEqual) {
329 _ilg.Emit(OpCodes.Ldc_I4_0);
330 _ilg.Emit(OpCodes.Ceq);
335 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
336 private void EmitLiftedBinaryOp(ExpressionType op, Type leftType, Type rightType, Type resultType, bool liftedToNull) {
337 Debug.Assert(TypeUtils.IsNullableType(leftType) || TypeUtils.IsNullableType(rightType));
339 case ExpressionType.And:
340 if (leftType == typeof(bool?)) {
341 EmitLiftedBooleanAnd();
343 EmitLiftedBinaryArithmetic(op, leftType, rightType, resultType);
346 case ExpressionType.Or:
347 if (leftType == typeof(bool?)) {
348 EmitLiftedBooleanOr();
350 EmitLiftedBinaryArithmetic(op, leftType, rightType, resultType);
353 case ExpressionType.ExclusiveOr:
354 case ExpressionType.Add:
355 case ExpressionType.AddChecked:
356 case ExpressionType.Subtract:
357 case ExpressionType.SubtractChecked:
358 case ExpressionType.Multiply:
359 case ExpressionType.MultiplyChecked:
360 case ExpressionType.Divide:
361 case ExpressionType.Modulo:
362 case ExpressionType.LeftShift:
363 case ExpressionType.RightShift:
364 EmitLiftedBinaryArithmetic(op, leftType, rightType, resultType);
366 case ExpressionType.LessThan:
367 case ExpressionType.LessThanOrEqual:
368 case ExpressionType.GreaterThan:
369 case ExpressionType.GreaterThanOrEqual:
370 case ExpressionType.Equal:
371 case ExpressionType.NotEqual:
372 EmitLiftedRelational(op, leftType, rightType, resultType, liftedToNull);
374 case ExpressionType.AndAlso:
375 case ExpressionType.OrElse:
377 throw ContractUtils.Unreachable;
382 private void EmitLiftedRelational(ExpressionType op, Type leftType, Type rightType, Type resultType, bool liftedToNull) {
383 Debug.Assert(TypeUtils.IsNullableType(leftType));
385 Label shortCircuit = _ilg.DefineLabel();
386 LocalBuilder locLeft = GetLocal(leftType);
387 LocalBuilder locRight = GetLocal(rightType);
389 // store values (reverse order since they are already on the stack)
390 _ilg.Emit(OpCodes.Stloc, locRight);
391 _ilg.Emit(OpCodes.Stloc, locLeft);
393 if (op == ExpressionType.Equal) {
394 // test for both null -> true
395 _ilg.Emit(OpCodes.Ldloca, locLeft);
396 _ilg.EmitHasValue(leftType);
397 _ilg.Emit(OpCodes.Ldc_I4_0);
398 _ilg.Emit(OpCodes.Ceq);
399 _ilg.Emit(OpCodes.Ldloca, locRight);
400 _ilg.EmitHasValue(rightType);
401 _ilg.Emit(OpCodes.Ldc_I4_0);
402 _ilg.Emit(OpCodes.Ceq);
403 _ilg.Emit(OpCodes.And);
404 _ilg.Emit(OpCodes.Dup);
405 _ilg.Emit(OpCodes.Brtrue_S, shortCircuit);
406 _ilg.Emit(OpCodes.Pop);
408 // test for either is null -> false
409 _ilg.Emit(OpCodes.Ldloca, locLeft);
410 _ilg.EmitHasValue(leftType);
411 _ilg.Emit(OpCodes.Ldloca, locRight);
412 _ilg.EmitHasValue(rightType);
413 _ilg.Emit(OpCodes.And);
415 _ilg.Emit(OpCodes.Dup);
416 _ilg.Emit(OpCodes.Brfalse_S, shortCircuit);
417 _ilg.Emit(OpCodes.Pop);
418 } else if (op == ExpressionType.NotEqual) {
419 // test for both null -> false
420 _ilg.Emit(OpCodes.Ldloca, locLeft);
421 _ilg.EmitHasValue(leftType);
422 _ilg.Emit(OpCodes.Ldloca, locRight);
423 _ilg.EmitHasValue(rightType);
424 _ilg.Emit(OpCodes.Or);
425 _ilg.Emit(OpCodes.Dup);
426 _ilg.Emit(OpCodes.Brfalse_S, shortCircuit);
427 _ilg.Emit(OpCodes.Pop);
429 // test for either is null -> true
430 _ilg.Emit(OpCodes.Ldloca, locLeft);
431 _ilg.EmitHasValue(leftType);
432 _ilg.Emit(OpCodes.Ldc_I4_0);
433 _ilg.Emit(OpCodes.Ceq);
434 _ilg.Emit(OpCodes.Ldloca, locRight);
435 _ilg.EmitHasValue(rightType);
436 _ilg.Emit(OpCodes.Ldc_I4_0);
437 _ilg.Emit(OpCodes.Ceq);
438 _ilg.Emit(OpCodes.Or);
439 _ilg.Emit(OpCodes.Dup);
440 _ilg.Emit(OpCodes.Brtrue_S, shortCircuit);
441 _ilg.Emit(OpCodes.Pop);
443 // test for either is null -> false
444 _ilg.Emit(OpCodes.Ldloca, locLeft);
445 _ilg.EmitHasValue(leftType);
446 _ilg.Emit(OpCodes.Ldloca, locRight);
447 _ilg.EmitHasValue(rightType);
448 _ilg.Emit(OpCodes.And);
449 _ilg.Emit(OpCodes.Dup);
450 _ilg.Emit(OpCodes.Brfalse_S, shortCircuit);
451 _ilg.Emit(OpCodes.Pop);
455 _ilg.Emit(OpCodes.Ldloca, locLeft);
456 _ilg.EmitGetValueOrDefault(leftType);
457 _ilg.Emit(OpCodes.Ldloca, locRight);
458 _ilg.EmitGetValueOrDefault(rightType);
460 //RELEASING locLeft locRight
466 TypeUtils.GetNonNullableType(leftType),
467 TypeUtils.GetNonNullableType(rightType),
468 TypeUtils.GetNonNullableType(resultType),
473 _ilg.MarkLabel(shortCircuit);
476 if (!TypeUtils.AreEquivalent(resultType, TypeUtils.GetNonNullableType(resultType))) {
477 _ilg.EmitConvertToType(TypeUtils.GetNonNullableType(resultType), resultType, true);
481 Label labEnd = _ilg.DefineLabel();
482 _ilg.Emit(OpCodes.Br, labEnd);
483 _ilg.MarkLabel(shortCircuit);
484 _ilg.Emit(OpCodes.Pop);
485 _ilg.Emit(OpCodes.Ldnull);
486 _ilg.Emit(OpCodes.Unbox_Any, resultType);
487 _ilg.MarkLabel(labEnd);
492 private void EmitLiftedBinaryArithmetic(ExpressionType op, Type leftType, Type rightType, Type resultType) {
493 bool leftIsNullable = TypeUtils.IsNullableType(leftType);
494 bool rightIsNullable = TypeUtils.IsNullableType(rightType);
496 Debug.Assert(leftIsNullable || rightIsNullable);
498 Label labIfNull = _ilg.DefineLabel();
499 Label labEnd = _ilg.DefineLabel();
500 LocalBuilder locLeft = GetLocal(leftType);
501 LocalBuilder locRight = GetLocal(rightType);
502 LocalBuilder locResult = GetLocal(resultType);
504 // store values (reverse order since they are already on the stack)
505 _ilg.Emit(OpCodes.Stloc, locRight);
506 _ilg.Emit(OpCodes.Stloc, locLeft);
509 // use short circuiting
510 if (leftIsNullable) {
511 _ilg.Emit(OpCodes.Ldloca, locLeft);
512 _ilg.EmitHasValue(leftType);
513 _ilg.Emit(OpCodes.Brfalse_S, labIfNull);
515 if (rightIsNullable) {
516 _ilg.Emit(OpCodes.Ldloca, locRight);
517 _ilg.EmitHasValue(rightType);
518 _ilg.Emit(OpCodes.Brfalse_S, labIfNull);
522 if (leftIsNullable) {
523 _ilg.Emit(OpCodes.Ldloca, locLeft);
524 _ilg.EmitGetValueOrDefault(leftType);
526 _ilg.Emit(OpCodes.Ldloc, locLeft);
529 if (rightIsNullable) {
530 _ilg.Emit(OpCodes.Ldloca, locRight);
531 _ilg.EmitGetValueOrDefault(rightType);
533 _ilg.Emit(OpCodes.Ldloc, locRight);
536 //RELEASING locLeft locRight
540 EmitBinaryOperator(op, TypeUtils.GetNonNullableType(leftType), TypeUtils.GetNonNullableType(rightType), TypeUtils.GetNonNullableType(resultType), false);
542 // construct result type
543 ConstructorInfo ci = resultType.GetConstructor(new Type[] { TypeUtils.GetNonNullableType(resultType) });
544 _ilg.Emit(OpCodes.Newobj, ci);
545 _ilg.Emit(OpCodes.Stloc, locResult);
546 _ilg.Emit(OpCodes.Br_S, labEnd);
548 // if null then create a default one
549 _ilg.MarkLabel(labIfNull);
550 _ilg.Emit(OpCodes.Ldloca, locResult);
551 _ilg.Emit(OpCodes.Initobj, resultType);
553 _ilg.MarkLabel(labEnd);
555 _ilg.Emit(OpCodes.Ldloc, locResult);
557 //RELEASING locResult
558 FreeLocal(locResult);
562 private void EmitLiftedBooleanAnd() {
563 Type type = typeof(bool?);
564 Label labComputeRight = _ilg.DefineLabel();
565 Label labReturnFalse = _ilg.DefineLabel();
566 Label labReturnNull = _ilg.DefineLabel();
567 Label labReturnValue = _ilg.DefineLabel();
568 Label labExit = _ilg.DefineLabel();
570 // store values (reverse order since they are already on the stack)
571 LocalBuilder locLeft = GetLocal(type);
572 LocalBuilder locRight = GetLocal(type);
573 _ilg.Emit(OpCodes.Stloc, locRight);
574 _ilg.Emit(OpCodes.Stloc, locLeft);
577 _ilg.Emit(OpCodes.Ldloca, locLeft);
578 _ilg.EmitHasValue(type);
579 _ilg.Emit(OpCodes.Brfalse, labComputeRight);
580 _ilg.Emit(OpCodes.Ldloca, locLeft);
581 _ilg.EmitGetValueOrDefault(type);
582 _ilg.Emit(OpCodes.Ldc_I4_0);
583 _ilg.Emit(OpCodes.Ceq);
584 _ilg.Emit(OpCodes.Brtrue, labReturnFalse);
587 _ilg.MarkLabel(labComputeRight);
588 _ilg.Emit(OpCodes.Ldloca, locRight);
589 _ilg.EmitHasValue(type);
590 _ilg.Emit(OpCodes.Brfalse_S, labReturnNull);
591 _ilg.Emit(OpCodes.Ldloca, locRight);
596 _ilg.EmitGetValueOrDefault(type);
597 _ilg.Emit(OpCodes.Ldc_I4_0);
598 _ilg.Emit(OpCodes.Ceq);
599 _ilg.Emit(OpCodes.Brtrue_S, labReturnFalse);
601 // check left for null again
602 _ilg.Emit(OpCodes.Ldloca, locLeft);
603 _ilg.EmitHasValue(type);
604 _ilg.Emit(OpCodes.Brfalse, labReturnNull);
607 _ilg.Emit(OpCodes.Ldc_I4_1);
608 _ilg.Emit(OpCodes.Br_S, labReturnValue);
611 _ilg.MarkLabel(labReturnFalse);
612 _ilg.Emit(OpCodes.Ldc_I4_0);
613 _ilg.Emit(OpCodes.Br_S, labReturnValue);
615 _ilg.MarkLabel(labReturnValue);
616 ConstructorInfo ci = type.GetConstructor(new Type[] { typeof(bool) });
617 _ilg.Emit(OpCodes.Newobj, ci);
618 _ilg.Emit(OpCodes.Stloc, locLeft);
619 _ilg.Emit(OpCodes.Br, labExit);
622 _ilg.MarkLabel(labReturnNull);
623 _ilg.Emit(OpCodes.Ldloca, locLeft);
624 _ilg.Emit(OpCodes.Initobj, type);
626 _ilg.MarkLabel(labExit);
627 _ilg.Emit(OpCodes.Ldloc, locLeft);
634 private void EmitLiftedBooleanOr() {
635 Type type = typeof(bool?);
636 Label labComputeRight = _ilg.DefineLabel();
637 Label labReturnTrue = _ilg.DefineLabel();
638 Label labReturnNull = _ilg.DefineLabel();
639 Label labReturnValue = _ilg.DefineLabel();
640 Label labExit = _ilg.DefineLabel();
642 // store values (reverse order since they are already on the stack)
643 LocalBuilder locLeft = GetLocal(type);
644 LocalBuilder locRight = GetLocal(type);
645 _ilg.Emit(OpCodes.Stloc, locRight);
646 _ilg.Emit(OpCodes.Stloc, locLeft);
649 _ilg.Emit(OpCodes.Ldloca, locLeft);
650 _ilg.EmitHasValue(type);
651 _ilg.Emit(OpCodes.Brfalse, labComputeRight);
652 _ilg.Emit(OpCodes.Ldloca, locLeft);
653 _ilg.EmitGetValueOrDefault(type);
654 _ilg.Emit(OpCodes.Ldc_I4_0);
655 _ilg.Emit(OpCodes.Ceq);
656 _ilg.Emit(OpCodes.Brfalse, labReturnTrue);
659 _ilg.MarkLabel(labComputeRight);
660 _ilg.Emit(OpCodes.Ldloca, locRight);
661 _ilg.EmitHasValue(type);
662 _ilg.Emit(OpCodes.Brfalse_S, labReturnNull);
663 _ilg.Emit(OpCodes.Ldloca, locRight);
668 _ilg.EmitGetValueOrDefault(type);
669 _ilg.Emit(OpCodes.Ldc_I4_0);
670 _ilg.Emit(OpCodes.Ceq);
671 _ilg.Emit(OpCodes.Brfalse_S, labReturnTrue);
673 // check left for null again
674 _ilg.Emit(OpCodes.Ldloca, locLeft);
675 _ilg.EmitHasValue(type);
676 _ilg.Emit(OpCodes.Brfalse, labReturnNull);
679 _ilg.Emit(OpCodes.Ldc_I4_0);
680 _ilg.Emit(OpCodes.Br_S, labReturnValue);
683 _ilg.MarkLabel(labReturnTrue);
684 _ilg.Emit(OpCodes.Ldc_I4_1);
685 _ilg.Emit(OpCodes.Br_S, labReturnValue);
687 _ilg.MarkLabel(labReturnValue);
688 ConstructorInfo ci = type.GetConstructor(new Type[] { typeof(bool) });
689 _ilg.Emit(OpCodes.Newobj, ci);
690 _ilg.Emit(OpCodes.Stloc, locLeft);
691 _ilg.Emit(OpCodes.Br, labExit);
694 _ilg.MarkLabel(labReturnNull);
695 _ilg.Emit(OpCodes.Ldloca, locLeft);
696 _ilg.Emit(OpCodes.Initobj, type);
698 _ilg.MarkLabel(labExit);
699 _ilg.Emit(OpCodes.Ldloc, locLeft);