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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
160
ExpressionElements/Base/Binary.cs
Normal file
160
ExpressionElements/Base/Binary.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection.Emit;
|
||||
using System.Reflection;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
namespace Flee.ExpressionElements.Base
|
||||
{
|
||||
[Obsolete("Base class for expression elements that operate on two child elements")]
|
||||
internal abstract class BinaryExpressionElement : ExpressionElement
|
||||
{
|
||||
|
||||
protected ExpressionElement MyLeftChild;
|
||||
protected ExpressionElement MyRightChild;
|
||||
private Type _myResultType;
|
||||
|
||||
protected BinaryExpressionElement()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a list of binary elements into a binary tree
|
||||
/// </summary>
|
||||
/// <param name="childValues"></param>
|
||||
/// <param name="elementType"></param>
|
||||
/// <returns></returns>
|
||||
public static BinaryExpressionElement CreateElement(IList childValues, Type elementType)
|
||||
{
|
||||
BinaryExpressionElement firstElement = (BinaryExpressionElement)Activator.CreateInstance(elementType);
|
||||
firstElement.Configure((ExpressionElement)childValues[0], (ExpressionElement)childValues[2], childValues[1]);
|
||||
|
||||
BinaryExpressionElement lastElement = firstElement;
|
||||
|
||||
for (int i = 3; i <= childValues.Count - 1; i += 2)
|
||||
{
|
||||
BinaryExpressionElement element = (BinaryExpressionElement)Activator.CreateInstance(elementType);
|
||||
element.Configure(lastElement, (ExpressionElement)childValues[i + 1], childValues[i]);
|
||||
lastElement = element;
|
||||
}
|
||||
|
||||
return lastElement;
|
||||
}
|
||||
|
||||
protected abstract void GetOperation(object operation);
|
||||
|
||||
protected void ValidateInternal(object op)
|
||||
{
|
||||
_myResultType = this.GetResultType(MyLeftChild.ResultType, MyRightChild.ResultType);
|
||||
|
||||
if (_myResultType == null)
|
||||
{
|
||||
this.ThrowOperandTypeMismatch(op, MyLeftChild.ResultType, MyRightChild.ResultType);
|
||||
}
|
||||
}
|
||||
|
||||
protected MethodInfo GetOverloadedBinaryOperator(string name, object operation)
|
||||
{
|
||||
Type leftType = MyLeftChild.ResultType;
|
||||
Type rightType = MyRightChild.ResultType;
|
||||
BinaryOperatorBinder binder = new BinaryOperatorBinder(leftType, rightType);
|
||||
|
||||
// If both arguments are of the same type, pick either as the owner type
|
||||
if (object.ReferenceEquals(leftType, rightType))
|
||||
{
|
||||
return Utility.GetOverloadedOperator(name, leftType, binder, leftType, rightType);
|
||||
}
|
||||
|
||||
// Get the operator for both types
|
||||
MethodInfo leftMethod = default(MethodInfo);
|
||||
MethodInfo rightMethod = default(MethodInfo);
|
||||
leftMethod = Utility.GetOverloadedOperator(name, leftType, binder, leftType, rightType);
|
||||
rightMethod = Utility.GetOverloadedOperator(name, rightType, binder, leftType, rightType);
|
||||
|
||||
// Pick the right one
|
||||
if (leftMethod == null & rightMethod == null)
|
||||
{
|
||||
// No operator defined for either
|
||||
return null;
|
||||
}
|
||||
else if (leftMethod == null)
|
||||
{
|
||||
return rightMethod;
|
||||
}
|
||||
else if (rightMethod == null)
|
||||
{
|
||||
return leftMethod;
|
||||
}
|
||||
else if (object.ReferenceEquals(leftMethod, rightMethod))
|
||||
{
|
||||
// same operator for both (most likely defined in a common base class)
|
||||
return leftMethod;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ambiguous call
|
||||
base.ThrowAmbiguousCallException(leftType, rightType, operation);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected void EmitOverloadedOperatorCall(MethodInfo method, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
ParameterInfo[] @params = method.GetParameters();
|
||||
ParameterInfo pLeft = @params[0];
|
||||
ParameterInfo pRight = @params[1];
|
||||
|
||||
EmitChildWithConvert(MyLeftChild, pLeft.ParameterType, ilg, services);
|
||||
EmitChildWithConvert(MyRightChild, pRight.ParameterType, ilg, services);
|
||||
ilg.Emit(OpCodes.Call, method);
|
||||
}
|
||||
|
||||
protected void ThrowOperandTypeMismatch(object operation, Type leftType, Type rightType)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.OperationNotDefinedForTypes, CompileExceptionReason.TypeMismatch, operation, leftType.Name, rightType.Name);
|
||||
}
|
||||
|
||||
protected abstract Type GetResultType(Type leftType, Type rightType);
|
||||
|
||||
protected static void EmitChildWithConvert(ExpressionElement child, Type resultType, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
child.Emit(ilg, services);
|
||||
bool converted = ImplicitConverter.EmitImplicitConvert(child.ResultType, resultType, ilg);
|
||||
Debug.Assert(converted, "convert failed");
|
||||
}
|
||||
|
||||
protected bool AreBothChildrenOfType(Type target)
|
||||
{
|
||||
return IsChildOfType(MyLeftChild, target) & IsChildOfType(MyRightChild, target);
|
||||
}
|
||||
|
||||
protected bool IsEitherChildOfType(Type target)
|
||||
{
|
||||
return IsChildOfType(MyLeftChild, target) || IsChildOfType(MyRightChild, target);
|
||||
}
|
||||
|
||||
protected static bool IsChildOfType(ExpressionElement child, Type t)
|
||||
{
|
||||
return object.ReferenceEquals(child.ResultType, t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the left and right operands, get the operation, and get the result type
|
||||
/// </summary>
|
||||
/// <param name="leftChild"></param>
|
||||
/// <param name="rightChild"></param>
|
||||
/// <param name="op"></param>
|
||||
private void Configure(ExpressionElement leftChild, ExpressionElement rightChild, object op)
|
||||
{
|
||||
MyLeftChild = leftChild;
|
||||
MyRightChild = rightChild;
|
||||
this.GetOperation(op);
|
||||
|
||||
this.ValidateInternal(op);
|
||||
}
|
||||
|
||||
public sealed override System.Type ResultType => _myResultType;
|
||||
}
|
||||
}
|
55
ExpressionElements/Base/ExpressionElement.cs
Normal file
55
ExpressionElements/Base/ExpressionElement.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System.Diagnostics;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
namespace Flee.ExpressionElements.Base
|
||||
{
|
||||
internal abstract class ExpressionElement
|
||||
{
|
||||
internal ExpressionElement()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// // All expression elements must be able to emit their IL
|
||||
/// </summary>
|
||||
/// <param name="ilg"></param>
|
||||
/// <param name="services"></param>
|
||||
public abstract void Emit(FleeILGenerator ilg, IServiceProvider services);
|
||||
/// <summary>
|
||||
/// All expression elements must expose the Type they evaluate to
|
||||
/// </summary>
|
||||
public abstract Type ResultType { get; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Name;
|
||||
}
|
||||
|
||||
protected void ThrowCompileException(string messageKey, CompileExceptionReason reason, params object[] arguments)
|
||||
{
|
||||
string messageTemplate = FleeResourceManager.Instance.GetCompileErrorString(messageKey);
|
||||
string message = string.Format(messageTemplate, arguments);
|
||||
message = string.Concat(this.Name, ": ", message);
|
||||
throw new ExpressionCompileException(message, reason);
|
||||
}
|
||||
|
||||
protected void ThrowAmbiguousCallException(Type leftType, Type rightType, object operation)
|
||||
{
|
||||
this.ThrowCompileException(CompileErrorResourceKeys.AmbiguousOverloadedOperator, CompileExceptionReason.AmbiguousMatch, leftType.Name, rightType.Name, operation);
|
||||
}
|
||||
|
||||
|
||||
protected string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
string key = this.GetType().Name;
|
||||
string value = FleeResourceManager.Instance.GetElementNameString(key);
|
||||
Debug.Assert(value != null, $"Element name for '{key}' not in resource file");
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
118
ExpressionElements/Base/Literals/Integral.cs
Normal file
118
ExpressionElements/Base/Literals/Integral.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using Flee.ExpressionElements.Literals.Integral;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.Base.Literals
|
||||
{
|
||||
internal abstract class IntegralLiteralElement : LiteralElement
|
||||
{
|
||||
protected IntegralLiteralElement()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to find the first type of integer that a number can fit into
|
||||
/// </summary>
|
||||
/// <param name="image"></param>
|
||||
/// <param name="isHex"></param>
|
||||
/// <param name="negated"></param>
|
||||
/// <param name="services"></param>
|
||||
/// <returns></returns>
|
||||
public static LiteralElement Create(string image, bool isHex, bool negated, IServiceProvider services)
|
||||
{
|
||||
StringComparison comparison = StringComparison.OrdinalIgnoreCase;
|
||||
|
||||
if (isHex == false)
|
||||
{
|
||||
// Create a real element if required
|
||||
LiteralElement realElement = RealLiteralElement.CreateFromInteger(image, services);
|
||||
|
||||
if ((realElement != null))
|
||||
{
|
||||
return realElement;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasUSuffix = image.EndsWith("u", comparison) & !image.EndsWith("lu", comparison);
|
||||
bool hasLSuffix = image.EndsWith("l", comparison) & !image.EndsWith("ul", comparison);
|
||||
bool hasUlSuffix = image.EndsWith("ul", comparison) | image.EndsWith("lu", comparison);
|
||||
bool hasSuffix = hasUSuffix | hasLSuffix | hasUlSuffix;
|
||||
|
||||
LiteralElement constant = default(LiteralElement);
|
||||
System.Globalization.NumberStyles numStyles = NumberStyles.Integer;
|
||||
|
||||
if (isHex == true)
|
||||
{
|
||||
numStyles = NumberStyles.AllowHexSpecifier;
|
||||
image = image.Remove(0, 2);
|
||||
}
|
||||
|
||||
if (hasSuffix == false)
|
||||
{
|
||||
// If the literal has no suffix, it has the first of these types in which its value can be represented: int, uint, long, ulong.
|
||||
constant = Int32LiteralElement.TryCreate(image, isHex, negated);
|
||||
|
||||
if ((constant != null))
|
||||
{
|
||||
return constant;
|
||||
}
|
||||
|
||||
constant = UInt32LiteralElement.TryCreate(image, numStyles);
|
||||
|
||||
if ((constant != null))
|
||||
{
|
||||
return constant;
|
||||
}
|
||||
|
||||
constant = Int64LiteralElement.TryCreate(image, isHex, negated);
|
||||
|
||||
if ((constant != null))
|
||||
{
|
||||
return constant;
|
||||
}
|
||||
|
||||
return new UInt64LiteralElement(image, numStyles);
|
||||
}
|
||||
else if (hasUSuffix == true)
|
||||
{
|
||||
image = image.Remove(image.Length - 1);
|
||||
// If the literal is suffixed by U or u, it has the first of these types in which its value can be represented: uint, ulong.
|
||||
|
||||
constant = UInt32LiteralElement.TryCreate(image, numStyles);
|
||||
|
||||
if ((constant != null))
|
||||
{
|
||||
return constant;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new UInt64LiteralElement(image, numStyles);
|
||||
}
|
||||
}
|
||||
else if (hasLSuffix == true)
|
||||
{
|
||||
// If the literal is suffixed by L or l, it has the first of these types in which its value can be represented: long, ulong.
|
||||
image = image.Remove(image.Length - 1);
|
||||
|
||||
constant = Int64LiteralElement.TryCreate(image, isHex, negated);
|
||||
|
||||
if ((constant != null))
|
||||
{
|
||||
return constant;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new UInt64LiteralElement(image, numStyles);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the literal is suffixed by UL, Ul, uL, ul, LU, Lu, lU, or lu, it is of type ulong.
|
||||
Debug.Assert(hasUlSuffix == true, "expecting ul suffix");
|
||||
image = image.Remove(image.Length - 2);
|
||||
return new UInt64LiteralElement(image, numStyles);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
107
ExpressionElements/Base/Literals/LiteralElement.cs
Normal file
107
ExpressionElements/Base/Literals/LiteralElement.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection.Emit;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.Base.Literals
|
||||
{
|
||||
internal abstract class LiteralElement : ExpressionElement
|
||||
{
|
||||
protected void OnParseOverflow(string image)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.ValueNotRepresentableInType, CompileExceptionReason.ConstantOverflow, image, this.ResultType.Name);
|
||||
}
|
||||
|
||||
public static void EmitLoad(Int32 value, FleeILGenerator ilg)
|
||||
{
|
||||
if (value >= -1 & value <= 8)
|
||||
{
|
||||
EmitSuperShort(value, ilg);
|
||||
}
|
||||
else if (value >= sbyte.MinValue & value <= sbyte.MaxValue)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldc_I4_S, Convert.ToSByte(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldc_I4, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void EmitLoad(Int64 value, FleeILGenerator ilg)
|
||||
{
|
||||
if (value >= Int32.MinValue & value <= Int32.MaxValue)
|
||||
{
|
||||
EmitLoad(Convert.ToInt32(value), ilg);
|
||||
ilg.Emit(OpCodes.Conv_I8);
|
||||
}
|
||||
else if (value >= 0 & value <= UInt32.MaxValue)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldc_I4, unchecked((int)Convert.ToUInt32(value)));
|
||||
ilg.Emit(OpCodes.Conv_U8);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldc_I8, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void EmitLoad(bool value, FleeILGenerator ilg)
|
||||
{
|
||||
if (value == true)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldc_I4_1);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldc_I4_0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitSuperShort(Int32 value, FleeILGenerator ilg)
|
||||
{
|
||||
OpCode ldcOpcode = default(OpCode);
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case 0:
|
||||
ldcOpcode = OpCodes.Ldc_I4_0;
|
||||
break;
|
||||
case 1:
|
||||
ldcOpcode = OpCodes.Ldc_I4_1;
|
||||
break;
|
||||
case 2:
|
||||
ldcOpcode = OpCodes.Ldc_I4_2;
|
||||
break;
|
||||
case 3:
|
||||
ldcOpcode = OpCodes.Ldc_I4_3;
|
||||
break;
|
||||
case 4:
|
||||
ldcOpcode = OpCodes.Ldc_I4_4;
|
||||
break;
|
||||
case 5:
|
||||
ldcOpcode = OpCodes.Ldc_I4_5;
|
||||
break;
|
||||
case 6:
|
||||
ldcOpcode = OpCodes.Ldc_I4_6;
|
||||
break;
|
||||
case 7:
|
||||
ldcOpcode = OpCodes.Ldc_I4_7;
|
||||
break;
|
||||
case 8:
|
||||
ldcOpcode = OpCodes.Ldc_I4_8;
|
||||
break;
|
||||
case -1:
|
||||
ldcOpcode = OpCodes.Ldc_I4_M1;
|
||||
break;
|
||||
default:
|
||||
Debug.Assert(false, "value out of range");
|
||||
break;
|
||||
}
|
||||
|
||||
ilg.Emit(ldcOpcode);
|
||||
}
|
||||
}
|
||||
}
|
130
ExpressionElements/Base/Literals/Real.cs
Normal file
130
ExpressionElements/Base/Literals/Real.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using System.Diagnostics;
|
||||
using Flee.ExpressionElements.Literals.Real;
|
||||
using Flee.PublicTypes;
|
||||
|
||||
namespace Flee.ExpressionElements.Base.Literals
|
||||
{
|
||||
internal abstract class RealLiteralElement : LiteralElement
|
||||
{
|
||||
protected RealLiteralElement()
|
||||
{
|
||||
}
|
||||
|
||||
public static LiteralElement CreateFromInteger(string image, IServiceProvider services)
|
||||
{
|
||||
LiteralElement element = default(LiteralElement);
|
||||
|
||||
element = CreateSingle(image, services);
|
||||
|
||||
if ((element != null))
|
||||
{
|
||||
return element;
|
||||
}
|
||||
|
||||
element = CreateDecimal(image, services);
|
||||
|
||||
if ((element != null))
|
||||
{
|
||||
return element;
|
||||
}
|
||||
|
||||
ExpressionOptions options = (ExpressionOptions)services.GetService(typeof(ExpressionOptions));
|
||||
|
||||
// Convert to a double if option is set
|
||||
if (options.IntegersAsDoubles == true)
|
||||
{
|
||||
return DoubleLiteralElement.Parse(image, services);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static LiteralElement Create(string image, IServiceProvider services)
|
||||
{
|
||||
LiteralElement element = default(LiteralElement);
|
||||
|
||||
element = CreateSingle(image, services);
|
||||
|
||||
if ((element != null))
|
||||
{
|
||||
return element;
|
||||
}
|
||||
|
||||
element = CreateDecimal(image, services);
|
||||
|
||||
if ((element != null))
|
||||
{
|
||||
return element;
|
||||
}
|
||||
|
||||
element = CreateDouble(image, services);
|
||||
|
||||
if ((element != null))
|
||||
{
|
||||
return element;
|
||||
}
|
||||
|
||||
element = CreateImplicitReal(image, services);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
private static LiteralElement CreateImplicitReal(string image, IServiceProvider services)
|
||||
{
|
||||
ExpressionOptions options = (ExpressionOptions)services.GetService(typeof(ExpressionOptions));
|
||||
RealLiteralDataType realType = options.RealLiteralDataType;
|
||||
|
||||
switch (realType)
|
||||
{
|
||||
case RealLiteralDataType.Double:
|
||||
return DoubleLiteralElement.Parse(image, services);
|
||||
case RealLiteralDataType.Single:
|
||||
return SingleLiteralElement.Parse(image, services);
|
||||
case RealLiteralDataType.Decimal:
|
||||
return DecimalLiteralElement.Parse(image, services);
|
||||
default:
|
||||
Debug.Fail("Unknown value");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static DoubleLiteralElement CreateDouble(string image, IServiceProvider services)
|
||||
{
|
||||
if (image.EndsWith("d", StringComparison.OrdinalIgnoreCase) == true)
|
||||
{
|
||||
image = image.Remove(image.Length - 1);
|
||||
return DoubleLiteralElement.Parse(image, services);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static SingleLiteralElement CreateSingle(string image, IServiceProvider services)
|
||||
{
|
||||
if (image.EndsWith("f", StringComparison.OrdinalIgnoreCase) == true)
|
||||
{
|
||||
image = image.Remove(image.Length - 1);
|
||||
return SingleLiteralElement.Parse(image, services);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static DecimalLiteralElement CreateDecimal(string image, IServiceProvider services)
|
||||
{
|
||||
if (image.EndsWith("m", StringComparison.OrdinalIgnoreCase) == true)
|
||||
{
|
||||
image = image.Remove(image.Length - 1);
|
||||
return DecimalLiteralElement.Parse(image, services);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
358
ExpressionElements/Base/Member.cs
Normal file
358
ExpressionElements/Base/Member.cs
Normal file
@@ -0,0 +1,358 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
namespace Flee.ExpressionElements.Base
|
||||
{
|
||||
internal abstract class MemberElement : ExpressionElement
|
||||
{
|
||||
protected string MyName;
|
||||
protected MemberElement MyPrevious;
|
||||
protected MemberElement MyNext;
|
||||
protected IServiceProvider MyServices;
|
||||
protected ExpressionOptions MyOptions;
|
||||
protected ExpressionContext MyContext;
|
||||
protected ImportBase MyImport;
|
||||
|
||||
public const BindingFlags BindFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
|
||||
|
||||
protected MemberElement()
|
||||
{
|
||||
}
|
||||
|
||||
public void Link(MemberElement nextElement)
|
||||
{
|
||||
MyNext = nextElement;
|
||||
if ((nextElement != null))
|
||||
{
|
||||
nextElement.MyPrevious = this;
|
||||
}
|
||||
}
|
||||
|
||||
public void Resolve(IServiceProvider services)
|
||||
{
|
||||
MyServices = services;
|
||||
MyOptions = (ExpressionOptions)services.GetService(typeof(ExpressionOptions));
|
||||
MyContext = (ExpressionContext)services.GetService(typeof(ExpressionContext));
|
||||
this.ResolveInternal();
|
||||
this.Validate();
|
||||
}
|
||||
|
||||
public void SetImport(ImportBase import)
|
||||
{
|
||||
MyImport = import;
|
||||
}
|
||||
|
||||
protected abstract void ResolveInternal();
|
||||
public abstract bool IsStatic { get; }
|
||||
public abstract bool IsExtensionMethod { get; }
|
||||
protected abstract bool IsPublic { get; }
|
||||
|
||||
protected virtual void Validate()
|
||||
{
|
||||
if (MyPrevious == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.IsStatic == true && this.SupportsStatic == false && IsExtensionMethod == false)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.StaticMemberCannotBeAccessedWithInstanceReference, CompileExceptionReason.TypeMismatch, MyName);
|
||||
}
|
||||
else if (this.IsStatic == false && this.SupportsInstance == false)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.ReferenceToNonSharedMemberRequiresObjectReference, CompileExceptionReason.TypeMismatch, MyName);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
if ((MyPrevious != null))
|
||||
{
|
||||
MyPrevious.Emit(ilg, services);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void EmitLoadVariables(FleeILGenerator ilg)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldarg_2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles a call emit for static, instance methods of reference/value types
|
||||
/// </summary>
|
||||
/// <param name="mi"></param>
|
||||
/// <param name="ilg"></param>
|
||||
protected void EmitMethodCall(MethodInfo mi, FleeILGenerator ilg)
|
||||
{
|
||||
EmitMethodCall(this.ResultType, this.NextRequiresAddress, mi, ilg);
|
||||
}
|
||||
|
||||
protected static void EmitMethodCall(Type resultType, bool nextRequiresAddress, MethodInfo mi, FleeILGenerator ilg)
|
||||
{
|
||||
if (mi.GetType().IsValueType == false)
|
||||
{
|
||||
EmitReferenceTypeMethodCall(mi, ilg);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitValueTypeMethodCall(mi, ilg);
|
||||
}
|
||||
|
||||
if (resultType.IsValueType & nextRequiresAddress)
|
||||
{
|
||||
EmitValueTypeLoadAddress(ilg, resultType);
|
||||
}
|
||||
}
|
||||
|
||||
protected static bool IsGetTypeMethod(MethodInfo mi)
|
||||
{
|
||||
MethodInfo miGetType = typeof(object).GetMethod("gettype", BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
|
||||
return mi.MethodHandle.Equals(miGetType.MethodHandle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit a function call for a value type
|
||||
/// </summary>
|
||||
/// <param name="mi"></param>
|
||||
/// <param name="ilg"></param>
|
||||
private static void EmitValueTypeMethodCall(MethodInfo mi, FleeILGenerator ilg)
|
||||
{
|
||||
if (mi.IsStatic == true)
|
||||
{
|
||||
ilg.Emit(OpCodes.Call, mi);
|
||||
}
|
||||
else if ((!object.ReferenceEquals(mi.DeclaringType, mi.ReflectedType)))
|
||||
{
|
||||
// Method is not defined on the value type
|
||||
|
||||
if (IsGetTypeMethod(mi) == true)
|
||||
{
|
||||
// Special GetType method which requires a box
|
||||
ilg.Emit(OpCodes.Box, mi.ReflectedType);
|
||||
ilg.Emit(OpCodes.Call, mi);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Equals, GetHashCode, and ToString methods on the base
|
||||
ilg.Emit(OpCodes.Constrained, mi.ReflectedType);
|
||||
ilg.Emit(OpCodes.Callvirt, mi);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Call value type's implementation
|
||||
ilg.Emit(OpCodes.Call, mi);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitReferenceTypeMethodCall(MethodInfo mi, FleeILGenerator ilg)
|
||||
{
|
||||
if (mi.IsStatic == true)
|
||||
{
|
||||
ilg.Emit(OpCodes.Call, mi);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Callvirt, mi);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void EmitValueTypeLoadAddress(FleeILGenerator ilg, Type targetType)
|
||||
{
|
||||
int index = ilg.GetTempLocalIndex(targetType);
|
||||
Utility.EmitStoreLocal(ilg, index);
|
||||
ilg.Emit(OpCodes.Ldloca_S, Convert.ToByte(index));
|
||||
}
|
||||
|
||||
protected void EmitLoadOwner(FleeILGenerator ilg)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldarg_0);
|
||||
|
||||
Type ownerType = MyOptions.OwnerType;
|
||||
|
||||
if (ownerType.IsValueType == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ilg.Emit(OpCodes.Unbox, ownerType);
|
||||
ilg.Emit(OpCodes.Ldobj, ownerType);
|
||||
|
||||
// Emit usual stuff for value types but use the owner type as the target
|
||||
if (this.RequiresAddress == true)
|
||||
{
|
||||
EmitValueTypeLoadAddress(ilg, ownerType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if a field, property, or method is public
|
||||
/// </summary>
|
||||
/// <param name="member"></param>
|
||||
/// <returns></returns>
|
||||
private static bool IsMemberPublic(MemberInfo member)
|
||||
{
|
||||
FieldInfo fi = member as FieldInfo;
|
||||
|
||||
if ((fi != null))
|
||||
{
|
||||
return fi.IsPublic;
|
||||
}
|
||||
|
||||
PropertyInfo pi = member as PropertyInfo;
|
||||
|
||||
if ((pi != null))
|
||||
{
|
||||
MethodInfo pmi = pi.GetGetMethod(true);
|
||||
return pmi.IsPublic;
|
||||
}
|
||||
|
||||
MethodInfo mi = member as MethodInfo;
|
||||
|
||||
if ((mi != null))
|
||||
{
|
||||
return mi.IsPublic;
|
||||
}
|
||||
|
||||
Debug.Assert(false, "unknown member type");
|
||||
return false;
|
||||
}
|
||||
|
||||
protected MemberInfo[] GetAccessibleMembers(MemberInfo[] members)
|
||||
{
|
||||
List<MemberInfo> accessible = new List<MemberInfo>();
|
||||
|
||||
// Keep all members that are accessible
|
||||
foreach (MemberInfo mi in members)
|
||||
{
|
||||
if (this.IsMemberAccessible(mi) == true)
|
||||
{
|
||||
accessible.Add(mi);
|
||||
}
|
||||
}
|
||||
|
||||
return accessible.ToArray();
|
||||
}
|
||||
|
||||
protected static bool IsOwnerMemberAccessible(MemberInfo member, ExpressionOptions options)
|
||||
{
|
||||
bool accessAllowed = false;
|
||||
|
||||
// Get the allowed access defined in the options
|
||||
if (IsMemberPublic(member) == true)
|
||||
{
|
||||
accessAllowed = (options.OwnerMemberAccess & BindingFlags.Public) != 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
accessAllowed = (options.OwnerMemberAccess & BindingFlags.NonPublic) != 0;
|
||||
}
|
||||
|
||||
// See if the member has our access attribute defined
|
||||
ExpressionOwnerMemberAccessAttribute attr = (ExpressionOwnerMemberAccessAttribute)Attribute.GetCustomAttribute(member, typeof(ExpressionOwnerMemberAccessAttribute));
|
||||
|
||||
if (attr == null)
|
||||
{
|
||||
// No, so return the access level
|
||||
return accessAllowed;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Member has our access attribute defined; use its access value instead
|
||||
return attr.AllowAccess;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsMemberAccessible(MemberInfo member)
|
||||
{
|
||||
if (MyOptions.IsOwnerType(member.ReflectedType) == true)
|
||||
{
|
||||
return IsOwnerMemberAccessible(member, MyOptions);
|
||||
}
|
||||
else
|
||||
{
|
||||
return IsMemberPublic(member);
|
||||
}
|
||||
}
|
||||
|
||||
protected MemberInfo[] GetMembers(MemberTypes targets)
|
||||
{
|
||||
if (MyPrevious == null)
|
||||
{
|
||||
// Do we have a namespace?
|
||||
if (MyImport == null)
|
||||
{
|
||||
// Get all members in the default namespace
|
||||
return this.GetDefaultNamespaceMembers(MyName, targets);
|
||||
}
|
||||
else
|
||||
{
|
||||
return MyImport.FindMembers(MyName, targets);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are not the first element; find all members with our name on the type of the previous member
|
||||
// We are not the first element; find all members with our name on the type of the previous member
|
||||
var foundMembers = MyPrevious.TargetType.FindMembers(targets, BindFlags, MyOptions.MemberFilter, MyName);
|
||||
var importedMembers = MyContext.Imports.RootImport.FindMembers(MyName, targets);
|
||||
if (foundMembers.Length == 0) //If no members found search in root import
|
||||
return importedMembers;
|
||||
|
||||
MemberInfo[] allMembers = new MemberInfo[foundMembers.Length + importedMembers.Length];
|
||||
foundMembers.CopyTo(allMembers, 0);
|
||||
importedMembers.CopyTo(allMembers, foundMembers.Length);
|
||||
return allMembers;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find members in the default namespace
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="memberType"></param>
|
||||
/// <returns></returns>
|
||||
protected MemberInfo[] GetDefaultNamespaceMembers(string name, MemberTypes memberType)
|
||||
{
|
||||
// Search the owner first
|
||||
MemberInfo[] members = MyContext.Imports.FindOwnerMembers(name, memberType);
|
||||
|
||||
// Keep only the accessible members
|
||||
members = this.GetAccessibleMembers(members);
|
||||
|
||||
//Also search imports
|
||||
var importedMembers = MyContext.Imports.RootImport.FindMembers(name, memberType);
|
||||
|
||||
//if no members, just return imports
|
||||
if (members.Length == 0)
|
||||
return importedMembers;
|
||||
|
||||
//combine members and imports
|
||||
MemberInfo[] allMembers = new MemberInfo[members.Length + importedMembers.Length];
|
||||
members.CopyTo(allMembers, 0);
|
||||
importedMembers.CopyTo(allMembers, members.Length);
|
||||
return allMembers;
|
||||
}
|
||||
|
||||
protected static bool IsElementPublic(MemberElement e)
|
||||
{
|
||||
return e.IsPublic;
|
||||
}
|
||||
|
||||
public string MemberName => MyName;
|
||||
|
||||
protected bool NextRequiresAddress => MyNext != null && MyNext.RequiresAddress;
|
||||
|
||||
protected virtual bool RequiresAddress => false;
|
||||
|
||||
protected virtual bool SupportsInstance => true;
|
||||
|
||||
protected virtual bool SupportsStatic => false;
|
||||
|
||||
public System.Type TargetType => this.ResultType;
|
||||
}
|
||||
}
|
28
ExpressionElements/Base/Unary.cs
Normal file
28
ExpressionElements/Base/Unary.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
namespace Flee.ExpressionElements.Base
|
||||
{
|
||||
internal abstract class UnaryElement : ExpressionElement
|
||||
{
|
||||
|
||||
protected ExpressionElement MyChild;
|
||||
|
||||
private Type _myResultType;
|
||||
public void SetChild(ExpressionElement child)
|
||||
{
|
||||
MyChild = child;
|
||||
_myResultType = this.GetResultType(child.ResultType);
|
||||
|
||||
if (_myResultType == null)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.OperationNotDefinedForType, CompileExceptionReason.TypeMismatch, MyChild.ResultType.Name);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Type GetResultType(Type childType);
|
||||
|
||||
public override System.Type ResultType => _myResultType;
|
||||
}
|
||||
|
||||
}
|
513
ExpressionElements/Cast.cs
Normal file
513
ExpressionElements/Cast.cs
Normal file
@@ -0,0 +1,513 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection.Emit;
|
||||
using System.Reflection;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
namespace Flee.ExpressionElements
|
||||
{
|
||||
internal class CastElement : ExpressionElement
|
||||
{
|
||||
private readonly ExpressionElement _myCastExpression;
|
||||
private readonly Type _myDestType;
|
||||
public CastElement(ExpressionElement castExpression, string[] destTypeParts, bool isArray, IServiceProvider services)
|
||||
{
|
||||
_myCastExpression = castExpression;
|
||||
|
||||
_myDestType = GetDestType(destTypeParts, services);
|
||||
|
||||
if (_myDestType == null)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.CouldNotResolveType, CompileExceptionReason.UndefinedName, GetDestTypeString(destTypeParts, isArray));
|
||||
}
|
||||
|
||||
if (isArray == true)
|
||||
{
|
||||
_myDestType = _myDestType.MakeArrayType();
|
||||
}
|
||||
|
||||
if (this.IsValidCast(_myCastExpression.ResultType, _myDestType) == false)
|
||||
{
|
||||
this.ThrowInvalidCastException();
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetDestTypeString(string[] parts, bool isArray)
|
||||
{
|
||||
string s = string.Join(".", parts);
|
||||
|
||||
if (isArray == true)
|
||||
{
|
||||
s = s + "[]";
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolve the type we are casting to
|
||||
/// </summary>
|
||||
/// <param name="destTypeParts"></param>
|
||||
/// <param name="services"></param>
|
||||
/// <returns></returns>
|
||||
private static Type GetDestType(string[] destTypeParts, IServiceProvider services)
|
||||
{
|
||||
ExpressionContext context = (ExpressionContext)services.GetService(typeof(ExpressionContext));
|
||||
|
||||
Type t = null;
|
||||
|
||||
// Try to find a builtin type with the name
|
||||
if (destTypeParts.Length == 1)
|
||||
{
|
||||
t = ExpressionImports.GetBuiltinType(destTypeParts[0]);
|
||||
}
|
||||
|
||||
if ((t != null))
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
// Try to find the type in an import
|
||||
t = context.Imports.FindType(destTypeParts);
|
||||
|
||||
if ((t != null))
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool IsValidCast(Type sourceType, Type destType)
|
||||
{
|
||||
if (object.ReferenceEquals(sourceType, destType))
|
||||
{
|
||||
// Identity cast always succeeds
|
||||
return true;
|
||||
}
|
||||
else if (destType.IsAssignableFrom(sourceType) == true)
|
||||
{
|
||||
// Cast is already implicitly valid
|
||||
return true;
|
||||
}
|
||||
else if (ImplicitConverter.EmitImplicitConvert(sourceType, destType, null) == true)
|
||||
{
|
||||
// Cast is already implicitly valid
|
||||
return true;
|
||||
}
|
||||
else if (IsCastableNumericType(sourceType) & IsCastableNumericType(destType))
|
||||
{
|
||||
// Explicit cast of numeric types always succeeds
|
||||
return true;
|
||||
}
|
||||
else if (sourceType.IsEnum == true | destType.IsEnum == true)
|
||||
{
|
||||
return this.IsValidExplicitEnumCast(sourceType, destType);
|
||||
}
|
||||
else if ((this.GetExplictOverloadedOperator(sourceType, destType) != null))
|
||||
{
|
||||
// Overloaded explict cast exists
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sourceType.IsValueType == true)
|
||||
{
|
||||
// If we get here then the cast always fails since we are either casting one value type to another
|
||||
// or a value type to an invalid reference type
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (destType.IsValueType == true)
|
||||
{
|
||||
// Reference type to value type
|
||||
// Can only succeed if the reference type is a base of the value type or
|
||||
// it is one of the interfaces the value type implements
|
||||
Type[] interfaces = destType.GetInterfaces();
|
||||
return IsBaseType(destType, sourceType) == true | System.Array.IndexOf(interfaces, sourceType) != -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reference type to reference type
|
||||
return this.IsValidExplicitReferenceCast(sourceType, destType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MethodInfo GetExplictOverloadedOperator(Type sourceType, Type destType)
|
||||
{
|
||||
ExplicitOperatorMethodBinder binder = new ExplicitOperatorMethodBinder(destType, sourceType);
|
||||
|
||||
// Look for an operator on the source type and dest types
|
||||
MethodInfo miSource = Utility.GetOverloadedOperator("Explicit", sourceType, binder, sourceType);
|
||||
MethodInfo miDest = Utility.GetOverloadedOperator("Explicit", destType, binder, sourceType);
|
||||
|
||||
if (miSource == null & miDest == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (miSource == null)
|
||||
{
|
||||
return miDest;
|
||||
}
|
||||
else if (miDest == null)
|
||||
{
|
||||
return miSource;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ThrowAmbiguousCallException(sourceType, destType, "Explicit");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsValidExplicitEnumCast(Type sourceType, Type destType)
|
||||
{
|
||||
sourceType = GetUnderlyingEnumType(sourceType);
|
||||
destType = GetUnderlyingEnumType(destType);
|
||||
return this.IsValidCast(sourceType, destType);
|
||||
}
|
||||
|
||||
private bool IsValidExplicitReferenceCast(Type sourceType, Type destType)
|
||||
{
|
||||
Debug.Assert(sourceType.IsValueType == false & destType.IsValueType == false, "expecting reference types");
|
||||
|
||||
if (object.ReferenceEquals(sourceType, typeof(object)))
|
||||
{
|
||||
// From object to any other reference-type
|
||||
return true;
|
||||
}
|
||||
else if (sourceType.IsArray == true & destType.IsArray == true)
|
||||
{
|
||||
// From an array-type S with an element type SE to an array-type T with an element type TE,
|
||||
// provided all of the following are true:
|
||||
|
||||
// S and T have the same number of dimensions
|
||||
if (sourceType.GetArrayRank() != destType.GetArrayRank())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Type SE = sourceType.GetElementType();
|
||||
Type TE = destType.GetElementType();
|
||||
|
||||
// Both SE and TE are reference-types
|
||||
if (SE.IsValueType == true | TE.IsValueType == true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// An explicit reference conversion exists from SE to TE
|
||||
return this.IsValidExplicitReferenceCast(SE, TE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sourceType.IsClass == true & destType.IsClass == true)
|
||||
{
|
||||
// From any class-type S to any class-type T, provided S is a base class of T
|
||||
return IsBaseType(destType, sourceType);
|
||||
}
|
||||
else if (sourceType.IsClass == true & destType.IsInterface == true)
|
||||
{
|
||||
// From any class-type S to any interface-type T, provided S is not sealed and provided S does not implement T
|
||||
return sourceType.IsSealed == false & ImplementsInterface(sourceType, destType) == false;
|
||||
}
|
||||
else if (sourceType.IsInterface == true & destType.IsClass == true)
|
||||
{
|
||||
// From any interface-type S to any class-type T, provided T is not sealed or provided T implements S.
|
||||
return destType.IsSealed == false | ImplementsInterface(destType, sourceType) == true;
|
||||
}
|
||||
else if (sourceType.IsInterface == true & destType.IsInterface == true)
|
||||
{
|
||||
// From any interface-type S to any interface-type T, provided S is not derived from T
|
||||
return ImplementsInterface(sourceType, destType) == false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(false, "unknown explicit cast");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsBaseType(Type target, Type potentialBase)
|
||||
{
|
||||
Type current = target;
|
||||
while ((current != null))
|
||||
{
|
||||
if (object.ReferenceEquals(current, potentialBase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
current = current.BaseType;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool ImplementsInterface(Type target, Type interfaceType)
|
||||
{
|
||||
Type[] interfaces = target.GetInterfaces();
|
||||
return System.Array.IndexOf(interfaces, interfaceType) != -1;
|
||||
}
|
||||
|
||||
private void ThrowInvalidCastException()
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.CannotConvertType, CompileExceptionReason.InvalidExplicitCast, _myCastExpression.ResultType.Name, _myDestType.Name);
|
||||
}
|
||||
|
||||
private static bool IsCastableNumericType(Type t)
|
||||
{
|
||||
return t.IsPrimitive == true & (!object.ReferenceEquals(t, typeof(bool)));
|
||||
}
|
||||
|
||||
private static Type GetUnderlyingEnumType(Type t)
|
||||
{
|
||||
if (t.IsEnum == true)
|
||||
{
|
||||
return System.Enum.GetUnderlyingType(t);
|
||||
}
|
||||
else
|
||||
{
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
_myCastExpression.Emit(ilg, services);
|
||||
|
||||
Type sourceType = _myCastExpression.ResultType;
|
||||
Type destType = _myDestType;
|
||||
|
||||
this.EmitCast(ilg, sourceType, destType, services);
|
||||
}
|
||||
|
||||
private void EmitCast(FleeILGenerator ilg, Type sourceType, Type destType, IServiceProvider services)
|
||||
{
|
||||
MethodInfo explicitOperator = this.GetExplictOverloadedOperator(sourceType, destType);
|
||||
|
||||
if (object.ReferenceEquals(sourceType, destType))
|
||||
{
|
||||
// Identity cast; do nothing
|
||||
return;
|
||||
}
|
||||
else if ((explicitOperator != null))
|
||||
{
|
||||
ilg.Emit(OpCodes.Call, explicitOperator);
|
||||
}
|
||||
else if (sourceType.IsEnum == true | destType.IsEnum == true)
|
||||
{
|
||||
this.EmitEnumCast(ilg, sourceType, destType, services);
|
||||
}
|
||||
else if (ImplicitConverter.EmitImplicitConvert(sourceType, destType, ilg) == true)
|
||||
{
|
||||
// Implicit numeric cast; do nothing
|
||||
return;
|
||||
}
|
||||
else if (IsCastableNumericType(sourceType) & IsCastableNumericType(destType))
|
||||
{
|
||||
// Explicit numeric cast
|
||||
EmitExplicitNumericCast(ilg, sourceType, destType, services);
|
||||
}
|
||||
else if (sourceType.IsValueType == true)
|
||||
{
|
||||
Debug.Assert(destType.IsValueType == false, "expecting reference type");
|
||||
ilg.Emit(OpCodes.Box, sourceType);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (destType.IsValueType == true)
|
||||
{
|
||||
// Reference type to value type
|
||||
ilg.Emit(OpCodes.Unbox_Any, destType);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reference type to reference type
|
||||
if (destType.IsAssignableFrom(sourceType) == false)
|
||||
{
|
||||
// Only emit cast if it is an explicit cast
|
||||
ilg.Emit(OpCodes.Castclass, destType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitEnumCast(FleeILGenerator ilg, Type sourceType, Type destType, IServiceProvider services)
|
||||
{
|
||||
if (destType.IsValueType == false)
|
||||
{
|
||||
ilg.Emit(OpCodes.Box, sourceType);
|
||||
}
|
||||
else if (sourceType.IsValueType == false)
|
||||
{
|
||||
ilg.Emit(OpCodes.Unbox_Any, destType);
|
||||
}
|
||||
else
|
||||
{
|
||||
sourceType = GetUnderlyingEnumType(sourceType);
|
||||
destType = GetUnderlyingEnumType(destType);
|
||||
this.EmitCast(ilg, sourceType, destType, services);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitExplicitNumericCast(FleeILGenerator ilg, Type sourceType, Type destType, IServiceProvider services)
|
||||
{
|
||||
TypeCode desttc = Type.GetTypeCode(destType);
|
||||
TypeCode sourcetc = Type.GetTypeCode(sourceType);
|
||||
bool unsigned = IsUnsignedType(sourceType);
|
||||
ExpressionOptions options = (ExpressionOptions)services.GetService(typeof(ExpressionOptions));
|
||||
bool @checked = options.Checked;
|
||||
OpCode op = OpCodes.Nop;
|
||||
|
||||
switch (desttc)
|
||||
{
|
||||
case TypeCode.SByte:
|
||||
if (unsigned == true & @checked == true)
|
||||
{
|
||||
op = OpCodes.Conv_Ovf_I1_Un;
|
||||
}
|
||||
else if (@checked == true)
|
||||
{
|
||||
op = OpCodes.Conv_Ovf_I1;
|
||||
}
|
||||
else
|
||||
{
|
||||
op = OpCodes.Conv_I1;
|
||||
}
|
||||
break;
|
||||
case TypeCode.Byte:
|
||||
if (unsigned == true & @checked == true)
|
||||
{
|
||||
op = OpCodes.Conv_Ovf_U1_Un;
|
||||
}
|
||||
else if (@checked == true)
|
||||
{
|
||||
op = OpCodes.Conv_Ovf_U1;
|
||||
}
|
||||
else
|
||||
{
|
||||
op = OpCodes.Conv_U1;
|
||||
}
|
||||
break;
|
||||
case TypeCode.Int16:
|
||||
if (unsigned == true & @checked == true)
|
||||
{
|
||||
op = OpCodes.Conv_Ovf_I2_Un;
|
||||
}
|
||||
else if (@checked == true)
|
||||
{
|
||||
op = OpCodes.Conv_Ovf_I2;
|
||||
}
|
||||
else
|
||||
{
|
||||
op = OpCodes.Conv_I2;
|
||||
}
|
||||
break;
|
||||
case TypeCode.UInt16:
|
||||
if (unsigned == true & @checked == true)
|
||||
{
|
||||
op = OpCodes.Conv_Ovf_U2_Un;
|
||||
}
|
||||
else if (@checked == true)
|
||||
{
|
||||
op = OpCodes.Conv_Ovf_U2;
|
||||
}
|
||||
else
|
||||
{
|
||||
op = OpCodes.Conv_U2;
|
||||
}
|
||||
break;
|
||||
case TypeCode.Int32:
|
||||
if (unsigned == true & @checked == true)
|
||||
{
|
||||
op = OpCodes.Conv_Ovf_I4_Un;
|
||||
}
|
||||
else if (@checked == true)
|
||||
{
|
||||
op = OpCodes.Conv_Ovf_I4;
|
||||
}
|
||||
else if (sourcetc != TypeCode.UInt32)
|
||||
{
|
||||
// Don't need to emit a convert for this case since, to the CLR, it is the same data type
|
||||
op = OpCodes.Conv_I4;
|
||||
}
|
||||
break;
|
||||
case TypeCode.UInt32:
|
||||
if (unsigned == true & @checked == true)
|
||||
{
|
||||
op = OpCodes.Conv_Ovf_U4_Un;
|
||||
}
|
||||
else if (@checked == true)
|
||||
{
|
||||
op = OpCodes.Conv_Ovf_U4;
|
||||
}
|
||||
else if (sourcetc != TypeCode.Int32)
|
||||
{
|
||||
op = OpCodes.Conv_U4;
|
||||
}
|
||||
break;
|
||||
case TypeCode.Int64:
|
||||
if (unsigned == true & @checked == true)
|
||||
{
|
||||
op = OpCodes.Conv_Ovf_I8_Un;
|
||||
}
|
||||
else if (@checked == true)
|
||||
{
|
||||
op = OpCodes.Conv_Ovf_I8;
|
||||
}
|
||||
else if (sourcetc != TypeCode.UInt64)
|
||||
{
|
||||
op = OpCodes.Conv_I8;
|
||||
}
|
||||
break;
|
||||
case TypeCode.UInt64:
|
||||
if (unsigned == true & @checked == true)
|
||||
{
|
||||
op = OpCodes.Conv_Ovf_U8_Un;
|
||||
}
|
||||
else if (@checked == true)
|
||||
{
|
||||
op = OpCodes.Conv_Ovf_U8;
|
||||
}
|
||||
else if (sourcetc != TypeCode.Int64)
|
||||
{
|
||||
op = OpCodes.Conv_U8;
|
||||
}
|
||||
break;
|
||||
case TypeCode.Single:
|
||||
op = OpCodes.Conv_R4;
|
||||
break;
|
||||
default:
|
||||
Debug.Assert(false, "Unknown cast dest type");
|
||||
break;
|
||||
}
|
||||
|
||||
if (op.Equals(OpCodes.Nop) == false)
|
||||
{
|
||||
ilg.Emit(op);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsUnsignedType(Type t)
|
||||
{
|
||||
TypeCode tc = Type.GetTypeCode(t);
|
||||
switch (tc)
|
||||
{
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.UInt32:
|
||||
case TypeCode.UInt64:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override System.Type ResultType => _myDestType;
|
||||
}
|
||||
}
|
279
ExpressionElements/Compare.cs
Normal file
279
ExpressionElements/Compare.cs
Normal file
@@ -0,0 +1,279 @@
|
||||
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;
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
75
ExpressionElements/Conditional.cs
Normal file
75
ExpressionElements/Conditional.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
namespace Flee.ExpressionElements
|
||||
{
|
||||
internal class ConditionalElement : ExpressionElement
|
||||
{
|
||||
private readonly ExpressionElement _myCondition;
|
||||
private readonly ExpressionElement _myWhenTrue;
|
||||
private readonly ExpressionElement _myWhenFalse;
|
||||
private readonly Type _myResultType;
|
||||
public ConditionalElement(ExpressionElement condition, ExpressionElement whenTrue, ExpressionElement whenFalse)
|
||||
{
|
||||
_myCondition = condition;
|
||||
_myWhenTrue = whenTrue;
|
||||
_myWhenFalse = whenFalse;
|
||||
|
||||
if ((!object.ReferenceEquals(_myCondition.ResultType, typeof(bool))))
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.FirstArgNotBoolean, CompileExceptionReason.TypeMismatch);
|
||||
}
|
||||
|
||||
// The result type is the type that is common to the true/false operands
|
||||
if (ImplicitConverter.EmitImplicitConvert(_myWhenFalse.ResultType, _myWhenTrue.ResultType, null) == true)
|
||||
{
|
||||
_myResultType = _myWhenTrue.ResultType;
|
||||
}
|
||||
else if (ImplicitConverter.EmitImplicitConvert(_myWhenTrue.ResultType, _myWhenFalse.ResultType, null) == true)
|
||||
{
|
||||
_myResultType = _myWhenFalse.ResultType;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.NeitherArgIsConvertibleToTheOther, CompileExceptionReason.TypeMismatch, _myWhenTrue.ResultType.Name, _myWhenFalse.ResultType.Name);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
this.EmitConditional(ilg, services);
|
||||
}
|
||||
|
||||
private void EmitConditional(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
Label falseLabel = ilg.DefineLabel();
|
||||
Label endLabel = ilg.DefineLabel();
|
||||
|
||||
// Emit the condition
|
||||
_myCondition.Emit(ilg, services);
|
||||
|
||||
// On false go to the false operand
|
||||
ilg.EmitBranchFalse(falseLabel);
|
||||
|
||||
// Emit the true operand
|
||||
_myWhenTrue.Emit(ilg, services);
|
||||
ImplicitConverter.EmitImplicitConvert(_myWhenTrue.ResultType, _myResultType, ilg);
|
||||
|
||||
// Jump to end
|
||||
ilg.EmitBranch(endLabel);
|
||||
|
||||
ilg.MarkLabel(falseLabel);
|
||||
|
||||
// Emit the false operand
|
||||
_myWhenFalse.Emit(ilg, services);
|
||||
ImplicitConverter.EmitImplicitConvert(_myWhenFalse.ResultType, _myResultType, ilg);
|
||||
// Fall through to end
|
||||
ilg.MarkLabel(endLabel);
|
||||
}
|
||||
|
||||
public override System.Type ResultType => _myResultType;
|
||||
}
|
||||
}
|
195
ExpressionElements/In.cs
Normal file
195
ExpressionElements/In.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
using System.Collections;
|
||||
using System.Reflection.Emit;
|
||||
using System.Reflection;
|
||||
using Flee.ExpressionElements.Base;
|
||||
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements
|
||||
{
|
||||
internal class InElement : ExpressionElement
|
||||
{
|
||||
// Element we will search for
|
||||
private ExpressionElement MyOperand;
|
||||
// Elements we will compare against
|
||||
private List<ExpressionElement> MyArguments;
|
||||
// Collection to look in
|
||||
private ExpressionElement MyTargetCollectionElement;
|
||||
// Type of the collection
|
||||
|
||||
private Type MyTargetCollectionType;
|
||||
// Initialize for searching a list of values
|
||||
public InElement(ExpressionElement operand, IList listElements)
|
||||
{
|
||||
MyOperand = operand;
|
||||
|
||||
ExpressionElement[] arr = new ExpressionElement[listElements.Count];
|
||||
listElements.CopyTo(arr, 0);
|
||||
|
||||
MyArguments = new List<ExpressionElement>(arr);
|
||||
this.ResolveForListSearch();
|
||||
}
|
||||
|
||||
// Initialize for searching a collection
|
||||
public InElement(ExpressionElement operand, ExpressionElement targetCollection)
|
||||
{
|
||||
MyOperand = operand;
|
||||
MyTargetCollectionElement = targetCollection;
|
||||
this.ResolveForCollectionSearch();
|
||||
}
|
||||
|
||||
private void ResolveForListSearch()
|
||||
{
|
||||
CompareElement ce = new CompareElement();
|
||||
|
||||
// Validate that our operand is comparable to all elements in the list
|
||||
foreach (ExpressionElement argumentElement in MyArguments)
|
||||
{
|
||||
ce.Initialize(MyOperand, argumentElement, LogicalCompareOperation.Equal);
|
||||
ce.Validate();
|
||||
}
|
||||
}
|
||||
|
||||
private void ResolveForCollectionSearch()
|
||||
{
|
||||
// Try to find a collection type
|
||||
MyTargetCollectionType = this.GetTargetCollectionType();
|
||||
|
||||
if (MyTargetCollectionType == null)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.SearchArgIsNotKnownCollectionType, CompileExceptionReason.TypeMismatch, MyTargetCollectionElement.ResultType.Name);
|
||||
}
|
||||
|
||||
// Validate that the operand type is compatible with the collection
|
||||
MethodInfo mi = this.GetCollectionContainsMethod();
|
||||
ParameterInfo p1 = mi.GetParameters()[0];
|
||||
|
||||
if (ImplicitConverter.EmitImplicitConvert(MyOperand.ResultType, p1.ParameterType, null) == false)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.OperandNotConvertibleToCollectionType, CompileExceptionReason.TypeMismatch, MyOperand.ResultType.Name, p1.ParameterType.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private Type GetTargetCollectionType()
|
||||
{
|
||||
Type collType = MyTargetCollectionElement.ResultType;
|
||||
|
||||
// Try to see if the collection is a generic ICollection or IDictionary
|
||||
Type[] interfaces = collType.GetInterfaces();
|
||||
|
||||
foreach (Type interfaceType in interfaces)
|
||||
{
|
||||
if (interfaceType.IsGenericType == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Type genericTypeDef = interfaceType.GetGenericTypeDefinition();
|
||||
|
||||
if (object.ReferenceEquals(genericTypeDef, typeof(ICollection<>)) | object.ReferenceEquals(genericTypeDef, typeof(IDictionary<,>)))
|
||||
{
|
||||
return interfaceType;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to see if it is a regular IList or IDictionary
|
||||
if (typeof(IList<>).IsAssignableFrom(collType) == true)
|
||||
{
|
||||
return typeof(IList<>);
|
||||
}
|
||||
else if (typeof(IDictionary<,>).IsAssignableFrom(collType) == true)
|
||||
{
|
||||
return typeof(IDictionary<,>);
|
||||
}
|
||||
|
||||
// Not a known collection type
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
if ((MyTargetCollectionType != null))
|
||||
{
|
||||
this.EmitCollectionIn(ilg, services);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do the real emit
|
||||
this.EmitListIn(ilg, services);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitCollectionIn(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
// Get the contains method
|
||||
MethodInfo mi = this.GetCollectionContainsMethod();
|
||||
ParameterInfo p1 = mi.GetParameters()[0];
|
||||
|
||||
// Load the collection
|
||||
MyTargetCollectionElement.Emit(ilg, services);
|
||||
// Load the argument
|
||||
MyOperand.Emit(ilg, services);
|
||||
// Do an implicit convert if necessary
|
||||
ImplicitConverter.EmitImplicitConvert(MyOperand.ResultType, p1.ParameterType, ilg);
|
||||
// Call the contains method
|
||||
ilg.Emit(OpCodes.Callvirt, mi);
|
||||
}
|
||||
|
||||
private MethodInfo GetCollectionContainsMethod()
|
||||
{
|
||||
string methodName = "Contains";
|
||||
|
||||
if (MyTargetCollectionType.IsGenericType == true && object.ReferenceEquals(MyTargetCollectionType.GetGenericTypeDefinition(), typeof(IDictionary<,>)))
|
||||
{
|
||||
methodName = "ContainsKey";
|
||||
}
|
||||
|
||||
return MyTargetCollectionType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||
}
|
||||
|
||||
private void EmitListIn(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
CompareElement ce = new CompareElement();
|
||||
Label endLabel = ilg.DefineLabel();
|
||||
Label trueTerminal = ilg.DefineLabel();
|
||||
|
||||
// Cache the operand since we will be comparing against it a lot
|
||||
LocalBuilder lb = ilg.DeclareLocal(MyOperand.ResultType);
|
||||
int targetIndex = lb.LocalIndex;
|
||||
|
||||
MyOperand.Emit(ilg, services);
|
||||
Utility.EmitStoreLocal(ilg, targetIndex);
|
||||
|
||||
// Wrap our operand in a local shim
|
||||
LocalBasedElement targetShim = new LocalBasedElement(MyOperand, targetIndex);
|
||||
|
||||
// Emit the compares
|
||||
foreach (ExpressionElement argumentElement in MyArguments)
|
||||
{
|
||||
ce.Initialize(targetShim, argumentElement, LogicalCompareOperation.Equal);
|
||||
ce.Emit(ilg, services);
|
||||
|
||||
EmitBranchToTrueTerminal(ilg, trueTerminal);
|
||||
}
|
||||
|
||||
ilg.Emit(OpCodes.Ldc_I4_0);
|
||||
ilg.Emit(OpCodes.Br_S, endLabel);
|
||||
|
||||
ilg.MarkLabel(trueTerminal);
|
||||
|
||||
ilg.Emit(OpCodes.Ldc_I4_1);
|
||||
|
||||
ilg.MarkLabel(endLabel);
|
||||
}
|
||||
|
||||
private static void EmitBranchToTrueTerminal(FleeILGenerator ilg, Label trueTerminal)
|
||||
{
|
||||
ilg.EmitBranchTrue(trueTerminal);
|
||||
}
|
||||
|
||||
public override System.Type ResultType => typeof(bool);
|
||||
}
|
||||
}
|
22
ExpressionElements/Literals/Boolean.cs
Normal file
22
ExpressionElements/Literals/Boolean.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Flee.ExpressionElements.Base.Literals;
|
||||
|
||||
using Flee.InternalTypes;
|
||||
|
||||
namespace Flee.ExpressionElements.Literals
|
||||
{
|
||||
internal class BooleanLiteralElement : LiteralElement
|
||||
{
|
||||
private readonly bool _myValue;
|
||||
public BooleanLiteralElement(bool value)
|
||||
{
|
||||
_myValue = value;
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
EmitLoad(_myValue, ilg);
|
||||
}
|
||||
|
||||
public override System.Type ResultType => typeof(bool);
|
||||
}
|
||||
}
|
24
ExpressionElements/Literals/Char.cs
Normal file
24
ExpressionElements/Literals/Char.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Flee.ExpressionElements.Base.Literals;
|
||||
|
||||
using Flee.InternalTypes;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.Literals
|
||||
{
|
||||
internal class CharLiteralElement : LiteralElement
|
||||
{
|
||||
private readonly char _myValue;
|
||||
public CharLiteralElement(char value)
|
||||
{
|
||||
_myValue = value;
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
int intValue = Convert.ToInt32(_myValue);
|
||||
EmitLoad(intValue, ilg);
|
||||
}
|
||||
|
||||
public override System.Type ResultType => typeof(char);
|
||||
}
|
||||
}
|
42
ExpressionElements/Literals/DateTime.cs
Normal file
42
ExpressionElements/Literals/DateTime.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Globalization;
|
||||
using Flee.ExpressionElements.Base.Literals;
|
||||
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
namespace Flee.ExpressionElements.Literals
|
||||
{
|
||||
internal class DateTimeLiteralElement : LiteralElement
|
||||
{
|
||||
private DateTime _myValue;
|
||||
public DateTimeLiteralElement(string image, ExpressionContext context)
|
||||
{
|
||||
ExpressionParserOptions options = context.ParserOptions;
|
||||
|
||||
if (DateTime.TryParseExact(image, options.DateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out _myValue) == false)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.CannotParseType, CompileExceptionReason.InvalidFormat, typeof(DateTime).Name);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
int index = ilg.GetTempLocalIndex(typeof(DateTime));
|
||||
|
||||
Utility.EmitLoadLocalAddress(ilg, index);
|
||||
|
||||
LiteralElement.EmitLoad(_myValue.Ticks, ilg);
|
||||
|
||||
ConstructorInfo ci = typeof(DateTime).GetConstructor(new Type[] { typeof(long) });
|
||||
|
||||
ilg.Emit(OpCodes.Call, ci);
|
||||
|
||||
Utility.EmitLoadLocal(ilg, index);
|
||||
}
|
||||
|
||||
public override System.Type ResultType => typeof(DateTime);
|
||||
}
|
||||
}
|
84
ExpressionElements/Literals/Integral/Int32.cs
Normal file
84
ExpressionElements/Literals/Integral/Int32.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using System.Globalization;
|
||||
using Flee.ExpressionElements.Base.Literals;
|
||||
|
||||
using Flee.InternalTypes;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.Literals.Integral
|
||||
{
|
||||
internal class Int32LiteralElement : IntegralLiteralElement
|
||||
{
|
||||
private Int32 _myValue;
|
||||
private const string MinValue = "2147483648";
|
||||
private readonly bool _myIsMinValue;
|
||||
public Int32LiteralElement(Int32 value)
|
||||
{
|
||||
_myValue = value;
|
||||
}
|
||||
|
||||
private Int32LiteralElement()
|
||||
{
|
||||
_myIsMinValue = true;
|
||||
}
|
||||
|
||||
public static Int32LiteralElement TryCreate(string image, bool isHex, bool negated)
|
||||
{
|
||||
if (negated == true & image == MinValue)
|
||||
{
|
||||
return new Int32LiteralElement();
|
||||
}
|
||||
else if (isHex == true)
|
||||
{
|
||||
Int32 value = default(Int32);
|
||||
|
||||
// Since Int32.TryParse will succeed for a string like 0xFFFFFFFF we have to do some special handling
|
||||
if (Int32.TryParse(image, NumberStyles.AllowHexSpecifier, null, out value) == false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (value >= 0 & value <= Int32.MaxValue)
|
||||
{
|
||||
return new Int32LiteralElement(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Int32 value = default(Int32);
|
||||
|
||||
if (Int32.TryParse(image,out value) == true)
|
||||
{
|
||||
return new Int32LiteralElement(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Negate()
|
||||
{
|
||||
if (_myIsMinValue == true)
|
||||
{
|
||||
_myValue = Int32.MinValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
_myValue = -_myValue;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
EmitLoad(_myValue, ilg);
|
||||
}
|
||||
|
||||
public override System.Type ResultType => typeof(Int32);
|
||||
|
||||
public int Value => _myValue;
|
||||
}
|
||||
}
|
82
ExpressionElements/Literals/Integral/Int64.cs
Normal file
82
ExpressionElements/Literals/Integral/Int64.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System.Globalization;
|
||||
using Flee.ExpressionElements.Base.Literals;
|
||||
|
||||
using Flee.InternalTypes;
|
||||
|
||||
namespace Flee.ExpressionElements.Literals.Integral
|
||||
{
|
||||
internal class Int64LiteralElement : IntegralLiteralElement
|
||||
{
|
||||
|
||||
private Int64 _myValue;
|
||||
private const string MinValue = "9223372036854775808";
|
||||
|
||||
private readonly bool _myIsMinValue;
|
||||
public Int64LiteralElement(Int64 value)
|
||||
{
|
||||
_myValue = value;
|
||||
}
|
||||
|
||||
private Int64LiteralElement()
|
||||
{
|
||||
_myIsMinValue = true;
|
||||
}
|
||||
|
||||
public static Int64LiteralElement TryCreate(string image, bool isHex, bool negated)
|
||||
{
|
||||
if (negated == true & image == MinValue)
|
||||
{
|
||||
return new Int64LiteralElement();
|
||||
}
|
||||
else if (isHex == true)
|
||||
{
|
||||
Int64 value = default(Int64);
|
||||
|
||||
if (Int64.TryParse(image, NumberStyles.AllowHexSpecifier, null, out value) == false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (value >= 0 & value <= Int64.MaxValue)
|
||||
{
|
||||
return new Int64LiteralElement(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Int64 value = default(Int64);
|
||||
|
||||
if (Int64.TryParse(image, out value) == true)
|
||||
{
|
||||
return new Int64LiteralElement(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
EmitLoad(_myValue, ilg);
|
||||
}
|
||||
|
||||
public void Negate()
|
||||
{
|
||||
if (_myIsMinValue == true)
|
||||
{
|
||||
_myValue = Int64.MinValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
_myValue = -_myValue;
|
||||
}
|
||||
}
|
||||
|
||||
public override System.Type ResultType => typeof(Int64);
|
||||
}
|
||||
}
|
34
ExpressionElements/Literals/Integral/UInt32.cs
Normal file
34
ExpressionElements/Literals/Integral/UInt32.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Flee.ExpressionElements.Base.Literals;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
namespace Flee.ExpressionElements.Literals.Integral
|
||||
{
|
||||
internal class UInt32LiteralElement : IntegralLiteralElement
|
||||
{
|
||||
private readonly UInt32 _myValue;
|
||||
public UInt32LiteralElement(UInt32 value)
|
||||
{
|
||||
_myValue = value;
|
||||
}
|
||||
|
||||
public static UInt32LiteralElement TryCreate(string image, System.Globalization.NumberStyles ns)
|
||||
{
|
||||
UInt32 value = default(UInt32);
|
||||
if (UInt32.TryParse(image, ns, null, out value) == true)
|
||||
{
|
||||
return new UInt32LiteralElement(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
EmitLoad(Convert.ToInt32(_myValue), ilg);
|
||||
}
|
||||
|
||||
public override System.Type ResultType => typeof(UInt32);
|
||||
}
|
||||
}
|
34
ExpressionElements/Literals/Integral/UInt64.cs
Normal file
34
ExpressionElements/Literals/Integral/UInt64.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Flee.ExpressionElements.Base.Literals;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.Literals.Integral
|
||||
{
|
||||
internal class UInt64LiteralElement : IntegralLiteralElement
|
||||
{
|
||||
private readonly UInt64 _myValue;
|
||||
public UInt64LiteralElement(string image, System.Globalization.NumberStyles ns)
|
||||
{
|
||||
try
|
||||
{
|
||||
_myValue = UInt64.Parse(image, ns);
|
||||
}
|
||||
catch (OverflowException ex)
|
||||
{
|
||||
base.OnParseOverflow(image);
|
||||
}
|
||||
}
|
||||
|
||||
public UInt64LiteralElement(UInt64 value)
|
||||
{
|
||||
_myValue = value;
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
EmitLoad(Convert.ToInt64(_myValue), ilg);
|
||||
}
|
||||
|
||||
public override System.Type ResultType => typeof(UInt64);
|
||||
}
|
||||
}
|
16
ExpressionElements/Literals/Null.cs
Normal file
16
ExpressionElements/Literals/Null.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base.Literals;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
namespace Flee.ExpressionElements.Literals
|
||||
{
|
||||
internal class NullLiteralElement : LiteralElement
|
||||
{
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldnull);
|
||||
}
|
||||
|
||||
public override System.Type ResultType => typeof(Null);
|
||||
}
|
||||
}
|
76
ExpressionElements/Literals/Real/Decimal.cs
Normal file
76
ExpressionElements/Literals/Real/Decimal.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System.Reflection.Emit;
|
||||
using System.Reflection;
|
||||
using Flee.ExpressionElements.Base.Literals;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.Literals.Real
|
||||
{
|
||||
internal class DecimalLiteralElement : RealLiteralElement
|
||||
{
|
||||
private static readonly ConstructorInfo OurConstructorInfo = GetConstructor();
|
||||
private readonly decimal _myValue;
|
||||
|
||||
private DecimalLiteralElement()
|
||||
{
|
||||
}
|
||||
|
||||
public DecimalLiteralElement(decimal value)
|
||||
{
|
||||
_myValue = value;
|
||||
}
|
||||
|
||||
private static ConstructorInfo GetConstructor()
|
||||
{
|
||||
Type[] types = {
|
||||
typeof(Int32),
|
||||
typeof(Int32),
|
||||
typeof(Int32),
|
||||
typeof(bool),
|
||||
typeof(byte)
|
||||
};
|
||||
return typeof(decimal).GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, CallingConventions.Any, types, null);
|
||||
}
|
||||
|
||||
public static DecimalLiteralElement Parse(string image, IServiceProvider services)
|
||||
{
|
||||
ExpressionParserOptions options = (ExpressionParserOptions)services.GetService(typeof(ExpressionParserOptions));
|
||||
DecimalLiteralElement element = new DecimalLiteralElement();
|
||||
|
||||
try
|
||||
{
|
||||
decimal value = options.ParseDecimal(image);
|
||||
return new DecimalLiteralElement(value);
|
||||
}
|
||||
catch (OverflowException ex)
|
||||
{
|
||||
element.OnParseOverflow(image);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
int index = ilg.GetTempLocalIndex(typeof(decimal));
|
||||
Utility.EmitLoadLocalAddress(ilg, index);
|
||||
|
||||
int[] bits = decimal.GetBits(_myValue);
|
||||
EmitLoad(bits[0], ilg);
|
||||
EmitLoad(bits[1], ilg);
|
||||
EmitLoad(bits[2], ilg);
|
||||
|
||||
int flags = bits[3];
|
||||
|
||||
EmitLoad((flags >> 31) == -1, ilg);
|
||||
|
||||
EmitLoad(flags >> 16, ilg);
|
||||
|
||||
ilg.Emit(OpCodes.Call, OurConstructorInfo);
|
||||
|
||||
Utility.EmitLoadLocal(ilg, index);
|
||||
}
|
||||
|
||||
public override System.Type ResultType => typeof(decimal);
|
||||
}
|
||||
}
|
46
ExpressionElements/Literals/Real/Double.cs
Normal file
46
ExpressionElements/Literals/Real/Double.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base.Literals;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.Literals.Real
|
||||
{
|
||||
internal class DoubleLiteralElement : RealLiteralElement
|
||||
{
|
||||
private readonly double _myValue;
|
||||
|
||||
private DoubleLiteralElement()
|
||||
{
|
||||
}
|
||||
|
||||
public DoubleLiteralElement(double value)
|
||||
{
|
||||
_myValue = value;
|
||||
}
|
||||
|
||||
public static DoubleLiteralElement Parse(string image, IServiceProvider services)
|
||||
{
|
||||
ExpressionParserOptions options = (ExpressionParserOptions)services.GetService(typeof(ExpressionParserOptions));
|
||||
DoubleLiteralElement element = new DoubleLiteralElement();
|
||||
|
||||
try
|
||||
{
|
||||
double value = options.ParseDouble(image);
|
||||
return new DoubleLiteralElement(value);
|
||||
}
|
||||
catch (OverflowException ex)
|
||||
{
|
||||
element.OnParseOverflow(image);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldc_R8, _myValue);
|
||||
}
|
||||
|
||||
public override System.Type ResultType => typeof(double);
|
||||
}
|
||||
}
|
45
ExpressionElements/Literals/Real/Single.cs
Normal file
45
ExpressionElements/Literals/Real/Single.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base.Literals;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
|
||||
namespace Flee.ExpressionElements.Literals.Real
|
||||
{
|
||||
internal class SingleLiteralElement : RealLiteralElement
|
||||
{
|
||||
private readonly float _myValue;
|
||||
|
||||
private SingleLiteralElement()
|
||||
{
|
||||
}
|
||||
|
||||
public SingleLiteralElement(float value)
|
||||
{
|
||||
_myValue = value;
|
||||
}
|
||||
|
||||
public static SingleLiteralElement Parse(string image, IServiceProvider services)
|
||||
{
|
||||
ExpressionParserOptions options = (ExpressionParserOptions)services.GetService(typeof(ExpressionParserOptions));
|
||||
SingleLiteralElement element = new SingleLiteralElement();
|
||||
|
||||
try
|
||||
{
|
||||
float value = options.ParseSingle(image);
|
||||
return new SingleLiteralElement(value);
|
||||
}
|
||||
catch (OverflowException ex)
|
||||
{
|
||||
element.OnParseOverflow(image);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldc_R4, _myValue);
|
||||
}
|
||||
|
||||
public override System.Type ResultType => typeof(float);
|
||||
}
|
||||
}
|
23
ExpressionElements/Literals/String.cs
Normal file
23
ExpressionElements/Literals/String.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base.Literals;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.Literals
|
||||
{
|
||||
internal class StringLiteralElement : LiteralElement
|
||||
{
|
||||
private readonly string _myValue;
|
||||
public StringLiteralElement(string value)
|
||||
{
|
||||
_myValue = value;
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldstr, _myValue);
|
||||
}
|
||||
|
||||
public override System.Type ResultType => typeof(string);
|
||||
}
|
||||
}
|
38
ExpressionElements/Literals/TimeSpan.cs
Normal file
38
ExpressionElements/Literals/TimeSpan.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base.Literals;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
namespace Flee.ExpressionElements.Literals
|
||||
{
|
||||
internal class TimeSpanLiteralElement : LiteralElement
|
||||
{
|
||||
private TimeSpan _myValue;
|
||||
public TimeSpanLiteralElement(string image)
|
||||
{
|
||||
if (TimeSpan.TryParse(image, out _myValue) == false)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.CannotParseType, CompileExceptionReason.InvalidFormat, typeof(TimeSpan).Name);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, System.IServiceProvider services)
|
||||
{
|
||||
int index = ilg.GetTempLocalIndex(typeof(TimeSpan));
|
||||
|
||||
Utility.EmitLoadLocalAddress(ilg, index);
|
||||
|
||||
LiteralElement.EmitLoad(_myValue.Ticks, ilg);
|
||||
|
||||
ConstructorInfo ci = typeof(TimeSpan).GetConstructor(new Type[] { typeof(long) });
|
||||
|
||||
ilg.Emit(OpCodes.Call, ci);
|
||||
|
||||
Utility.EmitLoadLocal(ilg, index);
|
||||
}
|
||||
|
||||
public override System.Type ResultType => typeof(TimeSpan);
|
||||
}
|
||||
}
|
348
ExpressionElements/LogicalBitwise/AndOr.cs
Normal file
348
ExpressionElements/LogicalBitwise/AndOr.cs
Normal file
@@ -0,0 +1,348 @@
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
namespace Flee.ExpressionElements.LogicalBitwise
|
||||
{
|
||||
internal class AndOrElement : BinaryExpressionElement
|
||||
{
|
||||
private AndOrOperation _myOperation;
|
||||
private static readonly object OurTrueTerminalKey = new object();
|
||||
private static readonly object OurFalseTerminalKey = new object();
|
||||
private static readonly object OurEndLabelKey = new object();
|
||||
|
||||
public void New()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void GetOperation(object operation)
|
||||
{
|
||||
_myOperation = (AndOrOperation)operation;
|
||||
}
|
||||
|
||||
protected override System.Type GetResultType(System.Type leftType, System.Type rightType)
|
||||
{
|
||||
Type bitwiseOpType = Utility.GetBitwiseOpType(leftType, rightType);
|
||||
if ((bitwiseOpType != null))
|
||||
{
|
||||
return bitwiseOpType;
|
||||
}
|
||||
else if (this.AreBothChildrenOfType(typeof(bool)))
|
||||
{
|
||||
return typeof(bool);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
Type resultType = this.ResultType;
|
||||
|
||||
if (object.ReferenceEquals(resultType, typeof(bool)))
|
||||
{
|
||||
this.DoEmitLogical(ilg, services);
|
||||
}
|
||||
else
|
||||
{
|
||||
MyLeftChild.Emit(ilg, services);
|
||||
ImplicitConverter.EmitImplicitConvert(MyLeftChild.ResultType, resultType, ilg);
|
||||
MyRightChild.Emit(ilg, services);
|
||||
ImplicitConverter.EmitImplicitConvert(MyRightChild.ResultType, resultType, ilg);
|
||||
EmitBitwiseOperation(ilg, _myOperation);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitBitwiseOperation(FleeILGenerator ilg, AndOrOperation op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case AndOrOperation.And:
|
||||
ilg.Emit(OpCodes.And);
|
||||
break;
|
||||
case AndOrOperation.Or:
|
||||
ilg.Emit(OpCodes.Or);
|
||||
break;
|
||||
default:
|
||||
Debug.Fail("Unknown op type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DoEmitLogical(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
// We have to do a 'fake' emit so we can get the positions of the labels
|
||||
ShortCircuitInfo info = new ShortCircuitInfo();
|
||||
|
||||
// Do the real emit
|
||||
this.EmitLogical(ilg, info, services);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit a short-circuited logical operation sequence
|
||||
/// The idea: Store all the leaf operands in a stack with the leftmost at the top and rightmost at the bottom.
|
||||
/// For each operand, emit it and try to find an end point for when it short-circuits. This means we go up through
|
||||
/// the stack of operators (ignoring siblings) until we find a different operation (then emit a branch to its right operand)
|
||||
/// or we reach the root (emit a branch to a true/false).
|
||||
/// Repeat the process for all operands and then emit the true/false/last operand end cases.
|
||||
/// </summary>
|
||||
/// <param name="ilg"></param>
|
||||
/// <param name="info"></param>
|
||||
/// <param name="services"></param>
|
||||
private void EmitLogical(FleeILGenerator ilg, ShortCircuitInfo info, IServiceProvider services)
|
||||
{
|
||||
// We always have an end label
|
||||
Label endLabel = ilg.DefineLabel();
|
||||
|
||||
// Populate our data structures
|
||||
this.PopulateData(info);
|
||||
|
||||
// Emit the sequence
|
||||
EmitLogicalShortCircuit(ilg, info, services);
|
||||
|
||||
// Get the last operand
|
||||
ExpressionElement terminalOperand = (ExpressionElement)info.Operands.Pop();
|
||||
// Emit it
|
||||
EmitOperand(terminalOperand, info, ilg, services);
|
||||
|
||||
// only 1-3 opcodes, always a short branch
|
||||
ilg.EmitBranch(endLabel);
|
||||
|
||||
// Emit our true/false terminals
|
||||
EmitTerminals(info, ilg, endLabel);
|
||||
|
||||
// Mark the end
|
||||
ilg.MarkLabel(endLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit a sequence of and/or expressions with short-circuiting
|
||||
/// </summary>
|
||||
/// <param name="ilg"></param>
|
||||
/// <param name="info"></param>
|
||||
/// <param name="services"></param>
|
||||
private static void EmitLogicalShortCircuit(FleeILGenerator ilg, ShortCircuitInfo info, IServiceProvider services)
|
||||
{
|
||||
while (info.Operators.Count != 0)
|
||||
{
|
||||
// Get the operator
|
||||
AndOrElement op = (AndOrElement)info.Operators.Pop();
|
||||
// Get the left operand
|
||||
ExpressionElement leftOperand = (ExpressionElement)info.Operands.Pop();
|
||||
|
||||
// Emit the left
|
||||
EmitOperand(leftOperand, info, ilg, services);
|
||||
|
||||
// Get the label for the short-circuit case
|
||||
Label l = GetShortCircuitLabel(op, info, ilg);
|
||||
// Emit the branch
|
||||
EmitBranch(op, ilg, l, info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void EmitBranch(AndOrElement op, FleeILGenerator ilg, Label target, ShortCircuitInfo info)
|
||||
{
|
||||
// Get the branch opcode
|
||||
if (op._myOperation == AndOrOperation.And)
|
||||
ilg.EmitBranchFalse(target);
|
||||
else
|
||||
ilg.EmitBranchTrue(target);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get the label for a short-circuit
|
||||
/// </summary>
|
||||
/// <param name="current"></param>
|
||||
/// <param name="info"></param>
|
||||
/// <param name="ilg"></param>
|
||||
/// <returns></returns>
|
||||
private static Label GetShortCircuitLabel(AndOrElement current, ShortCircuitInfo info, FleeILGenerator ilg)
|
||||
{
|
||||
// We modify the given stacks so we need to clone them
|
||||
Stack cloneOperands = (Stack)info.Operands.Clone();
|
||||
Stack cloneOperators = (Stack)info.Operators.Clone();
|
||||
|
||||
// Pop all siblings
|
||||
current.PopRightChild(cloneOperands, cloneOperators);
|
||||
|
||||
// Go until we run out of operators
|
||||
while (cloneOperators.Count > 0)
|
||||
{
|
||||
// Get the top operator
|
||||
AndOrElement top = (AndOrElement)cloneOperators.Pop();
|
||||
|
||||
// Is is a different operation?
|
||||
if (top._myOperation != current._myOperation)
|
||||
{
|
||||
// Yes, so return a label to its right operand
|
||||
object nextOperand = cloneOperands.Pop();
|
||||
return GetLabel(nextOperand, ilg, info);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No, so keep going up the stack
|
||||
top.PopRightChild(cloneOperands, cloneOperators);
|
||||
}
|
||||
}
|
||||
|
||||
// We've reached the end of the stack so return the label for the appropriate true/false terminal
|
||||
if (current._myOperation == AndOrOperation.And)
|
||||
{
|
||||
return GetLabel(OurFalseTerminalKey, ilg, info);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetLabel(OurTrueTerminalKey, ilg, info);
|
||||
}
|
||||
}
|
||||
|
||||
private void PopRightChild(Stack operands, Stack operators)
|
||||
{
|
||||
AndOrElement andOrChild = MyRightChild as AndOrElement;
|
||||
|
||||
// What kind of child do we have?
|
||||
if ((andOrChild != null))
|
||||
{
|
||||
// Another and/or expression so recurse
|
||||
andOrChild.Pop(operands, operators);
|
||||
}
|
||||
else
|
||||
{
|
||||
// A terminal so pop it off the operands stack
|
||||
operands.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively pop operators and operands
|
||||
/// </summary>
|
||||
/// <param name="operands"></param>
|
||||
/// <param name="operators"></param>
|
||||
private void Pop(Stack operands, Stack operators)
|
||||
{
|
||||
operators.Pop();
|
||||
|
||||
AndOrElement andOrChild = MyLeftChild as AndOrElement;
|
||||
if (andOrChild == null)
|
||||
{
|
||||
operands.Pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
andOrChild.Pop(operands, operators);
|
||||
}
|
||||
|
||||
andOrChild = MyRightChild as AndOrElement;
|
||||
|
||||
if (andOrChild == null)
|
||||
{
|
||||
operands.Pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
andOrChild.Pop(operands, operators);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitOperand(ExpressionElement operand, ShortCircuitInfo info, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
// Is this operand the target of a label?
|
||||
if (info.HasLabel(operand) == true)
|
||||
{
|
||||
// Yes, so mark it
|
||||
Label leftLabel = info.FindLabel(operand);
|
||||
ilg.MarkLabel(leftLabel);
|
||||
}
|
||||
|
||||
// Emit the operand
|
||||
operand.Emit(ilg, services);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit the end cases for a short-circuit
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
/// <param name="ilg"></param>
|
||||
/// <param name="endLabel"></param>
|
||||
private static void EmitTerminals(ShortCircuitInfo info, FleeILGenerator ilg, Label endLabel)
|
||||
{
|
||||
// Emit the false case if it was used
|
||||
if (info.HasLabel(OurFalseTerminalKey) == true)
|
||||
{
|
||||
Label falseLabel = info.FindLabel(OurFalseTerminalKey);
|
||||
|
||||
// Mark the label and note its position
|
||||
ilg.MarkLabel(falseLabel);
|
||||
|
||||
ilg.Emit(OpCodes.Ldc_I4_0);
|
||||
|
||||
// If we also have a true terminal, then skip over it
|
||||
if (info.HasLabel(OurTrueTerminalKey) == true)
|
||||
{
|
||||
// only 1-3 opcodes, always a short branch
|
||||
ilg.Emit(OpCodes.Br_S, endLabel);
|
||||
}
|
||||
}
|
||||
|
||||
// Emit the true case if it was used
|
||||
if (info.HasLabel(OurTrueTerminalKey) == true)
|
||||
{
|
||||
Label trueLabel = info.FindLabel(OurTrueTerminalKey);
|
||||
|
||||
// Mark the label and note its position
|
||||
ilg.MarkLabel(trueLabel);
|
||||
|
||||
ilg.Emit(OpCodes.Ldc_I4_1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static Label GetLabel(object key, FleeILGenerator ilg, ShortCircuitInfo info)
|
||||
{
|
||||
if (info.HasLabel(key))
|
||||
return info.FindLabel(key);
|
||||
return info.AddLabel(key, ilg.DefineLabel());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Visit the nodes of the tree (right then left) and populate some data structures
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
private void PopulateData(ShortCircuitInfo info)
|
||||
{
|
||||
// Is our right child a leaf or another And/Or expression?
|
||||
AndOrElement andOrChild = MyRightChild as AndOrElement;
|
||||
if (andOrChild == null)
|
||||
{
|
||||
// Leaf so push it on the stack
|
||||
info.Operands.Push(MyRightChild);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Another And/Or expression so recurse
|
||||
andOrChild.PopulateData(info);
|
||||
}
|
||||
|
||||
// Add ourselves as an operator
|
||||
info.Operators.Push(this);
|
||||
|
||||
// Do the same thing for the left child
|
||||
andOrChild = MyLeftChild as AndOrElement;
|
||||
|
||||
if (andOrChild == null)
|
||||
{
|
||||
info.Operands.Push(MyLeftChild);
|
||||
}
|
||||
else
|
||||
{
|
||||
andOrChild.PopulateData(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
46
ExpressionElements/LogicalBitwise/Not.cs
Normal file
46
ExpressionElements/LogicalBitwise/Not.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.LogicalBitwise
|
||||
{
|
||||
internal class NotElement : UnaryElement
|
||||
{
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
if (object.ReferenceEquals(MyChild.ResultType, typeof(bool)))
|
||||
{
|
||||
this.EmitLogical(ilg, services);
|
||||
}
|
||||
else
|
||||
{
|
||||
MyChild.Emit(ilg, services);
|
||||
ilg.Emit(OpCodes.Not);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitLogical(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
MyChild.Emit(ilg, services);
|
||||
ilg.Emit(OpCodes.Ldc_I4_0);
|
||||
ilg.Emit(OpCodes.Ceq);
|
||||
}
|
||||
|
||||
protected override System.Type GetResultType(System.Type childType)
|
||||
{
|
||||
if (object.ReferenceEquals(childType, typeof(bool)))
|
||||
{
|
||||
return typeof(bool);
|
||||
}
|
||||
else if (Utility.IsIntegralType(childType) == true)
|
||||
{
|
||||
return childType;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
44
ExpressionElements/LogicalBitwise/Xor.cs
Normal file
44
ExpressionElements/LogicalBitwise/Xor.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.LogicalBitwise
|
||||
{
|
||||
internal class XorElement : BinaryExpressionElement
|
||||
{
|
||||
protected override System.Type GetResultType(System.Type leftType, System.Type rightType)
|
||||
{
|
||||
Type bitwiseType = Utility.GetBitwiseOpType(leftType, rightType);
|
||||
|
||||
if ((bitwiseType != null))
|
||||
{
|
||||
return bitwiseType;
|
||||
}
|
||||
else if (this.AreBothChildrenOfType(typeof(bool)) == true)
|
||||
{
|
||||
return typeof(bool);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
Type resultType = this.ResultType;
|
||||
|
||||
MyLeftChild.Emit(ilg, services);
|
||||
ImplicitConverter.EmitImplicitConvert(MyLeftChild.ResultType, resultType, ilg);
|
||||
MyRightChild.Emit(ilg, services);
|
||||
ImplicitConverter.EmitImplicitConvert(MyRightChild.ResultType, resultType, ilg);
|
||||
ilg.Emit(OpCodes.Xor);
|
||||
}
|
||||
|
||||
|
||||
protected override void GetOperation(object operation)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
59
ExpressionElements/MemberElements/ArgumentList.cs
Normal file
59
ExpressionElements/MemberElements/ArgumentList.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System.Collections;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
namespace Flee.ExpressionElements.MemberElements
|
||||
{
|
||||
[Obsolete("Encapsulates an argument list")]
|
||||
internal class ArgumentList
|
||||
{
|
||||
private readonly IList<ExpressionElement> _myElements;
|
||||
public ArgumentList(ICollection elements)
|
||||
{
|
||||
ExpressionElement[] arr = new ExpressionElement[elements.Count];
|
||||
elements.CopyTo(arr, 0);
|
||||
_myElements = arr;
|
||||
}
|
||||
|
||||
private string[] GetArgumentTypeNames()
|
||||
{
|
||||
List<string> l = new List<string>();
|
||||
|
||||
foreach (ExpressionElement e in _myElements)
|
||||
{
|
||||
l.Add(e.ResultType.Name);
|
||||
}
|
||||
|
||||
return l.ToArray();
|
||||
}
|
||||
|
||||
public Type[] GetArgumentTypes()
|
||||
{
|
||||
List<Type> l = new List<Type>();
|
||||
|
||||
foreach (ExpressionElement e in _myElements)
|
||||
{
|
||||
l.Add(e.ResultType);
|
||||
}
|
||||
|
||||
return l.ToArray();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string[] typeNames = this.GetArgumentTypeNames();
|
||||
return Utility.FormatList(typeNames);
|
||||
}
|
||||
|
||||
public ExpressionElement[] ToArray()
|
||||
{
|
||||
ExpressionElement[] arr = new ExpressionElement[_myElements.Count];
|
||||
_myElements.CopyTo(arr, 0);
|
||||
return arr;
|
||||
}
|
||||
|
||||
public ExpressionElement this[int index] => _myElements[index];
|
||||
|
||||
public int Count => _myElements.Count;
|
||||
}
|
||||
}
|
409
ExpressionElements/MemberElements/FunctionCall.cs
Normal file
409
ExpressionElements/MemberElements/FunctionCall.cs
Normal file
@@ -0,0 +1,409 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.ExpressionElements.Base.Literals;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
namespace Flee.ExpressionElements.MemberElements
|
||||
{
|
||||
[Obsolete("Represents a function call")]
|
||||
internal class FunctionCallElement : MemberElement
|
||||
{
|
||||
private readonly ArgumentList _myArguments;
|
||||
private readonly ICollection<MethodInfo> _myMethods;
|
||||
private CustomMethodInfo _myTargetMethodInfo;
|
||||
|
||||
private Type _myOnDemandFunctionReturnType;
|
||||
public FunctionCallElement(string name, ArgumentList arguments)
|
||||
{
|
||||
this.MyName = name;
|
||||
_myArguments = arguments;
|
||||
}
|
||||
|
||||
internal FunctionCallElement(string name, ICollection<MethodInfo> methods, ArgumentList arguments)
|
||||
{
|
||||
MyName = name;
|
||||
_myArguments = arguments;
|
||||
_myMethods = methods;
|
||||
}
|
||||
|
||||
protected override void ResolveInternal()
|
||||
{
|
||||
// Get the types of our arguments
|
||||
Type[] argTypes = _myArguments.GetArgumentTypes();
|
||||
// Find all methods with our name on the type
|
||||
ICollection<MethodInfo> methods = _myMethods;
|
||||
|
||||
if (methods == null)
|
||||
{
|
||||
// Convert member info to method info
|
||||
MemberInfo[] arr = this.GetMembers(MemberTypes.Method);
|
||||
MethodInfo[] arr2 = new MethodInfo[arr.Length];
|
||||
Array.Copy(arr, arr2, arr.Length);
|
||||
methods = arr2;
|
||||
}
|
||||
|
||||
if (methods.Count > 0)
|
||||
{
|
||||
// More than one method exists with this name
|
||||
this.BindToMethod(methods, MyPrevious, argTypes);
|
||||
return;
|
||||
}
|
||||
|
||||
// No methods with this name exist; try to bind to an on-demand function
|
||||
_myOnDemandFunctionReturnType = MyContext.Variables.ResolveOnDemandFunction(MyName, argTypes);
|
||||
|
||||
if (_myOnDemandFunctionReturnType == null)
|
||||
{
|
||||
// Failed to bind to a function
|
||||
this.ThrowFunctionNotFoundException(MyPrevious);
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowFunctionNotFoundException(MemberElement previous)
|
||||
{
|
||||
if (previous == null)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.UndefinedFunction, CompileExceptionReason.UndefinedName, MyName, _myArguments);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.UndefinedFunctionOnType, CompileExceptionReason.UndefinedName, MyName, _myArguments, previous.TargetType.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowNoAccessibleMethodsException(MemberElement previous)
|
||||
{
|
||||
if (previous == null)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.NoAccessibleMatches, CompileExceptionReason.AccessDenied, MyName, _myArguments);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.NoAccessibleMatchesOnType, CompileExceptionReason.AccessDenied, MyName, _myArguments, previous.TargetType.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowAmbiguousMethodCallException()
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.AmbiguousCallOfFunction, CompileExceptionReason.AmbiguousMatch, MyName, _myArguments);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to find a match from a set of methods
|
||||
/// </summary>
|
||||
/// <param name="methods"></param>
|
||||
/// <param name="previous"></param>
|
||||
/// <param name="argTypes"></param>
|
||||
private void BindToMethod(ICollection<MethodInfo> methods, MemberElement previous, Type[] argTypes)
|
||||
{
|
||||
List<CustomMethodInfo> customInfos = new List<CustomMethodInfo>();
|
||||
|
||||
// Wrap the MethodInfos in our custom class
|
||||
foreach (MethodInfo mi in methods)
|
||||
{
|
||||
CustomMethodInfo cmi = new CustomMethodInfo(mi);
|
||||
customInfos.Add(cmi);
|
||||
}
|
||||
|
||||
// Discard any methods that cannot qualify as overloads
|
||||
CustomMethodInfo[] arr = customInfos.ToArray();
|
||||
customInfos.Clear();
|
||||
|
||||
foreach (CustomMethodInfo cmi in arr)
|
||||
{
|
||||
if (cmi.IsMatch(argTypes, MyPrevious, MyContext) == true)
|
||||
{
|
||||
customInfos.Add(cmi);
|
||||
}
|
||||
}
|
||||
|
||||
if (customInfos.Count == 0)
|
||||
{
|
||||
// We have no methods that can qualify as overloads; throw exception
|
||||
this.ThrowFunctionNotFoundException(previous);
|
||||
}
|
||||
else
|
||||
{
|
||||
// At least one method matches our criteria; do our custom overload resolution
|
||||
this.ResolveOverloads(customInfos.ToArray(), previous, argTypes);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the best match from a set of overloaded methods
|
||||
/// </summary>
|
||||
/// <param name="infos"></param>
|
||||
/// <param name="previous"></param>
|
||||
/// <param name="argTypes"></param>
|
||||
private void ResolveOverloads(CustomMethodInfo[] infos, MemberElement previous, Type[] argTypes)
|
||||
{
|
||||
// Compute a score for each candidate
|
||||
foreach (CustomMethodInfo cmi in infos)
|
||||
{
|
||||
cmi.ComputeScore(argTypes);
|
||||
}
|
||||
|
||||
// Sort array from best to worst matches
|
||||
Array.Sort<CustomMethodInfo>(infos);
|
||||
|
||||
// Discard any matches that aren't accessible
|
||||
infos = this.GetAccessibleInfos(infos);
|
||||
|
||||
// No accessible methods left
|
||||
if (infos.Length == 0)
|
||||
{
|
||||
this.ThrowNoAccessibleMethodsException(previous);
|
||||
}
|
||||
|
||||
// Handle case where we have more than one match with the same score
|
||||
this.DetectAmbiguousMatches(infos);
|
||||
|
||||
// If we get here, then there is only one best match
|
||||
_myTargetMethodInfo = infos[0];
|
||||
}
|
||||
|
||||
private CustomMethodInfo[] GetAccessibleInfos(CustomMethodInfo[] infos)
|
||||
{
|
||||
List<CustomMethodInfo> accessible = new List<CustomMethodInfo>();
|
||||
|
||||
foreach (CustomMethodInfo cmi in infos)
|
||||
{
|
||||
if (cmi.IsAccessible(this) == true)
|
||||
{
|
||||
accessible.Add(cmi);
|
||||
}
|
||||
}
|
||||
|
||||
return accessible.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle case where we have overloads with the same score
|
||||
/// </summary>
|
||||
/// <param name="infos"></param>
|
||||
private void DetectAmbiguousMatches(CustomMethodInfo[] infos)
|
||||
{
|
||||
List<CustomMethodInfo> sameScores = new List<CustomMethodInfo>();
|
||||
CustomMethodInfo first = infos[0];
|
||||
|
||||
// Find all matches with the same score as the best match
|
||||
foreach (CustomMethodInfo cmi in infos)
|
||||
{
|
||||
if (((IEquatable<CustomMethodInfo>)cmi).Equals(first) == true)
|
||||
{
|
||||
sameScores.Add(cmi);
|
||||
}
|
||||
}
|
||||
|
||||
// More than one accessible match with the same score exists
|
||||
if (sameScores.Count > 1)
|
||||
{
|
||||
this.ThrowAmbiguousMethodCallException();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Validate()
|
||||
{
|
||||
base.Validate();
|
||||
|
||||
if ((_myOnDemandFunctionReturnType != null))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Any function reference in an expression must return a value
|
||||
if (object.ReferenceEquals(this.Method.ReturnType, typeof(void)))
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.FunctionHasNoReturnValue, CompileExceptionReason.FunctionHasNoReturnValue, MyName);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
base.Emit(ilg, services);
|
||||
|
||||
ExpressionElement[] elements = _myArguments.ToArray();
|
||||
|
||||
// If we are an on-demand function, then emit that and exit
|
||||
if ((_myOnDemandFunctionReturnType != null))
|
||||
{
|
||||
this.EmitOnDemandFunction(elements, ilg, services);
|
||||
return;
|
||||
}
|
||||
|
||||
bool isOwnerMember = MyOptions.IsOwnerType(this.Method.ReflectedType);
|
||||
|
||||
// Load the owner if required
|
||||
if (MyPrevious == null && isOwnerMember == true && this.IsStatic == false)
|
||||
{
|
||||
this.EmitLoadOwner(ilg);
|
||||
}
|
||||
|
||||
this.EmitFunctionCall(this.NextRequiresAddress, ilg, services);
|
||||
}
|
||||
|
||||
private void EmitOnDemandFunction(ExpressionElement[] elements, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
// Load the variable collection
|
||||
EmitLoadVariables(ilg);
|
||||
// Load the function name
|
||||
ilg.Emit(OpCodes.Ldstr, MyName);
|
||||
// Load the arguments array
|
||||
EmitElementArrayLoad(elements, typeof(object), ilg, services);
|
||||
|
||||
// Call the function to get the result
|
||||
MethodInfo mi = VariableCollection.GetFunctionInvokeMethod(_myOnDemandFunctionReturnType);
|
||||
|
||||
this.EmitMethodCall(mi, ilg);
|
||||
}
|
||||
|
||||
// Emit the arguments to a paramArray method call
|
||||
private void EmitParamArrayArguments(ParameterInfo[] parameters, ExpressionElement[] elements, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
// Get the fixed parameters
|
||||
ParameterInfo[] fixedParameters = new ParameterInfo[_myTargetMethodInfo.MyFixedArgTypes.Length];
|
||||
Array.Copy(parameters, fixedParameters, fixedParameters.Length);
|
||||
|
||||
// Get the corresponding fixed parameters
|
||||
ExpressionElement[] fixedElements = new ExpressionElement[_myTargetMethodInfo.MyFixedArgTypes.Length];
|
||||
Array.Copy(elements, fixedElements, fixedElements.Length);
|
||||
|
||||
// Emit the fixed arguments
|
||||
this.EmitRegularFunctionInternal(fixedParameters, fixedElements, ilg, services);
|
||||
|
||||
// Get the paramArray arguments
|
||||
ExpressionElement[] paramArrayElements = new ExpressionElement[elements.Length - fixedElements.Length];
|
||||
Array.Copy(elements, fixedElements.Length, paramArrayElements, 0, paramArrayElements.Length);
|
||||
|
||||
// Emit them into an array
|
||||
EmitElementArrayLoad(paramArrayElements, _myTargetMethodInfo.ParamArrayElementType, ilg, services);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit elements into an array
|
||||
/// </summary>
|
||||
/// <param name="elements"></param>
|
||||
/// <param name="arrayElementType"></param>
|
||||
/// <param name="ilg"></param>
|
||||
/// <param name="services"></param>
|
||||
private static void EmitElementArrayLoad(ExpressionElement[] elements, Type arrayElementType, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
// Load the array length
|
||||
LiteralElement.EmitLoad(elements.Length, ilg);
|
||||
|
||||
// Create the array
|
||||
ilg.Emit(OpCodes.Newarr, arrayElementType);
|
||||
|
||||
// Store the new array in a unique local and remember the index
|
||||
LocalBuilder local = ilg.DeclareLocal(arrayElementType.MakeArrayType());
|
||||
int arrayLocalIndex = local.LocalIndex;
|
||||
Utility.EmitStoreLocal(ilg, arrayLocalIndex);
|
||||
|
||||
for (int i = 0; i <= elements.Length - 1; i++)
|
||||
{
|
||||
// Load the array
|
||||
Utility.EmitLoadLocal(ilg, arrayLocalIndex);
|
||||
// Load the index
|
||||
LiteralElement.EmitLoad(i, ilg);
|
||||
// Emit the element (with any required conversions)
|
||||
ExpressionElement element = elements[i];
|
||||
element.Emit(ilg, services);
|
||||
ImplicitConverter.EmitImplicitConvert(element.ResultType, arrayElementType, ilg);
|
||||
// Store it into the array
|
||||
Utility.EmitArrayStore(ilg, arrayElementType);
|
||||
}
|
||||
|
||||
// Load the array
|
||||
Utility.EmitLoadLocal(ilg, arrayLocalIndex);
|
||||
}
|
||||
|
||||
public void EmitFunctionCall(bool nextRequiresAddress, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
ParameterInfo[] parameters = this.Method.GetParameters();
|
||||
ExpressionElement[] elements = _myArguments.ToArray();
|
||||
|
||||
// Emit either a regular or paramArray call
|
||||
if (_myTargetMethodInfo.IsParamArray == false)
|
||||
{
|
||||
if (_myTargetMethodInfo.IsExtensionMethod == false)
|
||||
this.EmitRegularFunctionInternal(parameters, elements, ilg, services);
|
||||
else
|
||||
this.EmitExtensionFunctionInternal(parameters, elements, ilg, services);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.EmitParamArrayArguments(parameters, elements, ilg, services);
|
||||
}
|
||||
|
||||
MemberElement.EmitMethodCall(this.ResultType, nextRequiresAddress, this.Method, ilg);
|
||||
}
|
||||
|
||||
private void EmitExtensionFunctionInternal(ParameterInfo[] parameters, ExpressionElement[] elements, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
Debug.Assert(parameters.Length == elements.Length + 1, "argument count mismatch");
|
||||
if (MyPrevious == null) this.EmitLoadOwner(ilg);
|
||||
//Emit each element and any required conversions to the actual parameter type
|
||||
for (int i = 1; i <= parameters.Length - 1; i++)
|
||||
{
|
||||
ExpressionElement element = elements[i - 1];
|
||||
ParameterInfo pi = parameters[i];
|
||||
element.Emit(ilg, services);
|
||||
bool success = ImplicitConverter.EmitImplicitConvert(element.ResultType, pi.ParameterType, ilg);
|
||||
Debug.Assert(success, "conversion failed");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit the arguments to a regular method call
|
||||
/// </summary>
|
||||
/// <param name="parameters"></param>
|
||||
/// <param name="elements"></param>
|
||||
/// <param name="ilg"></param>
|
||||
/// <param name="services"></param>
|
||||
private void EmitRegularFunctionInternal(ParameterInfo[] parameters, ExpressionElement[] elements, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
Debug.Assert(parameters.Length == elements.Length, "argument count mismatch");
|
||||
|
||||
// Emit each element and any required conversions to the actual parameter type
|
||||
for (int i = 0; i <= parameters.Length - 1; i++)
|
||||
{
|
||||
ExpressionElement element = elements[i];
|
||||
ParameterInfo pi = parameters[i];
|
||||
element.Emit(ilg, services);
|
||||
bool success = ImplicitConverter.EmitImplicitConvert(element.ResultType, pi.ParameterType, ilg);
|
||||
Debug.Assert(success, "conversion failed");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The method info we will be calling
|
||||
/// </summary>
|
||||
private MethodInfo Method => _myTargetMethodInfo.Target;
|
||||
|
||||
public override Type ResultType
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((_myOnDemandFunctionReturnType != null))
|
||||
{
|
||||
return _myOnDemandFunctionReturnType;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.Method.ReturnType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool RequiresAddress => !IsGetTypeMethod(this.Method);
|
||||
|
||||
protected override bool IsPublic => this.Method.IsPublic;
|
||||
|
||||
public override bool IsStatic => this.Method.IsStatic;
|
||||
public override bool IsExtensionMethod => this._myTargetMethodInfo.IsExtensionMethod;
|
||||
}
|
||||
}
|
492
ExpressionElements/MemberElements/Identifier.cs
Normal file
492
ExpressionElements/MemberElements/Identifier.cs
Normal file
@@ -0,0 +1,492 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.ComponentModel;
|
||||
using Flee.CalcEngine.PublicTypes;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.ExpressionElements.Base.Literals;
|
||||
using Flee.ExpressionElements.Literals;
|
||||
using Flee.ExpressionElements.Literals.Integral;
|
||||
using Flee.ExpressionElements.Literals.Real;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.MemberElements
|
||||
{
|
||||
[Obsolete("Represents an identifier")]
|
||||
internal class IdentifierElement : MemberElement
|
||||
{
|
||||
private FieldInfo _myField;
|
||||
private PropertyInfo _myProperty;
|
||||
private PropertyDescriptor _myPropertyDescriptor;
|
||||
private Type _myVariableType;
|
||||
private Type _myCalcEngineReferenceType;
|
||||
public IdentifierElement(string name)
|
||||
{
|
||||
this.MyName = name;
|
||||
}
|
||||
|
||||
protected override void ResolveInternal()
|
||||
{
|
||||
// Try to bind to a field or property
|
||||
if (this.ResolveFieldProperty(MyPrevious) == true)
|
||||
{
|
||||
this.AddReferencedVariable(MyPrevious);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to find a variable with our name
|
||||
_myVariableType = MyContext.Variables.GetVariableTypeInternal(MyName);
|
||||
|
||||
// Variables are only usable as the first element
|
||||
if (MyPrevious == null && (_myVariableType != null))
|
||||
{
|
||||
this.AddReferencedVariable(MyPrevious);
|
||||
return;
|
||||
}
|
||||
|
||||
CalculationEngine ce = MyContext.CalculationEngine;
|
||||
|
||||
if ((ce != null))
|
||||
{
|
||||
ce.AddDependency(MyName, MyContext);
|
||||
_myCalcEngineReferenceType = ce.ResolveTailType(MyName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (MyPrevious == null)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.NoIdentifierWithName, CompileExceptionReason.UndefinedName, MyName);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.NoIdentifierWithNameOnType, CompileExceptionReason.UndefinedName, MyName, MyPrevious.TargetType.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ResolveFieldProperty(MemberElement previous)
|
||||
{
|
||||
MemberInfo[] members = this.GetMembers(MemberTypes.Field | MemberTypes.Property);
|
||||
|
||||
// Keep only the ones which are accessible
|
||||
members = this.GetAccessibleMembers(members);
|
||||
|
||||
if (members.Length == 0)
|
||||
{
|
||||
// No accessible members; try to resolve a virtual property
|
||||
return this.ResolveVirtualProperty(previous);
|
||||
}
|
||||
else if (members.Length > 1)
|
||||
{
|
||||
// More than one accessible member
|
||||
if (previous == null)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.IdentifierIsAmbiguous, CompileExceptionReason.AmbiguousMatch, MyName);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.IdentifierIsAmbiguousOnType, CompileExceptionReason.AmbiguousMatch, MyName, previous.TargetType.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only one member; bind to it
|
||||
_myField = members[0] as FieldInfo;
|
||||
if ((_myField != null))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Assume it must be a property
|
||||
_myProperty = (PropertyInfo)members[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ResolveVirtualProperty(MemberElement previous)
|
||||
{
|
||||
if (previous == null)
|
||||
{
|
||||
// We can't use virtual properties if we are the first element
|
||||
return false;
|
||||
}
|
||||
|
||||
PropertyDescriptorCollection coll = TypeDescriptor.GetProperties(previous.ResultType);
|
||||
_myPropertyDescriptor = coll.Find(MyName, true);
|
||||
return (_myPropertyDescriptor != null);
|
||||
}
|
||||
|
||||
private void AddReferencedVariable(MemberElement previous)
|
||||
{
|
||||
if ((previous != null))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((_myVariableType != null) || MyOptions.IsOwnerType(this.MemberOwnerType) == true)
|
||||
{
|
||||
ExpressionInfo info = (ExpressionInfo)MyServices.GetService(typeof(ExpressionInfo));
|
||||
info.AddReferencedVariable(MyName);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
base.Emit(ilg, services);
|
||||
|
||||
this.EmitFirst(ilg);
|
||||
|
||||
if ((_myCalcEngineReferenceType != null))
|
||||
{
|
||||
this.EmitReferenceLoad(ilg);
|
||||
}
|
||||
else if ((_myVariableType != null))
|
||||
{
|
||||
this.EmitVariableLoad(ilg);
|
||||
}
|
||||
else if ((_myField != null))
|
||||
{
|
||||
this.EmitFieldLoad(_myField, ilg, services);
|
||||
}
|
||||
else if ((_myPropertyDescriptor != null))
|
||||
{
|
||||
this.EmitVirtualPropertyLoad(ilg);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.EmitPropertyLoad(_myProperty, ilg);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitReferenceLoad(FleeILGenerator ilg)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldarg_1);
|
||||
MyContext.CalculationEngine.EmitLoad(MyName, ilg);
|
||||
}
|
||||
|
||||
private void EmitFirst(FleeILGenerator ilg)
|
||||
{
|
||||
if ((MyPrevious != null))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool isVariable = (_myVariableType != null);
|
||||
|
||||
if (isVariable == true)
|
||||
{
|
||||
// Load variables
|
||||
EmitLoadVariables(ilg);
|
||||
}
|
||||
else if (MyOptions.IsOwnerType(this.MemberOwnerType) == true & this.IsStatic == false)
|
||||
{
|
||||
this.EmitLoadOwner(ilg);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitVariableLoad(FleeILGenerator ilg)
|
||||
{
|
||||
MethodInfo mi = VariableCollection.GetVariableLoadMethod(_myVariableType);
|
||||
ilg.Emit(OpCodes.Ldstr, MyName);
|
||||
this.EmitMethodCall(mi, ilg);
|
||||
}
|
||||
|
||||
private void EmitFieldLoad(System.Reflection.FieldInfo fi, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
if (fi.IsLiteral == true)
|
||||
{
|
||||
EmitLiteral(fi, ilg, services);
|
||||
}
|
||||
else if (this.ResultType.IsValueType == true & this.NextRequiresAddress == true)
|
||||
{
|
||||
EmitLdfld(fi, true, ilg);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitLdfld(fi, false, ilg);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitLdfld(System.Reflection.FieldInfo fi, bool indirect, FleeILGenerator ilg)
|
||||
{
|
||||
if (fi.IsStatic == true)
|
||||
{
|
||||
if (indirect == true)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldsflda, fi);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldsfld, fi);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (indirect == true)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldflda, fi);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldfld, fi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit the load of a constant field. We can't emit a ldsfld/ldfld of a constant so we have to get its value
|
||||
/// and then emit a ldc.
|
||||
/// </summary>
|
||||
/// <param name="fi"></param>
|
||||
/// <param name="ilg"></param>
|
||||
/// <param name="services"></param>
|
||||
private static void EmitLiteral(System.Reflection.FieldInfo fi, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
object value = fi.GetValue(null);
|
||||
Type t = value.GetType();
|
||||
TypeCode code = Type.GetTypeCode(t);
|
||||
LiteralElement elem = default(LiteralElement);
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case TypeCode.Char:
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.Int32:
|
||||
elem = new Int32LiteralElement(System.Convert.ToInt32(value));
|
||||
break;
|
||||
case TypeCode.UInt32:
|
||||
elem = new UInt32LiteralElement((UInt32)value);
|
||||
break;
|
||||
case TypeCode.Int64:
|
||||
elem = new Int64LiteralElement((Int64)value);
|
||||
break;
|
||||
case TypeCode.UInt64:
|
||||
elem = new UInt64LiteralElement((UInt64)value);
|
||||
break;
|
||||
case TypeCode.Double:
|
||||
elem = new DoubleLiteralElement((double)value);
|
||||
break;
|
||||
case TypeCode.Single:
|
||||
elem = new SingleLiteralElement((float)value);
|
||||
break;
|
||||
case TypeCode.Boolean:
|
||||
elem = new BooleanLiteralElement((bool)value);
|
||||
break;
|
||||
case TypeCode.String:
|
||||
elem = new StringLiteralElement((string)value);
|
||||
break;
|
||||
default:
|
||||
elem = null;
|
||||
Debug.Fail("Unsupported constant type");
|
||||
break;
|
||||
}
|
||||
|
||||
elem.Emit(ilg, services);
|
||||
}
|
||||
|
||||
private void EmitPropertyLoad(System.Reflection.PropertyInfo pi, FleeILGenerator ilg)
|
||||
{
|
||||
System.Reflection.MethodInfo getter = pi.GetGetMethod(true);
|
||||
base.EmitMethodCall(getter, ilg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load a PropertyDescriptor based property
|
||||
/// </summary>
|
||||
/// <param name="ilg"></param>
|
||||
private void EmitVirtualPropertyLoad(FleeILGenerator ilg)
|
||||
{
|
||||
// The previous value is already on the top of the stack but we need it at the bottom
|
||||
|
||||
// Get a temporary local index
|
||||
int index = ilg.GetTempLocalIndex(MyPrevious.ResultType);
|
||||
|
||||
// Store the previous value there
|
||||
Utility.EmitStoreLocal(ilg, index);
|
||||
|
||||
// Load the variable collection
|
||||
EmitLoadVariables(ilg);
|
||||
// Load the property name
|
||||
ilg.Emit(OpCodes.Ldstr, MyName);
|
||||
|
||||
// Load the previous value and convert it to object
|
||||
Utility.EmitLoadLocal(ilg, index);
|
||||
ImplicitConverter.EmitImplicitConvert(MyPrevious.ResultType, typeof(object), ilg);
|
||||
|
||||
// Call the method to get the actual value
|
||||
MethodInfo mi = VariableCollection.GetVirtualPropertyLoadMethod(this.ResultType);
|
||||
this.EmitMethodCall(mi, ilg);
|
||||
}
|
||||
|
||||
private Type MemberOwnerType
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((_myField != null))
|
||||
{
|
||||
return _myField.ReflectedType;
|
||||
}
|
||||
else if ((_myPropertyDescriptor != null))
|
||||
{
|
||||
return _myPropertyDescriptor.ComponentType;
|
||||
}
|
||||
else if ((_myProperty != null))
|
||||
{
|
||||
return _myProperty.ReflectedType;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override System.Type ResultType
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((_myCalcEngineReferenceType != null))
|
||||
{
|
||||
return _myCalcEngineReferenceType;
|
||||
}
|
||||
else if ((_myVariableType != null))
|
||||
{
|
||||
return _myVariableType;
|
||||
}
|
||||
else if ((_myPropertyDescriptor != null))
|
||||
{
|
||||
return _myPropertyDescriptor.PropertyType;
|
||||
}
|
||||
else if ((_myField != null))
|
||||
{
|
||||
return _myField.FieldType;
|
||||
}
|
||||
else
|
||||
{
|
||||
MethodInfo mi = _myProperty.GetGetMethod(true);
|
||||
return mi.ReturnType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool RequiresAddress => _myPropertyDescriptor == null;
|
||||
|
||||
protected override bool IsPublic
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((_myVariableType != null) | (_myCalcEngineReferenceType != null))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if ((_myVariableType != null))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if ((_myPropertyDescriptor != null))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if ((_myField != null))
|
||||
{
|
||||
return _myField.IsPublic;
|
||||
}
|
||||
else
|
||||
{
|
||||
MethodInfo mi = _myProperty.GetGetMethod(true);
|
||||
return mi.IsPublic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool SupportsStatic
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((_myVariableType != null))
|
||||
{
|
||||
// Variables never support static
|
||||
return false;
|
||||
}
|
||||
else if ((_myPropertyDescriptor != null))
|
||||
{
|
||||
// Neither do virtual properties
|
||||
return false;
|
||||
}
|
||||
else if (MyOptions.IsOwnerType(this.MemberOwnerType) == true && MyPrevious == null)
|
||||
{
|
||||
// Owner members support static if we are the first element
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Support static if we are the first (ie: we are a static import)
|
||||
return MyPrevious == null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool SupportsInstance
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((_myVariableType != null))
|
||||
{
|
||||
// Variables always support instance
|
||||
return true;
|
||||
}
|
||||
else if ((_myPropertyDescriptor != null))
|
||||
{
|
||||
// So do virtual properties
|
||||
return true;
|
||||
}
|
||||
else if (MyOptions.IsOwnerType(this.MemberOwnerType) == true && MyPrevious == null)
|
||||
{
|
||||
// Owner members support instance if we are the first element
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We always support instance if we are not the first element
|
||||
return (MyPrevious != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsStatic
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((_myVariableType != null) | (_myCalcEngineReferenceType != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if ((_myVariableType != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if ((_myField != null))
|
||||
{
|
||||
return _myField.IsStatic;
|
||||
}
|
||||
else if ((_myPropertyDescriptor != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
MethodInfo mi = _myProperty.GetGetMethod(true);
|
||||
return mi.IsStatic;
|
||||
}
|
||||
}
|
||||
}
|
||||
public override bool IsExtensionMethod => false;
|
||||
}
|
||||
}
|
181
ExpressionElements/MemberElements/Indexer.cs
Normal file
181
ExpressionElements/MemberElements/Indexer.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.MemberElements
|
||||
{
|
||||
[Obsolete("Element representing an array index")]
|
||||
internal class IndexerElement : MemberElement
|
||||
{
|
||||
private ExpressionElement _myIndexerElement;
|
||||
|
||||
private readonly ArgumentList _myIndexerElements;
|
||||
public IndexerElement(ArgumentList indexer)
|
||||
{
|
||||
_myIndexerElements = indexer;
|
||||
}
|
||||
|
||||
protected override void ResolveInternal()
|
||||
{
|
||||
// Are we are indexing on an array?
|
||||
Type target = MyPrevious.TargetType;
|
||||
|
||||
// Yes, so setup for an array index
|
||||
if (target.IsArray == true)
|
||||
{
|
||||
this.SetupArrayIndexer();
|
||||
return;
|
||||
}
|
||||
|
||||
// Not an array, so try to find an indexer on the type
|
||||
if (this.FindIndexer(target) == false)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.TypeNotArrayAndHasNoIndexerOfType, CompileExceptionReason.TypeMismatch, target.Name, _myIndexerElements);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupArrayIndexer()
|
||||
{
|
||||
_myIndexerElement = _myIndexerElements[0];
|
||||
|
||||
if (_myIndexerElements.Count > 1)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.MultiArrayIndexNotSupported, CompileExceptionReason.TypeMismatch);
|
||||
}
|
||||
else if (ImplicitConverter.EmitImplicitConvert(_myIndexerElement.ResultType, typeof(Int32), null) == false)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.ArrayIndexersMustBeOfType, CompileExceptionReason.TypeMismatch, typeof(Int32).Name);
|
||||
}
|
||||
}
|
||||
|
||||
private bool FindIndexer(Type targetType)
|
||||
{
|
||||
// Get the default members
|
||||
MemberInfo[] members = targetType.GetDefaultMembers();
|
||||
|
||||
List<MethodInfo> methods = new List<MethodInfo>();
|
||||
|
||||
// Use the first one that's valid for our indexer type
|
||||
foreach (MemberInfo mi in members)
|
||||
{
|
||||
PropertyInfo pi = mi as PropertyInfo;
|
||||
if ((pi != null))
|
||||
{
|
||||
methods.Add(pi.GetGetMethod(true));
|
||||
}
|
||||
}
|
||||
|
||||
FunctionCallElement func = new FunctionCallElement("Indexer", methods.ToArray(), _myIndexerElements);
|
||||
func.Resolve(MyServices);
|
||||
_myIndexerElement = func;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
base.Emit(ilg, services);
|
||||
|
||||
if (this.IsArray == true)
|
||||
{
|
||||
this.EmitArrayLoad(ilg, services);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.EmitIndexer(ilg, services);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitArrayLoad(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
_myIndexerElement.Emit(ilg, services);
|
||||
ImplicitConverter.EmitImplicitConvert(_myIndexerElement.ResultType, typeof(Int32), ilg);
|
||||
|
||||
Type elementType = this.ResultType;
|
||||
|
||||
if (elementType.IsValueType == false)
|
||||
{
|
||||
// Simple reference load
|
||||
ilg.Emit(OpCodes.Ldelem_Ref);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.EmitValueTypeArrayLoad(ilg, elementType);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitValueTypeArrayLoad(FleeILGenerator ilg, Type elementType)
|
||||
{
|
||||
if (this.NextRequiresAddress == true)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldelema, elementType);
|
||||
}
|
||||
else
|
||||
{
|
||||
Utility.EmitArrayLoad(ilg, elementType);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitIndexer(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
FunctionCallElement func = (FunctionCallElement)_myIndexerElement;
|
||||
func.EmitFunctionCall(this.NextRequiresAddress, ilg, services);
|
||||
}
|
||||
|
||||
private Type ArrayType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.IsArray == true)
|
||||
{
|
||||
return MyPrevious.TargetType;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsArray => MyPrevious.TargetType.IsArray;
|
||||
|
||||
protected override bool RequiresAddress => this.IsArray == false;
|
||||
|
||||
public override System.Type ResultType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.IsArray == true)
|
||||
{
|
||||
return this.ArrayType.GetElementType();
|
||||
}
|
||||
else
|
||||
{
|
||||
return _myIndexerElement.ResultType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool IsPublic
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.IsArray == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return IsElementPublic((MemberElement)_myIndexerElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsStatic => false;
|
||||
public override bool IsExtensionMethod => false;
|
||||
}
|
||||
}
|
120
ExpressionElements/MemberElements/InvocationList.cs
Normal file
120
ExpressionElements/MemberElements/InvocationList.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using System.Collections;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.MemberElements
|
||||
{
|
||||
internal class InvocationListElement : ExpressionElement
|
||||
{
|
||||
private readonly MemberElement _myTail;
|
||||
public InvocationListElement(IList elements, IServiceProvider services)
|
||||
{
|
||||
this.HandleFirstElement(elements, services);
|
||||
LinkElements(elements);
|
||||
Resolve(elements, services);
|
||||
_myTail = (MemberElement)elements[elements.Count - 1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Arrange elements as a linked list
|
||||
/// </summary>
|
||||
/// <param name="elements"></param>
|
||||
private static void LinkElements(IList elements)
|
||||
{
|
||||
for (int i = 0; i <= elements.Count - 1; i++)
|
||||
{
|
||||
MemberElement current = (MemberElement)elements[i];
|
||||
MemberElement nextElement = null;
|
||||
if (i + 1 < elements.Count)
|
||||
{
|
||||
nextElement = (MemberElement)elements[i + 1];
|
||||
}
|
||||
current.Link(nextElement);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleFirstElement(IList elements, IServiceProvider services)
|
||||
{
|
||||
ExpressionElement first = (ExpressionElement)elements[0];
|
||||
|
||||
// If the first element is not a member element, then we assume it is an expression and replace it with the correct member element
|
||||
if (!(first is MemberElement))
|
||||
{
|
||||
ExpressionMemberElement actualFirst = new ExpressionMemberElement(first);
|
||||
elements[0] = actualFirst;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ResolveNamespaces(elements, services);
|
||||
}
|
||||
}
|
||||
|
||||
private void ResolveNamespaces(IList elements, IServiceProvider services)
|
||||
{
|
||||
ExpressionContext context = (ExpressionContext)services.GetService(typeof(ExpressionContext));
|
||||
ImportBase currentImport = context.Imports.RootImport;
|
||||
|
||||
while (true)
|
||||
{
|
||||
string name = GetName(elements);
|
||||
|
||||
if (name == null)
|
||||
{
|
||||
break; // TODO: might not be correct. Was : Exit While
|
||||
}
|
||||
|
||||
ImportBase import = currentImport.FindImport(name);
|
||||
|
||||
if (import == null)
|
||||
{
|
||||
break; // TODO: might not be correct. Was : Exit While
|
||||
}
|
||||
|
||||
currentImport = import;
|
||||
elements.RemoveAt(0);
|
||||
|
||||
if (elements.Count > 0)
|
||||
{
|
||||
MemberElement newFirst = (MemberElement)elements[0];
|
||||
newFirst.SetImport(currentImport);
|
||||
}
|
||||
}
|
||||
|
||||
if (elements.Count == 0)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.NamespaceCannotBeUsedAsType, CompileExceptionReason.TypeMismatch, currentImport.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetName(IList elements)
|
||||
{
|
||||
if (elements.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Is the first member a field/property element?
|
||||
var fpe = elements[0] as IdentifierElement;
|
||||
|
||||
return fpe?.MemberName;
|
||||
}
|
||||
|
||||
private static void Resolve(IList elements, IServiceProvider services)
|
||||
{
|
||||
foreach (MemberElement element in elements)
|
||||
{
|
||||
element.Resolve(services);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
_myTail.Emit(ilg, services);
|
||||
}
|
||||
|
||||
public override System.Type ResultType => _myTail.ResultType;
|
||||
}
|
||||
}
|
38
ExpressionElements/MemberElements/Miscellaneous.cs
Normal file
38
ExpressionElements/MemberElements/Miscellaneous.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.MemberElements
|
||||
{
|
||||
internal class ExpressionMemberElement : MemberElement
|
||||
{
|
||||
private readonly ExpressionElement _myElement;
|
||||
public ExpressionMemberElement(ExpressionElement element)
|
||||
{
|
||||
_myElement = element;
|
||||
}
|
||||
|
||||
protected override void ResolveInternal()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
base.Emit(ilg, services);
|
||||
_myElement.Emit(ilg, services);
|
||||
if (_myElement.ResultType.IsValueType == true)
|
||||
{
|
||||
EmitValueTypeLoadAddress(ilg, this.ResultType);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool SupportsInstance => true;
|
||||
|
||||
protected override bool IsPublic => true;
|
||||
|
||||
public override bool IsStatic => false;
|
||||
public override bool IsExtensionMethod => false;
|
||||
|
||||
public override System.Type ResultType => _myElement.ResultType;
|
||||
}
|
||||
}
|
56
ExpressionElements/Negate.cs
Normal file
56
ExpressionElements/Negate.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System.Reflection.Emit;
|
||||
using System.Reflection;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
namespace Flee.ExpressionElements
|
||||
{
|
||||
internal class NegateElement : UnaryElement
|
||||
{
|
||||
public NegateElement()
|
||||
{
|
||||
}
|
||||
|
||||
protected override System.Type GetResultType(System.Type childType)
|
||||
{
|
||||
TypeCode tc = Type.GetTypeCode(childType);
|
||||
|
||||
MethodInfo mi = Utility.GetSimpleOverloadedOperator("UnaryNegation", childType, null);
|
||||
if ((mi != null))
|
||||
{
|
||||
return mi.ReturnType;
|
||||
}
|
||||
|
||||
switch (tc)
|
||||
{
|
||||
case TypeCode.Single:
|
||||
case TypeCode.Double:
|
||||
case TypeCode.Int32:
|
||||
case TypeCode.Int64:
|
||||
return childType;
|
||||
case TypeCode.UInt32:
|
||||
return typeof(Int64);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
Type resultType = this.ResultType;
|
||||
MyChild.Emit(ilg, services);
|
||||
ImplicitConverter.EmitImplicitConvert(MyChild.ResultType, resultType, ilg);
|
||||
|
||||
MethodInfo mi = Utility.GetSimpleOverloadedOperator("UnaryNegation", resultType, null);
|
||||
|
||||
if (mi == null)
|
||||
{
|
||||
ilg.Emit(OpCodes.Neg);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Call, mi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
45
ExpressionElements/Root.cs
Normal file
45
ExpressionElements/Root.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
namespace Flee.ExpressionElements
|
||||
{
|
||||
internal class RootExpressionElement : ExpressionElement
|
||||
{
|
||||
private readonly ExpressionElement _myChild;
|
||||
private readonly Type _myResultType;
|
||||
public RootExpressionElement(ExpressionElement child, Type resultType)
|
||||
{
|
||||
_myChild = child;
|
||||
_myResultType = resultType;
|
||||
this.Validate();
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
_myChild.Emit(ilg, services);
|
||||
ImplicitConverter.EmitImplicitConvert(_myChild.ResultType, _myResultType, ilg);
|
||||
|
||||
ExpressionOptions options = (ExpressionOptions)services.GetService(typeof(ExpressionOptions));
|
||||
|
||||
if (options.IsGeneric == false)
|
||||
{
|
||||
ImplicitConverter.EmitImplicitConvert(_myResultType, typeof(object), ilg);
|
||||
}
|
||||
|
||||
ilg.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
private void Validate()
|
||||
{
|
||||
if (ImplicitConverter.EmitImplicitConvert(_myChild.ResultType, _myResultType, null) == false)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.CannotConvertTypeToExpressionResult, CompileExceptionReason.TypeMismatch, _myChild.ResultType.Name, _myResultType.Name);
|
||||
}
|
||||
}
|
||||
|
||||
public override System.Type ResultType => typeof(object);
|
||||
}
|
||||
}
|
136
ExpressionElements/Shift.cs
Normal file
136
ExpressionElements/Shift.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements
|
||||
{
|
||||
internal class ShiftElement : BinaryExpressionElement
|
||||
{
|
||||
private ShiftOperation _myOperation;
|
||||
|
||||
public ShiftElement()
|
||||
{
|
||||
}
|
||||
|
||||
protected override System.Type GetResultType(System.Type leftType, System.Type rightType)
|
||||
{
|
||||
// Right argument (shift count) must be convertible to int32
|
||||
if (ImplicitConverter.EmitImplicitNumericConvert(rightType, typeof(Int32), null) == false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Left argument must be an integer type
|
||||
if (Utility.IsIntegralType(leftType) == false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
TypeCode tc = Type.GetTypeCode(leftType);
|
||||
|
||||
switch (tc)
|
||||
{
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.Int32:
|
||||
return typeof(Int32);
|
||||
case TypeCode.UInt32:
|
||||
return typeof(UInt32);
|
||||
case TypeCode.Int64:
|
||||
return typeof(Int64);
|
||||
case TypeCode.UInt64:
|
||||
return typeof(UInt64);
|
||||
default:
|
||||
Debug.Assert(false, "unknown left shift operand");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void GetOperation(object operation)
|
||||
{
|
||||
_myOperation = (ShiftOperation)operation;
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
MyLeftChild.Emit(ilg, services);
|
||||
this.EmitShiftCount(ilg, services);
|
||||
this.EmitShift(ilg);
|
||||
}
|
||||
|
||||
// If the shift count is greater than the number of bits in the number, the result is undefined.
|
||||
// So we play it safe and force the shift count to 32/64 bits by ANDing it with the appropriate mask.
|
||||
private void EmitShiftCount(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
MyRightChild.Emit(ilg, services);
|
||||
TypeCode tc = Type.GetTypeCode(MyLeftChild.ResultType);
|
||||
switch (tc)
|
||||
{
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.Int32:
|
||||
case TypeCode.UInt32:
|
||||
ilg.Emit(OpCodes.Ldc_I4_S, Convert.ToSByte(0x1f));
|
||||
break;
|
||||
case TypeCode.Int64:
|
||||
case TypeCode.UInt64:
|
||||
ilg.Emit(OpCodes.Ldc_I4_S, Convert.ToSByte(0x3f));
|
||||
break;
|
||||
default:
|
||||
Debug.Assert(false, "unknown left shift operand");
|
||||
break;
|
||||
}
|
||||
|
||||
ilg.Emit(OpCodes.And);
|
||||
}
|
||||
|
||||
private void EmitShift(FleeILGenerator ilg)
|
||||
{
|
||||
TypeCode tc = Type.GetTypeCode(MyLeftChild.ResultType);
|
||||
OpCode op = default(OpCode);
|
||||
|
||||
switch (tc)
|
||||
{
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.Int32:
|
||||
case TypeCode.Int64:
|
||||
// Signed operand, emit a left shift or arithmetic right shift
|
||||
if (_myOperation == ShiftOperation.LeftShift)
|
||||
{
|
||||
op = OpCodes.Shl;
|
||||
}
|
||||
else
|
||||
{
|
||||
op = OpCodes.Shr;
|
||||
}
|
||||
break;
|
||||
case TypeCode.UInt32:
|
||||
case TypeCode.UInt64:
|
||||
// Unsigned operand, emit left shift or logical right shift
|
||||
if (_myOperation == ShiftOperation.LeftShift)
|
||||
{
|
||||
op = OpCodes.Shl;
|
||||
}
|
||||
else
|
||||
{
|
||||
op = OpCodes.Shr_Un;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Debug.Assert(false, "unknown left shift operand");
|
||||
break;
|
||||
}
|
||||
|
||||
ilg.Emit(op);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user