public override void Emit (EmitContext ec)
{
Label end_label = ec.DefineLabel ();
if (unwrap != null) {
Label is_null_label = ec.DefineLabel ();
unwrap.EmitCheck (ec);
ec.Emit (OpCodes.Brfalse, is_null_label);
//
// When both expressions are nullable the unwrap
// is needed only for null check not for value uwrap
//
if (type.IsNullableType && TypeSpecComparer.IsEqual (NullableInfo.GetUnderlyingType (type), unwrap.Type))
unwrap.Load (ec);
else
left.Emit (ec);
ec.Emit (OpCodes.Br, end_label);
ec.MarkLabel (is_null_label);
right.Emit (ec);
ec.MarkLabel (end_label);
return;
}
//
// Null check is done on original expression not after expression is converted to
// result type. This is in most cases same but when user conversion is involved
// we can end up in situation when use operator does the null handling which is
// not what the operator is supposed to do
//
var op_expr = left as UserCast;
if (op_expr != null) {
op_expr.Source.Emit (ec);
LocalTemporary temp;
// TODO: More load kinds can be special cased
if (!(op_expr.Source is VariableReference)) {
temp = new LocalTemporary (op_expr.Source.Type);
temp.Store (ec);
temp.Emit (ec);
op_expr.Source = temp;
} else {
temp = null;
}
var right_label = ec.DefineLabel ();
ec.Emit (OpCodes.Brfalse_S, right_label);
left.Emit (ec);
ec.Emit (OpCodes.Br, end_label);
ec.MarkLabel (right_label);
if (temp != null)
temp.Release (ec);
} else {
//
// Common case where expression is not modified before null check and
// we generate better/smaller code
//
left.Emit (ec);
ec.Emit (OpCodes.Dup);
// Only to make verifier happy
if (left.Type.IsGenericParameter)
ec.Emit (OpCodes.Box, left.Type);
ec.Emit (OpCodes.Brtrue, end_label);
ec.Emit (OpCodes.Pop);
}
right.Emit (ec);
ec.MarkLabel (end_label);
}