Flee
This commit is contained in:
327
ExpressionElements/Arithmetic.cs
Normal file
327
ExpressionElements/Arithmetic.cs
Normal file
@@ -0,0 +1,327 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.ExpressionElements.Base.Literals;
|
||||
using Flee.ExpressionElements.Literals.Integral;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
|
||||
namespace Flee.ExpressionElements
|
||||
{
|
||||
internal class ArithmeticElement : BinaryExpressionElement
|
||||
{
|
||||
private static MethodInfo _ourPowerMethodInfo;
|
||||
private static MethodInfo _ourStringConcatMethodInfo;
|
||||
private static MethodInfo _ourObjectConcatMethodInfo;
|
||||
private BinaryArithmeticOperation _myOperation;
|
||||
|
||||
public ArithmeticElement()
|
||||
{
|
||||
_ourPowerMethodInfo = typeof(Math).GetMethod("Pow", BindingFlags.Public | BindingFlags.Static);
|
||||
_ourStringConcatMethodInfo = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string) }, null);
|
||||
_ourObjectConcatMethodInfo = typeof(string).GetMethod("Concat", new Type[] { typeof(object), typeof(object) }, null);
|
||||
}
|
||||
|
||||
protected override void GetOperation(object operation)
|
||||
{
|
||||
_myOperation = (BinaryArithmeticOperation)operation;
|
||||
}
|
||||
|
||||
protected override System.Type GetResultType(System.Type leftType, System.Type rightType)
|
||||
{
|
||||
Type binaryResultType = ImplicitConverter.GetBinaryResultType(leftType, rightType);
|
||||
MethodInfo overloadedMethod = this.GetOverloadedArithmeticOperator();
|
||||
|
||||
// Is an overloaded operator defined for our left and right children?
|
||||
if ((overloadedMethod != null))
|
||||
{
|
||||
// Yes, so use its return type
|
||||
return overloadedMethod.ReturnType;
|
||||
}
|
||||
else if ((binaryResultType != null))
|
||||
{
|
||||
// Operands are primitive types. Return computed result type unless we are doing a power operation
|
||||
if (_myOperation == BinaryArithmeticOperation.Power)
|
||||
{
|
||||
return this.GetPowerResultType(leftType, rightType, binaryResultType);
|
||||
}
|
||||
else
|
||||
{
|
||||
return binaryResultType;
|
||||
}
|
||||
}
|
||||
else if (this.IsEitherChildOfType(typeof(string)) == true & (_myOperation == BinaryArithmeticOperation.Add))
|
||||
{
|
||||
// String concatenation
|
||||
return typeof(string);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invalid types
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Type GetPowerResultType(Type leftType, Type rightType, Type binaryResultType)
|
||||
{
|
||||
if (this.IsOptimizablePower == true)
|
||||
{
|
||||
return leftType;
|
||||
}
|
||||
else
|
||||
{
|
||||
return typeof(double);
|
||||
}
|
||||
}
|
||||
|
||||
private MethodInfo GetOverloadedArithmeticOperator()
|
||||
{
|
||||
// Get the name of the operator
|
||||
string name = GetOverloadedOperatorFunctionName(_myOperation);
|
||||
return base.GetOverloadedBinaryOperator(name, _myOperation);
|
||||
}
|
||||
|
||||
private static string GetOverloadedOperatorFunctionName(BinaryArithmeticOperation op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case BinaryArithmeticOperation.Add:
|
||||
return "Addition";
|
||||
case BinaryArithmeticOperation.Subtract:
|
||||
return "Subtraction";
|
||||
case BinaryArithmeticOperation.Multiply:
|
||||
return "Multiply";
|
||||
case BinaryArithmeticOperation.Divide:
|
||||
return "Division";
|
||||
case BinaryArithmeticOperation.Mod:
|
||||
return "Modulus";
|
||||
case BinaryArithmeticOperation.Power:
|
||||
return "Exponent";
|
||||
default:
|
||||
Debug.Assert(false, "unknown operator type");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
MethodInfo overloadedMethod = this.GetOverloadedArithmeticOperator();
|
||||
|
||||
if ((overloadedMethod != null))
|
||||
{
|
||||
// Emit a call to an overloaded operator
|
||||
this.EmitOverloadedOperatorCall(overloadedMethod, ilg, services);
|
||||
}
|
||||
else if (this.IsEitherChildOfType(typeof(string)) == true)
|
||||
{
|
||||
// One of our operands is a string so emit a concatenation
|
||||
this.EmitStringConcat(ilg, services);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Emit a regular arithmetic operation
|
||||
EmitArithmeticOperation(_myOperation, ilg, services);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsUnsignedForArithmetic(Type t)
|
||||
{
|
||||
return object.ReferenceEquals(t, typeof(UInt32)) | object.ReferenceEquals(t, typeof(UInt64));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit an arithmetic operation with handling for unsigned and checked contexts
|
||||
/// </summary>
|
||||
/// <param name="op"></param>
|
||||
/// <param name="ilg"></param>
|
||||
/// <param name="services"></param>
|
||||
private void EmitArithmeticOperation(BinaryArithmeticOperation op, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
ExpressionOptions options = (ExpressionOptions)services.GetService(typeof(ExpressionOptions));
|
||||
bool unsigned = IsUnsignedForArithmetic(MyLeftChild.ResultType) & IsUnsignedForArithmetic(MyRightChild.ResultType);
|
||||
bool integral = Utility.IsIntegralType(MyLeftChild.ResultType) & Utility.IsIntegralType(MyRightChild.ResultType);
|
||||
bool emitOverflow = integral & options.Checked;
|
||||
|
||||
EmitChildWithConvert(MyLeftChild, this.ResultType, ilg, services);
|
||||
|
||||
if (this.IsOptimizablePower == false)
|
||||
{
|
||||
EmitChildWithConvert(MyRightChild, this.ResultType, ilg, services);
|
||||
}
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case BinaryArithmeticOperation.Add:
|
||||
if (emitOverflow == true)
|
||||
{
|
||||
if (unsigned == true)
|
||||
{
|
||||
ilg.Emit(OpCodes.Add_Ovf_Un);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Add_Ovf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Add);
|
||||
}
|
||||
break;
|
||||
case BinaryArithmeticOperation.Subtract:
|
||||
if (emitOverflow == true)
|
||||
{
|
||||
if (unsigned == true)
|
||||
{
|
||||
ilg.Emit(OpCodes.Sub_Ovf_Un);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Sub_Ovf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Sub);
|
||||
}
|
||||
break;
|
||||
case BinaryArithmeticOperation.Multiply:
|
||||
this.EmitMultiply(ilg, emitOverflow, unsigned);
|
||||
break;
|
||||
case BinaryArithmeticOperation.Divide:
|
||||
if (unsigned == true)
|
||||
{
|
||||
ilg.Emit(OpCodes.Div_Un);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Div);
|
||||
}
|
||||
break;
|
||||
case BinaryArithmeticOperation.Mod:
|
||||
if (unsigned == true)
|
||||
{
|
||||
ilg.Emit(OpCodes.Rem_Un);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Rem);
|
||||
}
|
||||
break;
|
||||
case BinaryArithmeticOperation.Power:
|
||||
this.EmitPower(ilg, emitOverflow, unsigned);
|
||||
break;
|
||||
default:
|
||||
Debug.Fail("Unknown op type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitPower(FleeILGenerator ilg, bool emitOverflow, bool unsigned)
|
||||
{
|
||||
if (this.IsOptimizablePower == true)
|
||||
{
|
||||
this.EmitOptimizedPower(ilg, emitOverflow, unsigned);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Call, _ourPowerMethodInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitOptimizedPower(FleeILGenerator ilg, bool emitOverflow, bool unsigned)
|
||||
{
|
||||
Int32LiteralElement right = (Int32LiteralElement)MyRightChild;
|
||||
|
||||
if (right.Value == 0)
|
||||
{
|
||||
ilg.Emit(OpCodes.Pop);
|
||||
IntegralLiteralElement.EmitLoad(1, ilg);
|
||||
ImplicitConverter.EmitImplicitNumericConvert(typeof(Int32), MyLeftChild.ResultType, ilg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (right.Value == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Start at 1 since left operand has already been emited once
|
||||
for (int i = 1; i <= right.Value - 1; i++)
|
||||
{
|
||||
ilg.Emit(OpCodes.Dup);
|
||||
}
|
||||
|
||||
for (int i = 1; i <= right.Value - 1; i++)
|
||||
{
|
||||
this.EmitMultiply(ilg, emitOverflow, unsigned);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitMultiply(FleeILGenerator ilg, bool emitOverflow, bool unsigned)
|
||||
{
|
||||
if (emitOverflow == true)
|
||||
{
|
||||
if (unsigned == true)
|
||||
{
|
||||
ilg.Emit(OpCodes.Mul_Ovf_Un);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Mul_Ovf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Mul);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit a string concatenation
|
||||
/// </summary>
|
||||
/// <param name="ilg"></param>
|
||||
/// <param name="services"></param>
|
||||
private void EmitStringConcat(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
Type argType = default(Type);
|
||||
System.Reflection.MethodInfo concatMethodInfo = default(System.Reflection.MethodInfo);
|
||||
|
||||
// Pick the most specific concat method
|
||||
if (this.AreBothChildrenOfType(typeof(string)) == true)
|
||||
{
|
||||
concatMethodInfo = _ourStringConcatMethodInfo;
|
||||
argType = typeof(string);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(this.IsEitherChildOfType(typeof(string)), "one child must be a string");
|
||||
concatMethodInfo = _ourObjectConcatMethodInfo;
|
||||
argType = typeof(object);
|
||||
}
|
||||
|
||||
// Emit the operands and call the function
|
||||
MyLeftChild.Emit(ilg, services);
|
||||
ImplicitConverter.EmitImplicitConvert(MyLeftChild.ResultType, argType, ilg);
|
||||
MyRightChild.Emit(ilg, services);
|
||||
ImplicitConverter.EmitImplicitConvert(MyRightChild.ResultType, argType, ilg);
|
||||
ilg.Emit(OpCodes.Call, concatMethodInfo);
|
||||
}
|
||||
|
||||
private bool IsOptimizablePower
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_myOperation != BinaryArithmeticOperation.Power || !(MyRightChild is Int32LiteralElement))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Int32LiteralElement right = (Int32LiteralElement)MyRightChild;
|
||||
|
||||
return right?.Value >= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user