Files
Flee/ExpressionElements/Compare.cs

281 lines
9.8 KiB
C#

using System.Diagnostics;
using System.Reflection.Emit;
using System.Reflection;
using Flee.ExpressionElements.Base;
using Flee.ExpressionElements.Literals.Integral;
using Flee.InternalTypes;
using Flee.PublicTypes;
using System;
namespace Flee.ExpressionElements
{
internal class CompareElement : BinaryExpressionElement
{
private LogicalCompareOperation _myOperation;
public CompareElement()
{
}
public void Initialize(ExpressionElement leftChild, ExpressionElement rightChild, LogicalCompareOperation op)
{
MyLeftChild = leftChild;
MyRightChild = rightChild;
_myOperation = op;
}
public void Validate()
{
this.ValidateInternal(_myOperation);
}
protected override void GetOperation(object operation)
{
_myOperation = (LogicalCompareOperation)operation;
}
protected override System.Type GetResultType(System.Type leftType, System.Type rightType)
{
Type binaryResultType = ImplicitConverter.GetBinaryResultType(leftType, rightType);
MethodInfo overloadedOperator = this.GetOverloadedCompareOperator();
bool isEqualityOp = IsOpTypeEqualOrNotEqual(_myOperation);
// Use our string equality instead of overloaded operator
if (object.ReferenceEquals(leftType, typeof(string)) & object.ReferenceEquals(rightType, typeof(string)) & isEqualityOp == true)
{
// String equality
return typeof(bool);
}
else if ((overloadedOperator != null))
{
return overloadedOperator.ReturnType;
}
else if ((binaryResultType != null))
{
// Comparison of numeric operands
return typeof(bool);
}
else if (object.ReferenceEquals(leftType, typeof(bool)) & object.ReferenceEquals(rightType, typeof(bool)) & isEqualityOp == true)
{
// Boolean equality
return typeof(bool);
}
else if (this.AreBothChildrenReferenceTypes() == true & isEqualityOp == true)
{
// Comparison of reference types
return typeof(bool);
}
else if (this.AreBothChildrenSameEnum() == true)
{
return typeof(bool);
}
else
{
// Invalid operands
return null;
}
}
private MethodInfo GetOverloadedCompareOperator()
{
string name = GetCompareOperatorName(_myOperation);
return base.GetOverloadedBinaryOperator(name, _myOperation);
}
private static string GetCompareOperatorName(LogicalCompareOperation op)
{
switch (op)
{
case LogicalCompareOperation.Equal:
return "Equality";
case LogicalCompareOperation.NotEqual:
return "Inequality";
case LogicalCompareOperation.GreaterThan:
return "GreaterThan";
case LogicalCompareOperation.LessThan:
return "LessThan";
case LogicalCompareOperation.GreaterThanOrEqual:
return "GreaterThanOrEqual";
case LogicalCompareOperation.LessThanOrEqual:
return "LessThanOrEqual";
default:
Debug.Assert(false, "unknown compare type");
return null;
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
Type binaryResultType = ImplicitConverter.GetBinaryResultType(MyLeftChild.ResultType, MyRightChild.ResultType);
MethodInfo overloadedOperator = this.GetOverloadedCompareOperator();
if (this.AreBothChildrenOfType(typeof(string)))
{
// String equality
MyLeftChild.Emit(ilg, services);
MyRightChild.Emit(ilg, services);
EmitStringEquality(ilg, _myOperation, services);
}
else if ((overloadedOperator != null))
{
base.EmitOverloadedOperatorCall(overloadedOperator, ilg, services);
}
else if ((binaryResultType != null))
{
// Emit a compare of numeric operands
EmitChildWithConvert(MyLeftChild, binaryResultType, ilg, services);
EmitChildWithConvert(MyRightChild, binaryResultType, ilg, services);
EmitCompareOperation(ilg, _myOperation);
}
else if (this.AreBothChildrenOfType(typeof(bool)))
{
// Boolean equality
this.EmitRegular(ilg, services);
}
else if (this.AreBothChildrenReferenceTypes() == true)
{
// Reference equality
this.EmitRegular(ilg, services);
}
else if (MyLeftChild.ResultType.IsEnum == true & MyRightChild.ResultType.IsEnum == true)
{
this.EmitRegular(ilg, services);
}
else
{
Debug.Fail("unknown operand types");
}
}
private void EmitRegular(FleeILGenerator ilg, IServiceProvider services)
{
MyLeftChild.Emit(ilg, services);
MyRightChild.Emit(ilg, services);
this.EmitCompareOperation(ilg, _myOperation);
}
private static void EmitStringEquality(FleeILGenerator ilg, LogicalCompareOperation op, IServiceProvider services)
{
// Get the StringComparison from the options
ExpressionOptions options = (ExpressionOptions)services.GetService(typeof(ExpressionOptions));
Int32LiteralElement ic = new Int32LiteralElement((int)options.StringComparison);
ic.Emit(ilg, services);
// and emit the method call
System.Reflection.MethodInfo mi = typeof(string).GetMethod("Equals", new Type[] { typeof(string), typeof(string), typeof(StringComparison) }, null);
ilg.Emit(OpCodes.Call, mi);
if (op == LogicalCompareOperation.NotEqual)
{
ilg.Emit(OpCodes.Ldc_I4_0);
ilg.Emit(OpCodes.Ceq);
}
}
private static bool IsOpTypeEqualOrNotEqual(LogicalCompareOperation op)
{
return op == LogicalCompareOperation.Equal | op == LogicalCompareOperation.NotEqual;
}
private bool AreBothChildrenReferenceTypes()
{
return MyLeftChild.ResultType.IsValueType == false & MyRightChild.ResultType.IsValueType == false;
}
private bool AreBothChildrenSameEnum()
{
return MyLeftChild.ResultType.IsEnum == true && object.ReferenceEquals(MyLeftChild.ResultType, MyRightChild.ResultType);
}
/// <summary>
/// Emit the actual compare
/// </summary>
/// <param name="ilg"></param>
/// <param name="op"></param>
private void EmitCompareOperation(FleeILGenerator ilg, LogicalCompareOperation op)
{
OpCode ltOpcode = this.GetCompareGTLTOpcode(false);
OpCode gtOpcode = this.GetCompareGTLTOpcode(true);
switch (op)
{
case LogicalCompareOperation.Equal:
ilg.Emit(OpCodes.Ceq);
break;
case LogicalCompareOperation.LessThan:
ilg.Emit(ltOpcode);
break;
case LogicalCompareOperation.GreaterThan:
ilg.Emit(gtOpcode);
break;
case LogicalCompareOperation.NotEqual:
ilg.Emit(OpCodes.Ceq);
ilg.Emit(OpCodes.Ldc_I4_0);
ilg.Emit(OpCodes.Ceq);
break;
case LogicalCompareOperation.LessThanOrEqual:
ilg.Emit(gtOpcode);
ilg.Emit(OpCodes.Ldc_I4_0);
ilg.Emit(OpCodes.Ceq);
break;
case LogicalCompareOperation.GreaterThanOrEqual:
ilg.Emit(ltOpcode);
ilg.Emit(OpCodes.Ldc_I4_0);
ilg.Emit(OpCodes.Ceq);
break;
default:
Debug.Fail("Unknown op type");
break;
}
}
/// <summary>
/// Get the correct greater/less than opcode
/// </summary>
/// <param name="greaterThan"></param>
/// <returns></returns>
private OpCode GetCompareGTLTOpcode(bool greaterThan)
{
Type leftType = MyLeftChild.ResultType;
if (object.ReferenceEquals(leftType, MyRightChild.ResultType))
{
if (object.ReferenceEquals(leftType, typeof(UInt32)) | object.ReferenceEquals(leftType, typeof(UInt64)))
{
if (greaterThan == true)
{
return OpCodes.Cgt_Un;
}
else
{
return OpCodes.Clt_Un;
}
}
else
{
return GetCompareOpcode(greaterThan);
}
}
else
{
return GetCompareOpcode(greaterThan);
}
}
private static OpCode GetCompareOpcode(bool greaterThan)
{
if (greaterThan == true)
{
return OpCodes.Cgt;
}
else
{
return OpCodes.Clt;
}
}
}
}