protected override bool EmitInternal(UnaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { if (node.Type != typeof(bool) && node.Type != typeof(bool?)) { return(ExpressionEmittersCollection.Emit(Expression.OnesComplement(node.Operand, node.Method), context, returnDefaultValueLabel, whatReturn, extend, out resultType)); } GroboIL il = context.Il; if (node.Method != null) { throw new NotSupportedException("Custom operator '" + node.NodeType + "' is not supported"); } var operand = node.Operand; context.EmitLoadArgument(operand, false, out resultType); if (resultType == typeof(bool)) { il.Ldc_I4(1); il.Xor(); } else if (resultType == typeof(bool?)) { using (var value = context.DeclareLocal(typeof(bool?))) { il.Stloc(value); il.Ldloca(value); context.EmitHasValueAccess(typeof(bool?)); var returnLabel = il.DefineLabel("return"); il.Brfalse(returnLabel); il.Ldloca(value); context.EmitValueAccess(typeof(bool?)); il.Ldc_I4(1); il.Xor(); il.Newobj(nullableBoolConstructor); il.Stloc(value); context.MarkLabelAndSurroundWithSP(returnLabel); il.Ldloc(value); } } else { throw new InvalidOperationException("Cannot perform '" + node.NodeType + "' operator on type '" + resultType + "'"); } return(false); }
protected override bool EmitInternal(UnaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { Type operandType; var result = ExpressionEmittersCollection.Emit(node.Operand, context, returnDefaultValueLabel, ResultType.Value, extend, out operandType); GroboIL il = context.Il; if (node.NodeType == ExpressionType.IsTrue || node.NodeType == ExpressionType.IsFalse) { if (!operandType.IsNullable()) { if (node.Method != null) { il.Call(node.Method); } else if (operandType == typeof(bool)) { if (node.NodeType == ExpressionType.IsFalse) { il.Ldc_I4(1); il.Xor(); } } else { throw new InvalidOperationException("Cannot perform operation '" + node.NodeType + "' to a type '" + operandType + "'"); } } else { using (var temp = context.DeclareLocal(operandType)) { il.Stloc(temp); il.Ldloca(temp); context.EmitHasValueAccess(operandType); var returnFalseLabel = il.DefineLabel("returnFalse"); il.Brfalse(returnFalseLabel); il.Ldloca(temp); context.EmitValueAccess(operandType); if (node.Method != null) { il.Call(node.Method); } else if (operandType == typeof(bool?)) { if (node.NodeType == ExpressionType.IsFalse) { il.Ldc_I4(1); il.Xor(); } } else { throw new InvalidOperationException("Cannot perform operation '" + node.NodeType + "' to a type '" + operandType + "'"); } var doneLabel = il.DefineLabel("done"); il.Br(doneLabel); context.MarkLabelAndSurroundWithSP(returnFalseLabel); il.Ldc_I4(0); context.MarkLabelAndSurroundWithSP(doneLabel); } } } else { if (!operandType.IsNullable()) { if (node.Method != null) { il.Call(node.Method); } else { if (operandType.IsStruct()) { throw new InvalidOperationException("Cannot perform operation '" + node.NodeType + "' to a struct '" + operandType + "'"); } switch (node.NodeType) { case ExpressionType.UnaryPlus: break; case ExpressionType.Negate: il.Neg(); break; case ExpressionType.NegateChecked: using (var temp = context.DeclareLocal(operandType)) { il.Stloc(temp); il.Ldc_I4(0); context.EmitConvert(typeof(int), operandType); il.Ldloc(temp); il.Sub_Ovf(operandType.Unsigned()); } break; case ExpressionType.Increment: il.Ldc_I4(1); context.EmitConvert(typeof(int), operandType); il.Add(); break; case ExpressionType.Decrement: il.Ldc_I4(1); context.EmitConvert(typeof(int), operandType); il.Sub(); break; case ExpressionType.OnesComplement: il.Not(); break; default: throw new InvalidOperationException("Node type '" + node.NodeType + "' invalid at this point"); } } } else { using (var temp = context.DeclareLocal(operandType)) { il.Stloc(temp); il.Ldloca(temp); context.EmitHasValueAccess(operandType); var returnNullLabel = il.DefineLabel("returnLabel"); il.Brfalse(returnNullLabel); Type argumentType = operandType.GetGenericArguments()[0]; if (node.Method != null) { il.Ldloca(temp); context.EmitValueAccess(operandType); il.Call(node.Method); } else { switch (node.NodeType) { case ExpressionType.UnaryPlus: il.Ldloca(temp); context.EmitValueAccess(operandType); break; case ExpressionType.Negate: il.Ldloca(temp); context.EmitValueAccess(operandType); il.Neg(); break; case ExpressionType.NegateChecked: il.Ldc_I4(0); context.EmitConvert(typeof(int), argumentType); il.Ldloca(temp); context.EmitValueAccess(operandType); il.Sub_Ovf(argumentType.Unsigned()); break; case ExpressionType.Increment: il.Ldloca(temp); context.EmitValueAccess(operandType); il.Ldc_I4(1); context.EmitConvert(typeof(int), argumentType); il.Add(); break; case ExpressionType.Decrement: il.Ldloca(temp); context.EmitValueAccess(operandType); il.Ldc_I4(1); context.EmitConvert(typeof(int), argumentType); il.Sub(); break; case ExpressionType.OnesComplement: il.Ldloca(temp); context.EmitValueAccess(operandType); il.Not(); break; default: throw new InvalidOperationException("Node type '" + node.NodeType + "' invalid at this point"); } } il.Newobj(operandType.GetConstructor(new[] { argumentType })); var doneLabel = il.DefineLabel("done"); il.Br(doneLabel); context.MarkLabelAndSurroundWithSP(returnNullLabel); context.EmitLoadDefaultValue(operandType); context.MarkLabelAndSurroundWithSP(doneLabel); } } } resultType = node.Type; return(result); }
protected override bool EmitInternal(SwitchExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { GroboIL il = context.Il; var defaultLabel = il.DefineLabel("default"); var caseLabels = new GroboIL.Label[node.Cases.Count]; GroboIL.Label switchValueIsNullLabel = null; for (int index = 0; index < node.Cases.Count; index++) { caseLabels[index] = il.DefineLabel("case#" + index); } context.EmitLoadArguments(node.SwitchValue); using (var switchValue = context.DeclareLocal(node.SwitchValue.Type)) { il.Stloc(switchValue); Tuple <int, int, int> switchCase; if (context.ParsedLambda.ParsedSwitches.TryGetValue(node, out switchCase)) { // use simplified hashtable to locate the proper case var labels = new List <GroboIL.Label>(); for (int index = 0; index < node.Cases.Count; index++) { foreach (var testValue in node.Cases[index].TestValues) { if (((ConstantExpression)testValue).Value != null) { labels.Add(caseLabels[index]); } else { switchValueIsNullLabel = caseLabels[index]; } } } if (switchValueIsNullLabel != null) { if (!node.SwitchValue.Type.IsNullable()) { il.Ldloc(switchValue); } else { il.Ldloca(switchValue); context.EmitHasValueAccess(node.SwitchValue.Type); } il.Brfalse(switchValueIsNullLabel); } EmittingContext.LocalHolder pureSwitchValue = switchValue; if (node.SwitchValue.Type.IsNullable()) { pureSwitchValue = context.DeclareLocal(node.SwitchValue.Type.GetGenericArguments()[0]); il.Ldloca(switchValue); context.EmitValueAccess(node.SwitchValue.Type); il.Stloc(pureSwitchValue); } Type temp; ExpressionEmittersCollection.Emit(context.ParsedLambda.ConstantsBuilder.MakeAccess(context.ParsedLambda.ConstantsParameter, switchCase.Item1), context, out temp); var type = node.SwitchValue.Type.IsNullable() ? node.SwitchValue.Type.GetGenericArguments()[0] : node.SwitchValue.Type; var typeCode = Type.GetTypeCode(type); switch (typeCode) { case TypeCode.Byte: case TypeCode.Char: case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: case TypeCode.SByte: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: il.Ldloc(pureSwitchValue); break; default: if (type.IsValueType) { il.Ldloca(pureSwitchValue); } else { il.Ldloc(pureSwitchValue); } il.Call(typeof(object).GetMethod("GetHashCode"), type); break; } using (var index = context.DeclareLocal(typeof(int))) { if (typeCode == TypeCode.Int64 || typeCode == TypeCode.UInt64) { il.Ldc_I8(switchCase.Item3); } else { il.Ldc_I4(switchCase.Item3); } il.Rem(true); if (typeCode == TypeCode.Int64 || typeCode == TypeCode.UInt64) { il.Conv <int>(); } il.Stloc(index); il.Ldloc(index); il.Ldelem(type); il.Ldloc(pureSwitchValue); if (node.Comparison != null) { il.Call(node.Comparison); } else { il.Ceq(); } il.Brfalse(defaultLabel); ExpressionEmittersCollection.Emit(context.ParsedLambda.ConstantsBuilder.MakeAccess(context.ParsedLambda.ConstantsParameter, switchCase.Item2), context, out temp); il.Ldloc(index); il.Ldelem(typeof(int)); il.Switch(labels.ToArray()); } if (pureSwitchValue != switchValue) { pureSwitchValue.Dispose(); } } else { // use a number of if/else branches to locate the proper case EmittingContext.LocalHolder pureSwitchValue = switchValue; EmittingContext.LocalHolder switchValueIsNull = null; if (node.SwitchValue.Type.IsNullable()) { pureSwitchValue = context.DeclareLocal(node.SwitchValue.Type.GetGenericArguments()[0]); switchValueIsNull = context.DeclareLocal(typeof(bool)); il.Ldloca(switchValue); il.Dup(); context.EmitValueAccess(node.SwitchValue.Type); il.Stloc(pureSwitchValue); context.EmitHasValueAccess(node.SwitchValue.Type); il.Stloc(switchValueIsNull); } for (int index = 0; index < node.Cases.Count; index++) { var caSe = node.Cases[index]; var label = caseLabels[index]; foreach (var testValue in caSe.TestValues) { context.EmitLoadArguments(testValue); GroboIL.Label elseLabel = null; if (testValue.Type.IsNullable()) { elseLabel = il.DefineLabel("else"); using (var temp = context.DeclareLocal(testValue.Type)) { il.Stloc(temp); il.Ldloca(temp); context.EmitHasValueAccess(testValue.Type); if (switchValueIsNull != null) { il.Ldloc(switchValueIsNull); il.Or(); il.Brfalse(label); il.Ldloca(temp); context.EmitHasValueAccess(testValue.Type); il.Ldloc(switchValueIsNull); il.And(); } il.Brfalse(elseLabel); il.Ldloca(temp); context.EmitValueAccess(testValue.Type); } } il.Ldloc(pureSwitchValue); if (node.Comparison != null) { il.Call(node.Comparison); } else { il.Ceq(); } il.Brtrue(label); if (elseLabel != null) { context.MarkLabelAndSurroundWithSP(elseLabel); } } } } } context.MarkLabelAndSurroundWithSP(defaultLabel); var doneLabel = il.DefineLabel("done"); context.EmitLoadArguments(node.DefaultBody); il.Br(doneLabel); for (int index = 0; index < node.Cases.Count; ++index) { context.MarkLabelAndSurroundWithSP(caseLabels[index]); context.EmitLoadArguments(node.Cases[index].Body); if (index < node.Cases.Count - 1) { il.Br(doneLabel); } } context.MarkLabelAndSurroundWithSP(doneLabel); resultType = node.Type; return(false); }
protected override bool EmitInternal(UnaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { var il = context.Il; GroboIL.Label operandIsNullLabel = context.CanReturn ? il.DefineLabel("operandIsNull") : null; var operandIsNullLabelUsed = ExpressionEmittersCollection.Emit(node.Operand, context, operandIsNullLabel, ResultType.Value, extend, out resultType); // stack: [obj] if (operandIsNullLabelUsed) { context.EmitReturnDefaultValue(resultType, operandIsNullLabel, il.DefineLabel("operandIsNotNull")); } if (resultType != node.Type && !(context.Options.HasFlag(CompilerOptions.UseTernaryLogic) && resultType == typeof(bool?) && node.Type == typeof(bool))) { if (node.Method != null) { if (!resultType.IsNullable()) { il.Call(node.Method); if (node.Type.IsNullable()) { il.Newobj(node.Type.GetConstructor(new[] { node.Method.ReturnType })); } } else { using (var temp = context.DeclareLocal(resultType)) { il.Stloc(temp); il.Ldloca(temp); il.Dup(); context.EmitHasValueAccess(resultType); var valueIsNullLabel = operandIsNullLabelUsed ? operandIsNullLabel : il.DefineLabel("valueIsNull"); il.Brfalse(valueIsNullLabel); context.EmitValueAccess(resultType); il.Call(node.Method); il.Newobj(node.Type.GetConstructor(new[] { node.Method.ReturnType })); if (!operandIsNullLabelUsed) { context.EmitReturnDefaultValue(node.Type, valueIsNullLabel, il.DefineLabel("valueIsNotNull")); } } } } else { switch (node.NodeType) { case ExpressionType.Convert: context.EmitConvert(resultType, node.Type); // stack: [(type)obj] break; case ExpressionType.ConvertChecked: context.EmitConvert(resultType, node.Type, true); // stack: [(type)obj] break; default: throw new InvalidOperationException("Node type '" + node.NodeType + "' is not valid at this point"); } } resultType = node.Type; } return(false); }
protected override bool EmitInternal(UnaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { GroboIL il = context.Il; var result = false; var operand = node.Operand; Type assigneeType; AssigneeKind assigneeKind; bool checkNullReferences = context.Options.HasFlag(CompilerOptions.CheckNullReferences); extend |= context.Options.HasFlag(CompilerOptions.ExtendOnAssign); GroboIL.Label assigneeIsNullLabel = null; bool assigneeIsNullLabelUsed = false; switch (operand.NodeType) { case ExpressionType.Parameter: assigneeType = null; assigneeKind = AssigneeKind.Parameter; checkNullReferences = false; break; case ExpressionType.MemberAccess: var memberExpression = (MemberExpression)operand; if (memberExpression.Expression == null) { assigneeType = null; assigneeKind = memberExpression.Member is FieldInfo ? AssigneeKind.StaticField : AssigneeKind.StaticProperty; checkNullReferences = false; } else { bool closureAssign = memberExpression.Expression == context.ParsedLambda.ClosureParameter || memberExpression.Expression.Type.IsStaticClosure(); checkNullReferences &= !closureAssign; if (node.NodeType != ExpressionType.Assign && context.CanReturn) { result |= ExpressionEmittersCollection.Emit(memberExpression.Expression, context, returnDefaultValueLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType); } else { assigneeIsNullLabel = !closureAssign && context.CanReturn ? il.DefineLabel("assigneeIsNull") : null; assigneeIsNullLabelUsed = ExpressionEmittersCollection.Emit(memberExpression.Expression, context, assigneeIsNullLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType); } assigneeKind = memberExpression.Member is FieldInfo ? AssigneeKind.InstanceField : AssigneeKind.InstanceProperty; } break; case ExpressionType.Index: var indexExpression = (IndexExpression)operand; if (indexExpression.Object == null) { throw new InvalidOperationException("Indexing of null object is invalid"); } if (indexExpression.Object.Type.IsArray && indexExpression.Object.Type.GetArrayRank() == 1) { if (node.NodeType != ExpressionType.Assign && context.CanReturn) { result |= ExpressionEmittersCollection.Emit(Expression.ArrayIndex(indexExpression.Object, indexExpression.Arguments.Single()), context, returnDefaultValueLabel, ResultType.ByRefAll, extend, out assigneeType); checkNullReferences = false; } else { assigneeIsNullLabel = context.CanReturn ? il.DefineLabel("assigneeIsNull") : null; assigneeIsNullLabelUsed = ExpressionEmittersCollection.Emit(Expression.ArrayIndex(indexExpression.Object, indexExpression.Arguments.Single()), context, assigneeIsNullLabel, ResultType.ByRefAll, extend, out assigneeType); } assigneeKind = AssigneeKind.SimpleArray; } else { if (node.NodeType != ExpressionType.Assign && context.CanReturn) { result |= ExpressionEmittersCollection.Emit(indexExpression.Object, context, returnDefaultValueLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType); } else { assigneeIsNullLabel = context.CanReturn ? il.DefineLabel("assigneeIsNull") : null; assigneeIsNullLabelUsed = ExpressionEmittersCollection.Emit(indexExpression.Object, context, assigneeIsNullLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType); } assigneeKind = indexExpression.Indexer != null ? AssigneeKind.IndexedProperty : AssigneeKind.MultiDimensionalArray; } break; default: throw new InvalidOperationException("Unable to assign to an expression of type '" + operand.NodeType + "'"); } if (assigneeType != null && assigneeType.IsValueType) { using (var temp = context.DeclareLocal(assigneeType)) { il.Stloc(temp); il.Ldloca(temp); } assigneeType = assigneeType.MakeByRefType(); } if (assigneeIsNullLabelUsed) { context.EmitReturnDefaultValue(assigneeType, assigneeIsNullLabel, il.DefineLabel("assigneeIsNotNull")); } if (checkNullReferences) { il.Dup(); il.Brfalse(returnDefaultValueLabel); result = true; } if (assigneeType != null) { il.Dup(); } object[] arguments = EmitAccess(assigneeKind, operand, context); if (!operand.Type.IsNullable()) { if (whatReturn == ResultType.Void) { EmitOp(node.NodeType, node.Method, node.Type, context); EmitAssign(assigneeKind, operand, context, arguments); } else { if (node.NodeType == ExpressionType.PostDecrementAssign || node.NodeType == ExpressionType.PostIncrementAssign) { using (var assignmentResult = context.DeclareLocal(operand.Type)) { il.Stloc(assignmentResult); il.Ldloc(assignmentResult); EmitOp(node.NodeType, node.Method, node.Type, context); EmitAssign(assigneeKind, operand, context, arguments); il.Ldloc(assignmentResult); } } else { EmitOp(node.NodeType, node.Method, node.Type, context); using (var assignmentResult = context.DeclareLocal(operand.Type)) { il.Stloc(assignmentResult); EmitAssign(assigneeKind, operand, context, arguments, assignmentResult); il.Ldloc(assignmentResult); } } } } else { using (var value = context.DeclareLocal(operand.Type)) { il.Stloc(value); il.Ldloca(value); context.EmitHasValueAccess(operand.Type); var returnNullLabel = il.DefineLabel("returnNull"); il.Brfalse(returnNullLabel); il.Ldloca(value); context.EmitValueAccess(operand.Type); Type argumentType = operand.Type.GetGenericArguments()[0]; ConstructorInfo constructor = operand.Type.GetConstructor(new[] { argumentType }); if (whatReturn == ResultType.Void) { EmitOp(node.NodeType, node.Method, argumentType, context); il.Newobj(constructor); EmitAssign(assigneeKind, operand, context, arguments); } else { if (node.NodeType == ExpressionType.PostDecrementAssign || node.NodeType == ExpressionType.PostIncrementAssign) { EmitOp(node.NodeType, node.Method, argumentType, context); il.Newobj(constructor); EmitAssign(assigneeKind, operand, context, arguments); il.Ldloc(value); } else { EmitOp(node.NodeType, node.Method, argumentType, context); il.Newobj(constructor); using (var assignmentResult = context.DeclareLocal(operand.Type)) { il.Stloc(assignmentResult); EmitAssign(assigneeKind, operand, context, arguments, assignmentResult); il.Ldloc(assignmentResult); } } } var doneLabel = il.DefineLabel("done"); il.Br(doneLabel); context.MarkLabelAndSurroundWithSP(returnNullLabel); if (assigneeType != null) { il.Pop(); } if (whatReturn != ResultType.Void) { il.Ldloc(value); } context.MarkLabelAndSurroundWithSP(doneLabel); } } resultType = whatReturn == ResultType.Void ? typeof(void) : operand.Type; return(result); }
protected override bool EmitInternal(BinaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { var left = node.Left; var right = node.Right; Type leftType, rightType; context.EmitLoadArgument(left, false, out leftType); context.EmitLoadArgument(right, false, out rightType); var il = context.Il; if (node.Method != null) { if (!leftType.IsNullable() && !rightType.IsNullable()) { il.Call(node.Method); } else { using (var localLeft = context.DeclareLocal(leftType)) using (var localRight = context.DeclareLocal(rightType)) { il.Stloc(localRight); il.Stloc(localLeft); var returnNullLabel = il.DefineLabel("returnNull"); if (leftType.IsNullable()) { il.Ldloca(localLeft); context.EmitHasValueAccess(leftType); il.Brfalse(returnNullLabel); } if (rightType.IsNullable()) { il.Ldloca(localRight); context.EmitHasValueAccess(rightType); il.Brfalse(returnNullLabel); } if (!leftType.IsNullable()) { il.Ldloc(localLeft); } else { il.Ldloca(localLeft); context.EmitValueAccess(leftType); } if (!rightType.IsNullable()) { il.Ldloc(localRight); } else { il.Ldloca(localRight); context.EmitValueAccess(rightType); } il.Call(node.Method); var doneLabel = il.DefineLabel("done"); il.Br(doneLabel); context.MarkLabelAndSurroundWithSP(returnNullLabel); context.EmitLoadDefaultValue(node.Type); context.MarkLabelAndSurroundWithSP(doneLabel); } } resultType = node.Method.ReturnType; } else { var type = leftType; if (type != rightType) { throw new InvalidOperationException("Cannot compare objects of different types '" + leftType + "' and '" + rightType + "'"); } if (!type.IsNullable()) { switch (node.NodeType) { case ExpressionType.GreaterThan: il.Cgt(type.Unsigned()); break; case ExpressionType.LessThan: il.Clt(type.Unsigned()); break; case ExpressionType.GreaterThanOrEqual: il.Clt(type.Unsigned()); il.Ldc_I4(1); il.Xor(); break; case ExpressionType.LessThanOrEqual: il.Cgt(type.Unsigned()); il.Ldc_I4(1); il.Xor(); break; default: throw new InvalidOperationException(); } resultType = typeof(bool); } else { if (!context.Options.HasFlag(CompilerOptions.UseTernaryLogic)) { using (var localLeft = context.DeclareLocal(type)) using (var localRight = context.DeclareLocal(type)) { il.Stloc(localRight); il.Stloc(localLeft); il.Ldloca(localLeft); context.EmitValueAccess(type); il.Ldloca(localRight); context.EmitValueAccess(type); var returnFalseLabel = il.DefineLabel("returnFalse"); var argument = type.GetGenericArguments()[0]; switch (node.NodeType) { case ExpressionType.GreaterThan: il.Ble(returnFalseLabel, argument.Unsigned()); break; case ExpressionType.LessThan: il.Bge(returnFalseLabel, argument.Unsigned()); break; case ExpressionType.GreaterThanOrEqual: il.Blt(returnFalseLabel, argument.Unsigned()); break; case ExpressionType.LessThanOrEqual: il.Bgt(returnFalseLabel, argument.Unsigned()); break; default: throw new InvalidOperationException(); } il.Ldloca(localLeft); context.EmitHasValueAccess(type); il.Ldloca(localRight); context.EmitHasValueAccess(type); il.And(); var doneLabel = il.DefineLabel("done"); il.Br(doneLabel); context.MarkLabelAndSurroundWithSP(returnFalseLabel); il.Ldc_I4(0); context.MarkLabelAndSurroundWithSP(doneLabel); resultType = typeof(bool); } } else { using (var localLeft = context.DeclareLocal(type)) using (var localRight = context.DeclareLocal(type)) { il.Stloc(localRight); il.Stloc(localLeft); il.Ldloca(localLeft); context.EmitHasValueAccess(type); il.Ldloca(localRight); context.EmitHasValueAccess(type); il.And(); var returnNullLabel = il.DefineLabel("returnNull"); il.Brfalse(returnNullLabel); il.Ldloca(localLeft); context.EmitValueAccess(type); il.Ldloca(localRight); context.EmitValueAccess(type); var argumentType = type.GetGenericArguments()[0]; switch (node.NodeType) { case ExpressionType.GreaterThan: il.Cgt(argumentType.Unsigned()); break; case ExpressionType.LessThan: il.Clt(argumentType.Unsigned()); break; case ExpressionType.GreaterThanOrEqual: il.Clt(argumentType.Unsigned()); il.Ldc_I4(1); il.Xor(); break; case ExpressionType.LessThanOrEqual: il.Cgt(argumentType.Unsigned()); il.Ldc_I4(1); il.Xor(); break; default: throw new InvalidOperationException(); } il.Newobj(nullableBoolConstructor); var doneLabel = il.DefineLabel("done"); il.Br(doneLabel); context.MarkLabelAndSurroundWithSP(returnNullLabel); context.EmitLoadDefaultValue(typeof(bool?)); context.MarkLabelAndSurroundWithSP(doneLabel); resultType = typeof(bool?); } } } } return(false); }
protected override bool EmitInternal(BinaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { Expression left = node.Left; Expression right = node.Right; Type leftType, rightType; context.EmitLoadArgument(left, false, out leftType); context.EmitLoadArgument(right, false, out rightType); GroboIL il = context.Il; if (!leftType.IsNullable() && !rightType.IsNullable()) { if (node.Method != null) { il.Call(node.Method); } else { if (leftType.IsStruct() || rightType.IsStruct()) { throw new InvalidOperationException("Cannot compare structs"); } il.Ceq(); if (node.NodeType == ExpressionType.NotEqual) { il.Ldc_I4(1); il.Xor(); } } } else { var type = leftType; if (type != rightType) { throw new InvalidOperationException("Cannot compare objects of different types '" + leftType + "' and '" + rightType + "'"); } using (var localLeft = context.DeclareLocal(type)) using (var localRight = context.DeclareLocal(type)) { il.Stloc(localRight); il.Stloc(localLeft); if (node.Method != null) { il.Ldloca(localLeft); // stack: [&left] context.EmitHasValueAccess(type); // stack: [left.HasValue] il.Dup(); // stack: [left.HasValue, left.HasValue] il.Ldloca(localRight); // stack: [left.HasValue, left.HasValue, &right] context.EmitHasValueAccess(type); // stack: [left.HasValue, left.HasValue, right.HasValue] var notEqualLabel = il.DefineLabel("notEqual"); il.Bne_Un(notEqualLabel); // stack: [left.HasValue] var equalLabel = il.DefineLabel("equal"); il.Brfalse(equalLabel); il.Ldloca(localLeft); context.EmitValueAccess(type); il.Ldloca(localRight); context.EmitValueAccess(type); il.Call(node.Method); var doneLabel = il.DefineLabel("done"); il.Br(doneLabel); context.MarkLabelAndSurroundWithSP(notEqualLabel); il.Pop(); il.Ldc_I4(node.NodeType == ExpressionType.Equal ? 0 : 1); il.Br(doneLabel); context.MarkLabelAndSurroundWithSP(equalLabel); il.Ldc_I4(node.NodeType == ExpressionType.Equal ? 1 : 0); context.MarkLabelAndSurroundWithSP(doneLabel); } else { il.Ldloca(localLeft); context.EmitValueAccess(type); il.Ldloca(localRight); context.EmitValueAccess(type); var notEqualLabel = il.DefineLabel("notEqual"); il.Bne_Un(notEqualLabel); il.Ldloca(localLeft); context.EmitHasValueAccess(type); il.Ldloca(localRight); context.EmitHasValueAccess(type); il.Ceq(); var doneLabel = il.DefineLabel("done"); il.Br(doneLabel); context.MarkLabelAndSurroundWithSP(notEqualLabel); il.Ldc_I4(0); context.MarkLabelAndSurroundWithSP(doneLabel); if (node.NodeType == ExpressionType.NotEqual) { il.Ldc_I4(1); il.Xor(); } } } } resultType = typeof(bool); return(false); }
protected override bool EmitInternal(BinaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) { GroboIL il = context.Il; if (node.Method != null) { throw new NotSupportedException("Custom operator '" + node.NodeType + "' is not supported"); } Expression left = node.Left; Expression right = node.Right; Type leftType; context.EmitLoadArgument(left, false, out leftType); // stack: [left] if (leftType == typeof(bool)) { switch (node.NodeType) { case ExpressionType.AndAlso: { var returnFalseLabel = il.DefineLabel("returnFalse"); il.Brfalse(returnFalseLabel); // if(left == false) return false; stack: [] Type rightType; context.EmitLoadArgument(right, false, out rightType); // stack: [right] var doneLabel = il.DefineLabel("done"); il.Br(doneLabel); // goto done; stack: [right] context.MarkLabelAndSurroundWithSP(returnFalseLabel); il.Ldc_I4(0); // stack: [false] if (rightType == typeof(bool?)) { il.Newobj(nullableBoolConstructor); // stack: [new bool?(false)] } context.MarkLabelAndSurroundWithSP(doneLabel); resultType = rightType; break; } case ExpressionType.OrElse: { var returnTrueLabel = il.DefineLabel("returnTrue"); il.Brtrue(returnTrueLabel); // if(left == true) return true; stack: [] Type rightType; context.EmitLoadArgument(right, false, out rightType); // stack: [right] var doneLabel = il.DefineLabel("done"); il.Br(doneLabel); // goto done; stack: [right] context.MarkLabelAndSurroundWithSP(returnTrueLabel); il.Ldc_I4(1); // stack: [true] if (rightType == typeof(bool?)) { il.Newobj(nullableBoolConstructor); // stack: [new bool?(true)] } context.MarkLabelAndSurroundWithSP(doneLabel); resultType = rightType; break; } default: throw new NotSupportedException("Node type '" + node.NodeType + "' is not supported"); } } else // bool? { switch (node.NodeType) { case ExpressionType.AndAlso: { /* * +-------+-------+-------+-------+ * | && | null | false | true | * +-------+-------+-------+-------+ * | null | null | false | null | * +-------+-------+-------+-------+ * | false | false | false | false | * +-------+-------+-------+-------+ * | true | null | false | true | * +-------+-------+-------+-------+ */ using (var localLeft = context.DeclareLocal(typeof(bool?))) { il.Stloc(localLeft); // localLeft = left; stack: [] il.Ldloca(localLeft); // stack: [&localLeft] context.EmitHasValueAccess(typeof(bool?)); // stack: [localLeft.HasValue] il.Ldc_I4(1); // stack: [localLeft.HasValue, true] il.Xor(); // stack: [!localLeft.HasValue] il.Ldloca(localLeft); // stack: [!localLeft.HasValue, &localLeft] context.EmitValueAccess(typeof(bool?)); // stack: [!localLeft.HasValue, localLeft.Value] il.Or(); // stack: [!localLeft.HasValue || localLeft.Value] var returnFalseLabel = il.DefineLabel("returnFalse"); il.Brfalse(returnFalseLabel); // if(localLeft == false) goto returnFalse; stack: [] Type rightType; context.EmitLoadArgument(right, false, out rightType); // stack: [right] using (var localRight = context.DeclareLocal(rightType)) { il.Stloc(localRight); // localRight = right; stack: [] if (rightType == typeof(bool)) { il.Ldloc(localRight); // stack: [localRight] } else { il.Ldloca(localRight); // stack: [&localRight] context.EmitHasValueAccess(typeof(bool?)); // stack: [localRight.HasValue] il.Ldc_I4(1); // stack: [localRight.HasValue, true] il.Xor(); // stack: [!localRight.HasValue] il.Ldloca(localRight); // stack: [!localRight.HasValue, &localRight] context.EmitValueAccess(typeof(bool?)); // stack: [!localRight.HasValue, localRight.Value] il.Or(); // stack: [!localRight.HasValue || localRight.Value] } il.Brfalse(returnFalseLabel); // if(localRight == false) goto returnFalse; if (rightType == typeof(bool)) { il.Ldloc(localRight); // stack: [localRight] } else { il.Ldloca(localRight); // stack: [&localRight] context.EmitHasValueAccess(typeof(bool?)); // stack: [localRight.HasValue] il.Ldloca(localRight); // stack: [localRight.HasValue, &localRight] context.EmitValueAccess(typeof(bool?)); // stack: [localRight.HasValue, localRight.Value] il.And(); // stack: [localRight.HasValue && localRight.Value] } var returnLeftLabel = il.DefineLabel("returnLeft"); il.Brtrue(returnLeftLabel); // if(localRight == true) goto returnLeft; il.Ldloca(localLeft); // stack: [&localLeft] il.Initobj(typeof(bool?)); // localLeft = default(bool?); stack: [] context.MarkLabelAndSurroundWithSP(returnLeftLabel); il.Ldloc(localLeft); // stack: [localLeft] var doneLabel = il.DefineLabel("done"); il.Br(doneLabel); context.MarkLabelAndSurroundWithSP(returnFalseLabel); il.Ldc_I4(0); // stack: [false] il.Newobj(nullableBoolConstructor); // new bool?(false) context.MarkLabelAndSurroundWithSP(doneLabel); resultType = typeof(bool?); } } break; } case ExpressionType.OrElse: { /* * +-------+-------+-------+-------+ * | || | null | false | true | * +-------+-------+-------+-------+ * | null | null | null | true | * +-------+-------+-------+-------+ * | false | null | false | true | * +-------+-------+-------+-------+ * | true | true | true | true | * +-------+-------+-------+-------+ */ using (var localLeft = context.DeclareLocal(typeof(bool?))) { il.Stloc(localLeft); // localLeft = left; stack: [] il.Ldloca(localLeft); // stack: [&localLeft] context.EmitHasValueAccess(typeof(bool?)); // stack: [localLeft.HasValue] il.Ldloca(localLeft); // stack: [localLeft.HasValue, &localLeft] context.EmitValueAccess(typeof(bool?)); // stack: [localLeft.HasValue, localLeft.Value] il.And(); // stack: [localLeft.HasValue && localLeft.Value] var returnTrueLabel = il.DefineLabel("returnTrue"); il.Brtrue(returnTrueLabel); // if(localLeft == true) goto returnTrue; stack: [] Type rightType; context.EmitLoadArgument(right, false, out rightType); // stack: [right] using (var localRight = context.DeclareLocal(rightType)) { il.Stloc(localRight); // localRight = right; stack: [] if (rightType == typeof(bool)) { il.Ldloc(localRight); // stack: [localRight] } else { il.Ldloca(localRight); // stack: [&localRight] context.EmitHasValueAccess(typeof(bool?)); // stack: [localRight.HasValue] il.Ldloca(localRight); // stack: [localRight.HasValue, &localRight] context.EmitValueAccess(typeof(bool?)); // stack: [localRight.HasValue, localRight.Value] il.And(); // stack: [localRight.HasValue && localRight.Value] } il.Brtrue(returnTrueLabel); // if(localRight == true) goto returnTrue; stack: [] if (rightType == typeof(bool)) { il.Ldloc(localRight); // stack: [localRight] } else { il.Ldloca(localRight); // stack: [&localRight] context.EmitHasValueAccess(typeof(bool?)); // stack: [localRight.HasValue] il.Ldc_I4(1); // stack: [localRight.HasValue, true] il.Xor(); // stack: [!localRight.HasValue] il.Ldloca(localRight); // stack: [!localRight.HasValue, &localRight] context.EmitValueAccess(typeof(bool?)); // stack: [!localRight.HasValue, localRight.Value] il.Or(); // stack: [!localRight.HasValue || localRight.Value] } var returnLeftLabel = il.DefineLabel("returnLeft"); il.Brfalse(returnLeftLabel); // if(localRight == false) goto returnLeft; il.Ldloca(localLeft); // stack: [&localLeft] il.Initobj(typeof(bool?)); // localLeft = default(bool?); stack: [] context.MarkLabelAndSurroundWithSP(returnLeftLabel); il.Ldloc(localLeft); // stack: [localLeft] var doneLabel = il.DefineLabel("done"); il.Br(doneLabel); context.MarkLabelAndSurroundWithSP(returnTrueLabel); il.Ldc_I4(1); // stack: [true] il.Newobj(nullableBoolConstructor); // new bool?(true) context.MarkLabelAndSurroundWithSP(doneLabel); resultType = typeof(bool?); } } break; } default: throw new NotSupportedException("Node type '" + node.NodeType + "' is not supported"); } } return(false); }