void EmitEquality (EmitContext ec)
{
//
// Either left or right is null
//
if (UnwrapLeft != null && Binary.Right.IsNull) { // TODO: Optimize for EmitBranchable
//
// left.HasValue == false
//
UnwrapLeft.EmitCheck (ec);
if (Binary.Oper == Binary.Operator.Equality) {
ec.EmitInt (0);
ec.Emit (OpCodes.Ceq);
}
return;
}
if (UnwrapRight != null && Binary.Left.IsNull) {
//
// right.HasValue == false
//
UnwrapRight.EmitCheck (ec);
if (Binary.Oper == Binary.Operator.Equality) {
ec.EmitInt (0);
ec.Emit (OpCodes.Ceq);
}
return;
}
Label dissimilar_label = ec.DefineLabel ();
Label end_label = ec.DefineLabel ();
if (UserOperator != null) {
var left = Left;
if (UnwrapLeft != null) {
UnwrapLeft.EmitCheck (ec);
} else {
// Keep evaluation order same
if (!(Left is VariableReference)) {
Left.Emit (ec);
var lt = new LocalTemporary (Left.Type);
lt.Store (ec);
left = lt;
}
}
if (UnwrapRight != null) {
UnwrapRight.EmitCheck (ec);
if (UnwrapLeft != null) {
ec.Emit (OpCodes.Bne_Un, dissimilar_label);
Label compare_label = ec.DefineLabel ();
UnwrapLeft.EmitCheck (ec);
ec.Emit (OpCodes.Brtrue, compare_label);
if (Binary.Oper == Binary.Operator.Equality)
ec.EmitInt (1);
else
ec.EmitInt (0);
ec.Emit (OpCodes.Br, end_label);
ec.MarkLabel (compare_label);
} else {
ec.Emit (OpCodes.Brfalse, dissimilar_label);
}
} else {
ec.Emit (OpCodes.Brfalse, dissimilar_label);
}
var args = new Arguments (2);
args.Add (new Argument (left));
args.Add (new Argument (Right));
var call = new CallEmitter ();
call.EmitPredefined (ec, UserOperator, args);
} else {
if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) {
Left = Left.EmitToField (ec);
Right = Right.EmitToField (ec);
}
//
// Emit underlying value comparison first.
//
// For this code: int? a = 1; bool b = a == 1;
//
// We emit something similar to this. Expressions with side effects have local
// variable created by Unwrap expression
//
// left.GetValueOrDefault ()
// right
// bne.un.s dissimilar_label
// left.HasValue
// br.s end_label
// dissimilar_label:
// ldc.i4.0
// end_label:
//
Left.Emit (ec);
Right.Emit (ec);
ec.Emit (OpCodes.Bne_Un_S, dissimilar_label);
//
// Check both left and right expressions for Unwrap call in which
// case we need to run get_HasValue() check because the type is
// nullable and could have null value
//
if (UnwrapLeft != null)
UnwrapLeft.EmitCheck (ec);
if (UnwrapRight != null)
UnwrapRight.EmitCheck (ec);
if (UnwrapLeft != null && UnwrapRight != null) {
if (Binary.Oper == Binary.Operator.Inequality)
ec.Emit (OpCodes.Xor);
else
ec.Emit (OpCodes.Ceq);
} else {
if (Binary.Oper == Binary.Operator.Inequality) {
ec.EmitInt (0);
ec.Emit (OpCodes.Ceq);
}
}
}
ec.Emit (OpCodes.Br_S, end_label);
ec.MarkLabel (dissimilar_label);
if (Binary.Oper == Binary.Operator.Inequality)
ec.EmitInt (1);
else
ec.EmitInt (0);
ec.MarkLabel (end_label);
}