/// <summary> /// Emits code for comparing a nullable /// </summary> private void compileNullable(Context ctx, NodeBase nullValue, NodeBase otherValue) { var gen = ctx.CurrentILGenerator; var nullType = nullValue.GetExpressionType(ctx); var otherType = otherValue.GetExpressionType(ctx); var otherNull = otherType.IsNullableType(); var getValOrDefault = nullType.GetMethod("GetValueOrDefault", Type.EmptyTypes); var hasValueGetter = nullType.GetProperty("HasValue").GetGetMethod(); var falseLabel = gen.DefineLabel(); var endLabel = gen.DefineLabel(); LocalName nullVar, otherVar = null; nullVar = ctx.CurrentScope.DeclareImplicitName(ctx, nullType, true); if (otherNull) otherVar = ctx.CurrentScope.DeclareImplicitName(ctx, otherType, true); // if (otherNull) // { // otherVar = ctx.CurrentScope.DeclareImplicitName(ctx, otherType, true); // // var code = Expr.Block( // Expr.Let(nullVar, nullValue), // Expr.Let(otherVar, otherValue), // Expr.Binary( // Kind == ComparisonOperatorKind.Equals ? BooleanOperatorKind.And : BooleanOperatorKind.Or, // Expr.Compare( // Kind, // Expr.Invoke(Expr.GetIdentifier(nullVar), "GetValueOrDefault"), // Expr.Invoke(Expr.GetIdentifier(otherVar), "GetValueOrDefault") // ), // Expr.Compare( // Kind, // Expr.Invoke(Expr.GetIdentifier(nullVar), "get_HasValue"), // Expr.Invoke(Expr.GetIdentifier(otherVar), "get_HasValue") // ) // ) // ); // // code.Compile(ctx, true); // } // else // { // var code = Expr.Block( // Expr.Let(nullVar, nullValue), // Expr.Binary( // Kind == ComparisonOperatorKind.Equals ? BooleanOperatorKind.And : BooleanOperatorKind.Or, // Expr.Compare( // Kind, // Expr.Invoke(Expr.GetIdentifier(nullVar), "GetValueOrDefault"), // Expr.Cast(otherValue, Nullable.GetUnderlyingType(nullType)) // ), // Expr.Invoke(Expr.GetIdentifier(nullVar), "get_HasValue") // ) // ); // // code.Compile(ctx, true); // } // $tmp = nullValue nullValue.Compile(ctx, true); gen.EmitSaveLocal(nullVar); if (otherNull) { // $tmp2 = otherValue otherValue.Compile(ctx, true); gen.EmitSaveLocal(otherVar); } // $tmp == $tmp2 gen.EmitLoadLocal(nullVar, true); gen.EmitCall(getValOrDefault); if (otherNull) { gen.EmitLoadLocal(otherVar, true); gen.EmitCall(getValOrDefault); } else { otherValue.Compile(ctx, true); } gen.EmitBranchNotEquals(falseLabel); // otherwise, compare HasValues gen.EmitLoadLocal(nullVar, true); gen.EmitCall(hasValueGetter); if (otherNull) { gen.EmitLoadLocal(otherVar, true); gen.EmitCall(hasValueGetter); gen.EmitCompareEqual(); } if(Kind == ComparisonOperatorKind.NotEquals) emitInversion(gen); gen.EmitJump(endLabel); gen.MarkLabel(falseLabel); gen.EmitConstant(false); gen.MarkLabel(endLabel); gen.EmitNop(); }
/// <summary> /// Checks if the nullable expression is null. /// </summary> private void compileHasValue(Context ctx, NodeBase nullValue) { var gen = ctx.CurrentILGenerator; var nullType = nullValue.GetExpressionType(ctx); var nullVar = ctx.CurrentScope.DeclareImplicitName(ctx, nullType, true); var hasValueGetter = nullType.GetProperty("HasValue").GetGetMethod(); nullValue.Compile(ctx, true); gen.EmitSaveLocal(nullVar); gen.EmitLoadLocal(nullVar, true); gen.EmitCall(hasValueGetter); // sic! get_HasValue == true when value != null if(Kind == ComparisonOperatorKind.Equals) emitInversion(gen); }