2 // nullable.cs: Nullable types support
4 // Authors: Martin Baulig (martin@ximian.com)
5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@gmail.com)
8 // Licensed under the terms of the GNU GPL
10 // (C) 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
11 // (C) 2004 Novell, Inc
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Collections;
19 namespace Mono.CSharp.Nullable
21 public class NullableType : TypeExpr
23 Expression underlying;
25 public NullableType (Expression underlying, Location l)
27 this.underlying = underlying;
30 eclass = ExprClass.Type;
33 public NullableType (Type type, Location loc)
34 : this (new TypeExpression (type, loc), loc)
37 public override string Name {
38 get { return underlying.ToString () + "?"; }
41 public override string FullName {
42 get { return underlying.ToString () + "?"; }
45 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
47 TypeArguments args = new TypeArguments (loc);
48 args.Add (underlying);
50 if (TypeManager.generic_nullable_type == null) {
51 TypeManager.generic_nullable_type = TypeManager.CoreLookupType (
52 "System", "Nullable`1", Kind.Struct, true);
55 ConstructedType ctype = new ConstructedType (TypeManager.generic_nullable_type, args, loc);
56 return ctype.ResolveAsTypeTerminal (ec, false);
60 public sealed class NullableInfo
62 public readonly Type Type;
63 public readonly Type UnderlyingType;
64 public readonly MethodInfo HasValue;
65 public readonly MethodInfo Value;
66 public readonly MethodInfo GetValueOrDefault;
67 public readonly ConstructorInfo Constructor;
69 public NullableInfo (Type type)
72 UnderlyingType = TypeManager.GetTypeArguments (type) [0];
74 PropertyInfo has_value_pi = TypeManager.GetPredefinedProperty (type, "HasValue", Location.Null);
75 PropertyInfo value_pi = TypeManager.GetPredefinedProperty (type, "Value", Location.Null);
76 GetValueOrDefault = TypeManager.GetPredefinedMethod (type, "GetValueOrDefault", Location.Null, Type.EmptyTypes);
78 HasValue = has_value_pi.GetGetMethod (false);
79 Value = value_pi.GetGetMethod (false);
81 if (UnderlyingType.Module == CodeGen.Module.Builder) {
82 Type o_type = TypeManager.DropGenericTypeArguments (type);
83 Constructor = TypeBuilder.GetConstructor (type,
84 TypeManager.GetPredefinedConstructor (o_type, Location.Null, o_type.GetGenericArguments ()));
88 Constructor = type.GetConstructor (new Type[] { UnderlyingType });
92 public class HasValue : Expression
97 private HasValue (Expression expr)
102 public static Expression Create (Expression expr, EmitContext ec)
104 return new HasValue (expr).Resolve (ec);
107 public override void Emit (EmitContext ec)
109 IMemoryLocation memory_loc = expr as IMemoryLocation;
110 if (memory_loc == null) {
111 LocalTemporary temp = new LocalTemporary (expr.Type);
116 memory_loc.AddressOf (ec, AddressOp.LoadStore);
117 ec.ig.EmitCall (OpCodes.Call, info.HasValue, null);
120 public override Expression DoResolve (EmitContext ec)
122 this.info = new NullableInfo (expr.Type);
124 type = TypeManager.bool_type;
125 eclass = expr.eclass;
130 public class Unwrap : Expression, IMemoryLocation, IAssignMethod
138 protected Unwrap (Expression expr)
141 this.loc = expr.Location;
144 public static Unwrap Create (Expression expr, EmitContext ec)
146 return new Unwrap (expr).Resolve (ec) as Unwrap;
149 public override Expression CreateExpressionTree (EmitContext ec)
151 return expr.CreateExpressionTree (ec);
154 public override Expression DoResolve (EmitContext ec)
159 temp = new LocalTemporary (expr.Type);
161 info = new NullableInfo (expr.Type);
162 type = info.UnderlyingType;
163 eclass = expr.eclass;
167 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
169 return DoResolve (ec);
172 public override void Emit (EmitContext ec)
174 AddressOf (ec, AddressOp.LoadStore);
175 ec.ig.EmitCall (OpCodes.Call, info.Value, null);
178 public void EmitCheck (EmitContext ec)
180 AddressOf (ec, AddressOp.LoadStore);
181 ec.ig.EmitCall (OpCodes.Call, info.HasValue, null);
184 public void EmitGetValueOrDefault (EmitContext ec)
186 AddressOf (ec, AddressOp.LoadStore);
187 ec.ig.EmitCall (OpCodes.Call, info.GetValueOrDefault, null);
190 public override bool Equals (object obj)
192 Unwrap uw = obj as Unwrap;
193 return uw != null && expr.Equals (uw.expr);
196 public Expression Original {
202 public override int GetHashCode ()
204 return expr.GetHashCode ();
207 public override bool IsNull {
213 public void Store (EmitContext ec)
218 void create_temp (EmitContext ec)
220 if ((temp != null) && !has_temp) {
227 public void LoadTemporary (EmitContext ec)
232 public void AddressOf (EmitContext ec, AddressOp mode)
236 temp.AddressOf (ec, AddressOp.LoadStore);
238 ((IMemoryLocation) expr).AddressOf (ec, AddressOp.LoadStore);
241 public void Emit (EmitContext ec, bool leave_copy)
254 public void EmitAssign (EmitContext ec, Expression source,
255 bool leave_copy, bool prepare_for_load)
257 InternalWrap wrap = new InternalWrap (source, info, loc);
258 ((IAssignMethod) expr).EmitAssign (ec, wrap, leave_copy, false);
261 protected class InternalWrap : Expression
263 public Expression expr;
264 public NullableInfo info;
266 public InternalWrap (Expression expr, NullableInfo info, Location loc)
273 eclass = ExprClass.Value;
276 public override Expression DoResolve (EmitContext ec)
281 public override void Emit (EmitContext ec)
284 ec.ig.Emit (OpCodes.Newobj, info.Constructor);
289 public class Wrap : EmptyCast
291 readonly NullableInfo info;
293 protected Wrap (Expression expr, Type type)
296 info = new NullableInfo (type);
297 eclass = ExprClass.Value;
300 public static new Expression Create (Expression expr, Type type)
302 return new Wrap (expr, type);
305 public override void Emit (EmitContext ec)
308 ec.ig.Emit (OpCodes.Newobj, info.Constructor);
312 class LiftedWrap : Wrap
314 public LiftedWrap (Expression expr, Type type)
319 public override Expression CreateExpressionTree (EmitContext ec)
321 return child.CreateExpressionTree (ec);
326 // Represents null literal lifted to nullable type
328 public class LiftedNull : EmptyConstantCast, IMemoryLocation
330 private LiftedNull (Type nullable_type, Location loc)
331 : base (new NullLiteral (loc), nullable_type)
333 eclass = ExprClass.Value;
336 public static Constant Create (Type nullable, Location loc)
338 return new LiftedNull (nullable, loc);
341 public override Expression CreateExpressionTree (EmitContext ec)
343 ArrayList args = new ArrayList (2);
344 args.Add (new Argument (this));
345 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
347 return CreateExpressionFactoryCall ("Constant", args);
350 public override void Emit (EmitContext ec)
352 // TODO: generate less temporary variables
353 LocalTemporary value_target = new LocalTemporary (type);
355 value_target.AddressOf (ec, AddressOp.Store);
356 ec.ig.Emit (OpCodes.Initobj, type);
357 value_target.Emit (ec);
360 public void AddressOf (EmitContext ec, AddressOp Mode)
362 LocalTemporary value_target = new LocalTemporary (type);
364 value_target.AddressOf (ec, AddressOp.Store);
365 ec.ig.Emit (OpCodes.Initobj, type);
366 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
370 public abstract class Lifted : Expression, IMemoryLocation
372 Expression expr, underlying, wrap, null_value;
375 protected Lifted (Expression expr, Location loc)
381 public override Expression CreateExpressionTree (EmitContext ec)
383 return underlying.CreateExpressionTree (ec);
386 public override Expression DoResolve (EmitContext ec)
388 expr = expr.Resolve (ec);
392 unwrap = Unwrap.Create (expr, ec);
396 underlying = ResolveUnderlying (unwrap, ec);
397 if (underlying == null)
400 TypeExpr target_type = new NullableType (underlying.Type, loc);
401 target_type = target_type.ResolveAsTypeTerminal (ec, false);
402 if (target_type == null)
405 wrap = Wrap.Create (underlying, target_type.Type);
409 null_value = LiftedNull.Create (wrap.Type, loc);
412 eclass = ExprClass.Value;
416 protected abstract Expression ResolveUnderlying (Expression unwrap, EmitContext ec);
418 public override void Emit (EmitContext ec)
420 ILGenerator ig = ec.ig;
421 Label is_null_label = ig.DefineLabel ();
422 Label end_label = ig.DefineLabel ();
424 unwrap.EmitCheck (ec);
425 ig.Emit (OpCodes.Brfalse, is_null_label);
428 ig.Emit (OpCodes.Br, end_label);
430 ig.MarkLabel (is_null_label);
431 null_value.Emit (ec);
433 ig.MarkLabel (end_label);
436 public void AddressOf (EmitContext ec, AddressOp mode)
438 unwrap.AddressOf (ec, mode);
442 public class LiftedConversion : Lifted
444 public readonly bool IsUser;
445 public readonly bool IsExplicit;
446 public readonly Type TargetType;
448 public LiftedConversion (Expression expr, Type target_type, bool is_user,
449 bool is_explicit, Location loc)
452 this.IsUser = is_user;
453 this.IsExplicit = is_explicit;
454 this.TargetType = target_type;
457 protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec)
459 Type type = TypeManager.GetTypeArguments (TargetType) [0];
462 return Convert.UserDefinedConversion (ec, unwrap, type, loc, IsExplicit);
465 return Convert.ExplicitConversion (ec, unwrap, type, loc);
467 return Convert.ImplicitConversion (ec, unwrap, type, loc);
472 public class LiftedUnaryOperator : Lifted
474 public readonly Unary.Operator Oper;
476 public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc)
482 protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec)
484 return new Unary (Oper, unwrap, loc).Resolve (ec);
488 public class LiftedBinaryOperator : Binary
490 Unwrap left_unwrap, right_unwrap;
491 bool left_null_lifted, right_null_lifted;
492 Expression left_orig, right_orig;
494 public LiftedBinaryOperator (Binary.Operator op, Expression left, Expression right,
496 : base (op, left, right)
502 // CSC 2 has this behavior, it allows structs to be compared
503 // with the null literal *outside* of a generics context and
504 // inlines that as true or false.
506 Expression CreateNullConstant (Expression expr)
508 // FIXME: Handle side effect constants
509 Constant c = new BoolConstant (Oper == Operator.Inequality, loc);
511 if ((Oper & Operator.EqualityMask) != 0) {
512 Report.Warning (472, 2, loc, "The result of comparing `{0}' against null is always `{1}'. " +
513 "This operation is undocumented and it is temporary supported for compatibility reasons only",
514 expr.GetSignatureForError (), c.AsString ());
516 Report.Warning (464, 2, loc, "The result of comparing type `{0}' against null is always `{1}'",
517 expr.GetSignatureForError (), c.AsString ());
520 return ReducedExpression.Create (c, this);
523 public override Expression DoResolve (EmitContext ec)
525 if (eclass != ExprClass.Invalid)
528 // TODO: How does it work with use-operators?
529 if ((Oper == Binary.Operator.LogicalAnd) ||
530 (Oper == Binary.Operator.LogicalOr)) {
531 Error_OperatorCannotBeApplied (left, right);
536 if (TypeManager.IsNullableType (left.Type)) {
537 left = left_unwrap = Unwrap.Create (left, ec);
543 if (TypeManager.IsNullableType (right.Type)) {
544 right = right_unwrap = Unwrap.Create (right, ec);
549 if (left is NullLiteral) {
551 left_null_lifted = true;
554 if (right is NullLiteral) {
556 right_null_lifted = true;
559 eclass = ExprClass.Value;
560 Expression expr = DoResolveCore (ec, left_orig, right_orig);
561 if (expr != this || (Oper & Operator.ComparisonMask) != 0)
564 TypeExpr target_type = new NullableType (type, loc);
565 target_type = target_type.ResolveAsTypeTerminal (ec, false);
566 if (target_type == null)
569 type = target_type.Type;
573 void EmitBitwiseBoolean (EmitContext ec)
575 ILGenerator ig = ec.ig;
577 Label load_left = ig.DefineLabel ();
578 Label load_right = ig.DefineLabel ();
579 Label end_label = ig.DefineLabel ();
581 left_unwrap.EmitGetValueOrDefault (ec);
582 ig.Emit (OpCodes.Brtrue_S, load_right);
584 right_unwrap.EmitGetValueOrDefault (ec);
585 ig.Emit (OpCodes.Brtrue_S, load_left);
587 left_unwrap.EmitCheck (ec);
588 ig.Emit (OpCodes.Brfalse_S, load_right);
591 ig.MarkLabel (load_left);
593 if (Oper == Operator.BitwiseAnd) {
594 left_unwrap.LoadTemporary (ec);
596 right_unwrap.LoadTemporary (ec);
597 right_unwrap = left_unwrap;
599 ig.Emit (OpCodes.Br_S, end_label);
602 ig.MarkLabel (load_right);
603 right_unwrap.LoadTemporary (ec);
605 ig.MarkLabel (end_label);
608 void EmitEquality (EmitContext ec)
610 ILGenerator ig = ec.ig;
612 Label both_have_value_label = ig.DefineLabel ();
613 Label end_label = ig.DefineLabel ();
616 // Both are nullable types
618 if (left_unwrap != null && right_unwrap != null && !right.IsNull && !left.IsNull) {
619 Label dissimilar_label = ig.DefineLabel ();
621 left_unwrap.EmitGetValueOrDefault (ec);
622 right_unwrap.EmitGetValueOrDefault (ec);
623 ig.Emit (OpCodes.Bne_Un_S, dissimilar_label);
625 left_unwrap.EmitCheck (ec);
626 right_unwrap.EmitCheck (ec);
627 if (Oper == Operator.Inequality)
628 ig.Emit (OpCodes.Xor);
630 ig.Emit (OpCodes.Ceq);
632 ig.Emit (OpCodes.Br_S, end_label);
634 ig.MarkLabel (dissimilar_label);
635 if (Oper == Operator.Inequality)
636 ig.Emit (OpCodes.Ldc_I4_1);
638 ig.Emit (OpCodes.Ldc_I4_0);
640 ig.MarkLabel (end_label);
645 // Either left or right is nullable
647 if (left_unwrap != null) {
648 left_unwrap.EmitCheck (ec);
650 if (Oper == Binary.Operator.Equality) {
651 ig.Emit (OpCodes.Ldc_I4_0);
652 ig.Emit (OpCodes.Ceq);
656 ig.Emit (OpCodes.Brtrue_S, both_have_value_label);
658 right_unwrap.EmitCheck (ec);
660 if (Oper == Binary.Operator.Equality) {
661 ig.Emit (OpCodes.Ldc_I4_0);
662 ig.Emit (OpCodes.Ceq);
666 ig.Emit (OpCodes.Brtrue_S, both_have_value_label);
669 if (Oper == Binary.Operator.Equality)
670 ig.Emit (OpCodes.Ldc_I4_0);
672 ig.Emit (OpCodes.Ldc_I4_1);
673 ig.Emit (OpCodes.Br, end_label);
675 ig.MarkLabel (both_have_value_label);
678 ig.MarkLabel (end_label);
681 void EmitComparision (EmitContext ec)
683 ILGenerator ig = ec.ig;
685 Label is_null_label = ig.DefineLabel ();
686 Label end_label = ig.DefineLabel ();
688 if (left_unwrap != null) {
689 left_unwrap.EmitCheck (ec);
690 ig.Emit (OpCodes.Brfalse, is_null_label);
693 if (right_unwrap != null) {
694 right_unwrap.EmitCheck (ec);
695 ig.Emit (OpCodes.Brfalse, is_null_label);
699 ig.Emit (OpCodes.Br_S, end_label);
701 ig.MarkLabel (is_null_label);
702 ig.Emit (OpCodes.Ldc_I4_0);
704 ig.MarkLabel (end_label);
707 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
710 ec.ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
713 public override void Emit (EmitContext ec)
715 if (left_unwrap != null)
716 left_unwrap.Store (ec);
717 if (right_unwrap != null)
718 right_unwrap.Store (ec);
720 if (IsBitwiseBoolean) {
721 EmitBitwiseBoolean (ec);
725 if ((Oper & Operator.EqualityMask) != 0) {
730 if ((Oper & Operator.ComparisonMask) != 0) {
731 EmitComparision (ec);
735 ILGenerator ig = ec.ig;
737 Label is_null_label = ig.DefineLabel ();
738 Label end_label = ig.DefineLabel ();
740 if (left_unwrap != null) {
741 left_unwrap.EmitCheck (ec);
742 ig.Emit (OpCodes.Brfalse, is_null_label);
745 if (right_unwrap != null) {
746 right_unwrap.EmitCheck (ec);
747 ig.Emit (OpCodes.Brfalse, is_null_label);
750 base.EmitOperator (ec);
752 ig.Emit (OpCodes.Br_S, end_label);
753 ig.MarkLabel (is_null_label);
754 LiftedNull.Create (type, loc).Emit (ec);
756 ig.MarkLabel (end_label);
759 bool IsBitwiseBoolean {
761 return (Oper & Operator.BitwiseMask) != 0 && left_unwrap != null && right_unwrap != null &&
762 left_unwrap.Type == TypeManager.bool_type && right_unwrap.Type == TypeManager.bool_type;
766 Expression LiftResult (EmitContext ec, Expression res_expr)
768 TypeExpr lifted_type;
771 // Avoid double conversion
773 if (left_unwrap == null || left_null_lifted || !TypeManager.IsEqual (left_unwrap.Type, left.Type)) {
774 lifted_type = new NullableType (left.Type, loc);
775 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
776 if (lifted_type == null)
779 left = EmptyCast.Create (left, lifted_type.Type);
782 if (right_unwrap == null || right_null_lifted || !TypeManager.IsEqual (right_unwrap.Type, right.Type)) {
783 lifted_type = new NullableType (right.Type, loc);
784 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
785 if (lifted_type == null)
788 right = EmptyCast.Create (right, lifted_type.Type);
791 if (left_null_lifted) {
792 left = LiftedNull.Create (right.Type, left.Location);
795 // Value types and null comparison
797 if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0)
798 return CreateNullConstant (right_orig).Resolve (ec);
801 if (right_null_lifted) {
802 right = LiftedNull.Create (left.Type, right.Location);
805 // Value types and null comparison
807 if (left_unwrap == null || (Oper & Operator.RelationalMask) != 0)
808 return CreateNullConstant (left_orig).Resolve (ec);
811 // TODO: Handle bitwise bool
812 if ((Oper & Operator.ComparisonMask) != 0 || IsBitwiseBoolean)
815 lifted_type = new NullableType (res_expr.Type, loc);
816 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
817 if (lifted_type == null)
820 return new LiftedWrap (res_expr, lifted_type.Type).Resolve (ec);
823 protected override Expression ResolveOperatorPredefined (EmitContext ec, Binary.PredefinedOperator [] operators, bool primitives_only)
825 Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only);
828 return LiftResult (ec, e);
831 // 7.9.9 Equality operators and null
833 // The == and != operators permit one operand to be a value of a nullable type and
834 // the other to be the null literal, even if no predefined or user-defined operator
835 // (in unlifted or lifted form) exists for the operation.
837 if (e == null && (Oper & Operator.EqualityMask) != 0) {
838 if ((left_null_lifted && right_unwrap != null) || (right_null_lifted && left_unwrap != null))
839 return LiftResult (ec, this);
845 protected override Expression ResolveUserOperator (EmitContext ec, Type l, Type r)
847 Expression expr = base.ResolveUserOperator (ec, l, r);
851 return LiftResult (ec, expr);
855 public class NullCoalescingOperator : Expression
857 Expression left, right;
860 public NullCoalescingOperator (Expression left, Expression right, Location loc)
867 public override Expression CreateExpressionTree (EmitContext ec)
869 UserCast uc = left as UserCast;
870 Expression conversion = null;
874 ArrayList c_args = new ArrayList (2);
875 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
876 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
877 conversion = CreateExpressionFactoryCall ("Lambda", c_args);
880 ArrayList args = new ArrayList (3);
881 args.Add (new Argument (left.CreateExpressionTree (ec)));
882 args.Add (new Argument (right.CreateExpressionTree (ec)));
883 if (conversion != null)
884 args.Add (new Argument (conversion));
886 return CreateExpressionFactoryCall ("Coalesce", args);
889 public override Expression DoResolve (EmitContext ec)
894 left = left.Resolve (ec);
895 right = right.Resolve (ec);
897 if (left == null || right == null)
900 eclass = ExprClass.Value;
901 Type ltype = left.Type, rtype = right.Type;
904 if (TypeManager.IsNullableType (ltype)) {
905 NullableInfo info = new NullableInfo (ltype);
907 unwrap = Unwrap.Create (left, ec);
911 expr = Convert.ImplicitConversion (ec, right, info.UnderlyingType, loc);
918 } else if (!TypeManager.IsReferenceType (ltype)) {
919 Binary.Error_OperatorCannotBeApplied (loc, "??", ltype, rtype);
923 expr = Convert.ImplicitConversion (ec, right, ltype, loc);
930 Expression left_null = unwrap != null ? unwrap : left;
931 expr = Convert.ImplicitConversion (ec, left_null, rtype, loc);
938 Binary.Error_OperatorCannotBeApplied (loc, "??", ltype, rtype);
942 public override void Emit (EmitContext ec)
944 ILGenerator ig = ec.ig;
946 Label is_null_label = ig.DefineLabel ();
947 Label end_label = ig.DefineLabel ();
949 if (unwrap != null) {
950 unwrap.EmitCheck (ec);
951 ig.Emit (OpCodes.Brfalse, is_null_label);
954 ig.Emit (OpCodes.Br, end_label);
956 ig.MarkLabel (is_null_label);
959 ig.MarkLabel (end_label);
962 ig.Emit (OpCodes.Dup);
963 ig.Emit (OpCodes.Brtrue, end_label);
965 ig.MarkLabel (is_null_label);
967 ig.Emit (OpCodes.Pop);
970 ig.MarkLabel (end_label);
973 protected override void CloneTo (CloneContext clonectx, Expression t)
975 NullCoalescingOperator target = (NullCoalescingOperator) t;
977 target.left = left.Clone (clonectx);
978 target.right = right.Clone (clonectx);
982 public class LiftedUnaryMutator : ExpressionStatement
984 public readonly UnaryMutator.Mode Mode;
985 Expression expr, null_value;
986 UnaryMutator underlying;
989 public LiftedUnaryMutator (UnaryMutator.Mode mode, Expression expr, Location loc)
995 eclass = ExprClass.Value;
998 public override Expression DoResolve (EmitContext ec)
1000 expr = expr.Resolve (ec);
1004 unwrap = Unwrap.Create (expr, ec);
1008 underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap, loc).Resolve (ec);
1009 if (underlying == null)
1013 null_value = LiftedNull.Create (type, loc);
1017 void DoEmit (EmitContext ec, bool is_expr)
1019 ILGenerator ig = ec.ig;
1020 Label is_null_label = ig.DefineLabel ();
1021 Label end_label = ig.DefineLabel ();
1023 unwrap.EmitCheck (ec);
1024 ig.Emit (OpCodes.Brfalse, is_null_label);
1027 underlying.Emit (ec);
1029 underlying.EmitStatement (ec);
1030 ig.Emit (OpCodes.Br, end_label);
1032 ig.MarkLabel (is_null_label);
1034 null_value.Emit (ec);
1036 ig.MarkLabel (end_label);
1039 public override void Emit (EmitContext ec)
1044 public override void EmitStatement (EmitContext ec)