Flee
This commit is contained in:
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;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user