public override void Emit(EmitContext ec) { Label is_null_label = ec.DefineLabel(); Label end_label = ec.DefineLabel(); unwrap.EmitCheck(ec); ec.Emit(OpCodes.Brfalse, is_null_label); if (user_operator != null) { user_operator.Emit(ec); } else { EmitOperator(ec, NullableInfo.GetUnderlyingType(type)); } ec.Emit(OpCodes.Newobj, NullableInfo.GetConstructor(type)); ec.Emit(OpCodes.Br_S, end_label); ec.MarkLabel(is_null_label); LiftedNull.Create(type, loc).Emit(ec); ec.MarkLabel(end_label); }
public override void Emit(EmitContext ec) { var call = new CallEmitter(); call.InstanceExpression = Child; call.EmitPredefined(ec, NullableInfo.GetValue(Child.Type), null); }
protected override void EmitOperation(EmitContext ec) { Label is_null_label = ec.DefineLabel(); Label end_label = ec.DefineLabel(); LocalTemporary lt = new LocalTemporary(type); // Value is on the stack lt.Store(ec); var call = new CallEmitter(); call.InstanceExpression = lt; call.EmitPredefined(ec, NullableInfo.GetHasValue(expr.Type), null); ec.Emit(OpCodes.Brfalse, is_null_label); call = new CallEmitter(); call.InstanceExpression = lt; call.EmitPredefined(ec, NullableInfo.GetGetValueOrDefault(expr.Type), null); lt.Release(ec); base.EmitOperation(ec); ec.Emit(OpCodes.Newobj, NullableInfo.GetConstructor(type)); ec.Emit(OpCodes.Br_S, end_label); ec.MarkLabel(is_null_label); LiftedNull.Create(type, loc).Emit(ec); ec.MarkLabel(end_label); }
public Unwrap(Expression expr, bool useDefaultValue = true) { this.expr = expr; this.loc = expr.Location; this.useDefaultValue = useDefaultValue; type = NullableInfo.GetUnderlyingType(expr.Type); eclass = expr.eclass; }
public void EmitCheck(EmitContext ec) { Store(ec); var call = new CallEmitter(); call.InstanceExpression = this; call.EmitPredefined(ec, NullableInfo.GetHasValue(expr.Type), null); }
protected override Expression DoResolve(ResolveContext rc) { base.DoResolve(rc); if (type != null) { type = NullableInfo.GetUnderlyingType(type); } return(this); }
public static Expression Create(Expression expr, TypeSpec type) { // // Avoid unwraping and wraping of the same type // if (expr is Unwrap unwrap && expr.Type == NullableInfo.GetUnderlyingType(type)) { return(unwrap.Original); } return(new Wrap(expr, type)); }
Expression LiftOperand(ResolveContext rc, Expression expr) { TypeSpec type; if (expr.IsNull) { type = Left.IsNull ? Right.Type : Left.Type; } else { type = expr.Type; } if (!type.IsNullableType) { type = NullableInfo.MakeType(rc.Module, type); } return(Wrap.Create(expr, type)); }
public override void Emit(EmitContext ec) { Store(ec); var call = new CallEmitter(); call.InstanceExpression = this; // // Using GetGetValueOrDefault is prefered because JIT can possibly // inline it whereas Value property contains a throw which is very // unlikely to be inlined // if (useDefaultValue) { call.EmitPredefined(ec, NullableInfo.GetGetValueOrDefault(expr.Type), null); } else { call.EmitPredefined(ec, NullableInfo.GetValue(expr.Type), null); } }
void EmitBitwiseBoolean(EmitContext ec) { Label load_left = ec.DefineLabel(); Label load_right = ec.DefineLabel(); Label end_label = ec.DefineLabel(); Label is_null_label = ec.DefineLabel(); bool or = Binary.Oper == Binary.Operator.BitwiseOr; // // Both operands are bool? types // if (UnwrapLeft != null && UnwrapRight != null) { if (ec.HasSet(BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait()) { Left = Left.EmitToField(ec); Right = Right.EmitToField(ec); } else { UnwrapLeft.Store(ec); UnwrapRight.Store(ec); } Left.Emit(ec); ec.Emit(OpCodes.Brtrue_S, load_right); Right.Emit(ec); ec.Emit(OpCodes.Brtrue_S, load_left); UnwrapLeft.EmitCheck(ec); ec.Emit(OpCodes.Brfalse_S, load_right); // load left ec.MarkLabel(load_left); if (or) { UnwrapRight.Load(ec); } else { UnwrapLeft.Load(ec); } ec.Emit(OpCodes.Br_S, end_label); // load right ec.MarkLabel(load_right); if (or) { UnwrapLeft.Load(ec); } else { UnwrapRight.Load(ec); } ec.MarkLabel(end_label); return; } // // Faster version when one operand is bool // if (UnwrapLeft == null) { // // (bool, bool?) // // Optimizes remaining (false & bool?), (true | bool?) which are not easy to handle // in binary expression reduction // var c = Left as BoolConstant; if (c != null) { // Keep evaluation order UnwrapRight.Store(ec); ec.EmitInt(or ? 1 : 0); ec.Emit(OpCodes.Newobj, NullableInfo.GetConstructor(type)); } else if (Left.IsNull) { UnwrapRight.Emit(ec); ec.Emit(or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label); UnwrapRight.Load(ec); ec.Emit(OpCodes.Br_S, end_label); ec.MarkLabel(is_null_label); LiftedNull.Create(type, loc).Emit(ec); } else { Left.Emit(ec); ec.Emit(or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right); ec.EmitInt(or ? 1 : 0); ec.Emit(OpCodes.Newobj, NullableInfo.GetConstructor(type)); ec.Emit(OpCodes.Br_S, end_label); ec.MarkLabel(load_right); UnwrapRight.Original.Emit(ec); } } else { // // (bool?, bool) // // Keep left-right evaluation order UnwrapLeft.Store(ec); // // Optimizes remaining (bool? & false), (bool? | true) which are not easy to handle // in binary expression reduction // var c = Right as BoolConstant; if (c != null) { ec.EmitInt(or ? 1 : 0); ec.Emit(OpCodes.Newobj, NullableInfo.GetConstructor(type)); } else if (Right.IsNull) { UnwrapLeft.Emit(ec); ec.Emit(or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label); UnwrapLeft.Load(ec); ec.Emit(OpCodes.Br_S, end_label); ec.MarkLabel(is_null_label); LiftedNull.Create(type, loc).Emit(ec); } else { Right.Emit(ec); ec.Emit(or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right); ec.EmitInt(or ? 1 : 0); ec.Emit(OpCodes.Newobj, NullableInfo.GetConstructor(type)); ec.Emit(OpCodes.Br_S, end_label); ec.MarkLabel(load_right); UnwrapLeft.Load(ec); } } ec.MarkLabel(end_label); }
public override void Emit(EmitContext ec) { if (IsBitwiseBoolean && UserOperator == null) { EmitBitwiseBoolean(ec); return; } if ((Binary.Oper & Binary.Operator.EqualityMask) != 0) { EmitEquality(ec); return; } Label is_null_label = ec.DefineLabel(); Label end_label = ec.DefineLabel(); if (ec.HasSet(BuilderContext.Options.AsyncBody) && Right.ContainsEmitWithAwait()) { Left = Left.EmitToField(ec); Right = Right.EmitToField(ec); } if (UnwrapLeft != null) { UnwrapLeft.EmitCheck(ec); } // // Don't emit HasValue check when left and right expressions are same // if (UnwrapRight != null && !Binary.Left.Equals(Binary.Right)) { UnwrapRight.EmitCheck(ec); if (UnwrapLeft != null) { ec.Emit(OpCodes.And); } } ec.Emit(OpCodes.Brfalse, is_null_label); if (UserOperator != null) { 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 { Binary.EmitOperator(ec, Left, Right); } // // Wrap the result when the operator return type is nullable type // if (type.IsNullableType) { ec.Emit(OpCodes.Newobj, NullableInfo.GetConstructor(type)); } ec.Emit(OpCodes.Br_S, end_label); ec.MarkLabel(is_null_label); if ((Binary.Oper & Binary.Operator.ComparisonMask) != 0) { ec.EmitInt(0); } else { LiftedNull.Create(type, loc).Emit(ec); } ec.MarkLabel(end_label); }
public override void Emit(EmitContext ec) { child.Emit(ec); ec.Emit(OpCodes.Newobj, NullableInfo.GetConstructor(type)); }
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); }