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);
}