Flee
This commit is contained in:
285
InternalTypes/BranchManager.cs
Normal file
285
InternalTypes/BranchManager.cs
Normal file
@@ -0,0 +1,285 @@
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Flee.InternalTypes
|
||||
{
|
||||
[Obsolete("Manages branch information and allows us to determine if we should emit a short or long branch")]
|
||||
internal class BranchManager
|
||||
{
|
||||
private readonly IList<BranchInfo> MyBranchInfos;
|
||||
|
||||
public BranchManager()
|
||||
{
|
||||
MyBranchInfos = new List<BranchInfo>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// check if any long branches exist
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool HasLongBranches()
|
||||
{
|
||||
foreach (BranchInfo bi in MyBranchInfos)
|
||||
{
|
||||
if (bi.ComputeIsLongBranch()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether to use short or long branches.
|
||||
/// This advances the ilg offset with No-op to adjust
|
||||
/// for the long branches needed.
|
||||
/// </summary>
|
||||
/// <remarks></remarks>
|
||||
public bool ComputeBranches()
|
||||
{
|
||||
//
|
||||
// we need to iterate in reverse order of the
|
||||
// starting location, as branch between our
|
||||
// branch could push our branch to a long branch.
|
||||
//
|
||||
for( var idx=MyBranchInfos.Count-1; idx >= 0; idx--)
|
||||
{
|
||||
var bi = MyBranchInfos[idx];
|
||||
|
||||
// count long branches between
|
||||
int longBranchesBetween = 0;
|
||||
for( var ii=idx+1; ii < MyBranchInfos.Count; ii++)
|
||||
{
|
||||
var bi2 = MyBranchInfos[ii];
|
||||
if (bi2.IsBetween(bi) && bi2.ComputeIsLongBranch())
|
||||
++longBranchesBetween;
|
||||
}
|
||||
|
||||
// Adjust the branch as necessary
|
||||
bi.AdjustForLongBranchesBetween(longBranchesBetween);
|
||||
}
|
||||
|
||||
int longBranchCount = 0;
|
||||
|
||||
// Adjust the start location of each branch
|
||||
foreach (BranchInfo bi in MyBranchInfos)
|
||||
{
|
||||
// Save the short/long branch type
|
||||
bi.BakeIsLongBranch();
|
||||
|
||||
// Adjust the start location as necessary
|
||||
bi.AdjustForLongBranches(longBranchCount);
|
||||
|
||||
// Keep a tally of the number of long branches
|
||||
longBranchCount += Convert.ToInt32(bi.IsLongBranch);
|
||||
}
|
||||
|
||||
return (longBranchCount > 0);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determine if a branch from a point to a label will be long
|
||||
/// </summary>
|
||||
/// <param name="ilg"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks></remarks>
|
||||
public bool IsLongBranch(FleeILGenerator ilg)
|
||||
{
|
||||
ILLocation startLoc = new ILLocation(ilg.Length);
|
||||
|
||||
foreach (var bi in MyBranchInfos)
|
||||
{
|
||||
if (bi.Equals(startLoc))
|
||||
return bi.IsLongBranch;
|
||||
}
|
||||
|
||||
// we don't really know since this branch didn't exist.
|
||||
// we could throw an exceptio but
|
||||
// do a long branch to be safe.
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a branch from a location to a target label
|
||||
/// </summary>
|
||||
/// <param name="ilg"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <remarks></remarks>
|
||||
public void AddBranch(FleeILGenerator ilg, Label target)
|
||||
{
|
||||
ILLocation startLoc = new ILLocation(ilg.Length);
|
||||
|
||||
BranchInfo bi = new BranchInfo(startLoc, target);
|
||||
// branches will be sorted in order
|
||||
MyBranchInfos.Add(bi);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Set the position for a label
|
||||
/// </summary>
|
||||
/// <param name="ilg"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <remarks></remarks>
|
||||
public void MarkLabel(FleeILGenerator ilg, Label target)
|
||||
{
|
||||
int pos = ilg.Length;
|
||||
|
||||
foreach (BranchInfo bi in MyBranchInfos)
|
||||
{
|
||||
bi.Mark(target, pos);
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string[] arr = new string[MyBranchInfos.Count];
|
||||
|
||||
for (int i = 0; i <= MyBranchInfos.Count - 1; i++)
|
||||
{
|
||||
arr[i] = MyBranchInfos[i].ToString();
|
||||
}
|
||||
|
||||
return string.Join(System.Environment.NewLine, arr);
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Represents a location in an IL stream")]
|
||||
internal class ILLocation : IEquatable<ILLocation>, IComparable<ILLocation>
|
||||
{
|
||||
private int _myPosition;
|
||||
|
||||
/// <summary>
|
||||
/// ' Long branch is 5 bytes; short branch is 2; so we adjust by the difference
|
||||
/// </summary>
|
||||
private const int LongBranchAdjust = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Length of the Br_s opcode
|
||||
/// </summary>
|
||||
private const int BrSLength = 2;
|
||||
|
||||
public ILLocation()
|
||||
{
|
||||
}
|
||||
|
||||
public ILLocation(int position)
|
||||
{
|
||||
_myPosition = position;
|
||||
}
|
||||
|
||||
public void SetPosition(int position)
|
||||
{
|
||||
_myPosition = position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjust our position by a certain amount of long branches
|
||||
/// </summary>
|
||||
/// <param name="longBranchCount"></param>
|
||||
/// <remarks></remarks>
|
||||
public void AdjustForLongBranch(int longBranchCount)
|
||||
{
|
||||
_myPosition += longBranchCount * LongBranchAdjust;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if this branch is long
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks></remarks>
|
||||
public bool IsLongBranch(ILLocation target)
|
||||
{
|
||||
// The branch offset is relative to the instruction *after* the branch so we add 2 (length of a br_s) to our position
|
||||
return Utility.IsLongBranch(_myPosition + BrSLength, target._myPosition);
|
||||
}
|
||||
|
||||
public bool Equals1(ILLocation other)
|
||||
{
|
||||
return _myPosition == other._myPosition;
|
||||
}
|
||||
bool System.IEquatable<ILLocation>.Equals(ILLocation other)
|
||||
{
|
||||
return Equals1(other);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _myPosition.ToString("x");
|
||||
}
|
||||
|
||||
public int CompareTo(ILLocation other)
|
||||
{
|
||||
return _myPosition.CompareTo(other._myPosition);
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Represents a branch from a start location to an end location")]
|
||||
internal class BranchInfo
|
||||
{
|
||||
private readonly ILLocation _myStart;
|
||||
private readonly ILLocation _myEnd;
|
||||
private Label _myLabel;
|
||||
private bool _myIsLongBranch;
|
||||
|
||||
public BranchInfo(ILLocation startLocation, Label endLabel)
|
||||
{
|
||||
_myStart = startLocation;
|
||||
_myLabel = endLabel;
|
||||
_myEnd = new ILLocation();
|
||||
}
|
||||
|
||||
public void AdjustForLongBranches(int longBranchCount)
|
||||
{
|
||||
_myStart.AdjustForLongBranch(longBranchCount);
|
||||
// end not necessarily needed once we determine
|
||||
// if this is long, but keep it accurate anyway.
|
||||
_myEnd.AdjustForLongBranch(longBranchCount);
|
||||
}
|
||||
|
||||
public void BakeIsLongBranch()
|
||||
{
|
||||
_myIsLongBranch = this.ComputeIsLongBranch();
|
||||
}
|
||||
|
||||
public void AdjustForLongBranchesBetween(int betweenLongBranchCount)
|
||||
{
|
||||
_myEnd.AdjustForLongBranch(betweenLongBranchCount);
|
||||
}
|
||||
|
||||
public bool IsBetween(BranchInfo other)
|
||||
{
|
||||
return _myStart.CompareTo(other._myStart) > 0 && _myStart.CompareTo(other._myEnd) < 0;
|
||||
}
|
||||
|
||||
public bool ComputeIsLongBranch()
|
||||
{
|
||||
return _myStart.IsLongBranch(_myEnd);
|
||||
}
|
||||
|
||||
public void Mark(Label target, int position)
|
||||
{
|
||||
if (_myLabel.Equals(target) == true)
|
||||
{
|
||||
_myEnd.SetPosition(position);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We only need to compare the start point. Can only have a single
|
||||
/// brach from the exact address, so if label doesn't match we have
|
||||
/// bigger problems.
|
||||
/// </summary>
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
public bool Equals(ILLocation start)
|
||||
{
|
||||
return _myStart.Equals1(start);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{_myStart} -> {_myEnd} (L={_myStart.IsLongBranch(_myEnd)})";
|
||||
}
|
||||
|
||||
public bool IsLongBranch => _myIsLongBranch;
|
||||
}
|
||||
}
|
215
InternalTypes/Expression.cs
Normal file
215
InternalTypes/Expression.cs
Normal file
@@ -0,0 +1,215 @@
|
||||
using System.ComponentModel.Design;
|
||||
using System.Reflection.Emit;
|
||||
using System.Reflection;
|
||||
using Flee.ExpressionElements;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
using IDynamicExpression = Flee.PublicTypes.IDynamicExpression;
|
||||
|
||||
namespace Flee.InternalTypes
|
||||
{
|
||||
internal class Expression<T> : IExpression, IDynamicExpression, IGenericExpression<T>
|
||||
{
|
||||
private readonly string _myExpression;
|
||||
private ExpressionContext _myContext;
|
||||
private ExpressionOptions _myOptions;
|
||||
private readonly ExpressionInfo _myInfo;
|
||||
private ExpressionEvaluator<T> _myEvaluator;
|
||||
|
||||
private object _myOwner;
|
||||
private const string EmitAssemblyName = "FleeExpression";
|
||||
|
||||
private const string DynamicMethodName = "Flee Expression";
|
||||
public Expression(string expression, ExpressionContext context, bool isGeneric)
|
||||
{
|
||||
Utility.AssertNotNull(expression, "expression");
|
||||
_myExpression = expression;
|
||||
_myOwner = context.ExpressionOwner;
|
||||
|
||||
_myContext = context;
|
||||
|
||||
if (context.NoClone == false)
|
||||
{
|
||||
_myContext = context.CloneInternal(false);
|
||||
}
|
||||
|
||||
_myInfo = new ExpressionInfo();
|
||||
|
||||
this.SetupOptions(_myContext.Options, isGeneric);
|
||||
|
||||
_myContext.Imports.ImportOwner(_myOptions.OwnerType);
|
||||
|
||||
this.ValidateOwner(_myOwner);
|
||||
|
||||
this.Compile(expression, _myOptions);
|
||||
|
||||
_myContext.CalculationEngine?.FixTemporaryHead(this, _myContext, _myOptions.ResultType);
|
||||
}
|
||||
|
||||
private void SetupOptions(ExpressionOptions options, bool isGeneric)
|
||||
{
|
||||
// Make sure we clone the options
|
||||
_myOptions = options;
|
||||
_myOptions.IsGeneric = isGeneric;
|
||||
|
||||
if (isGeneric)
|
||||
{
|
||||
_myOptions.ResultType = typeof(T);
|
||||
}
|
||||
|
||||
_myOptions.SetOwnerType(_myOwner.GetType());
|
||||
}
|
||||
|
||||
private void Compile(string expression, ExpressionOptions options)
|
||||
{
|
||||
// Add the services that will be used by elements during the compile
|
||||
IServiceContainer services = new ServiceContainer();
|
||||
this.AddServices(services);
|
||||
|
||||
// Parse and get the root element of the parse tree
|
||||
ExpressionElement topElement = _myContext.Parse(expression, services);
|
||||
|
||||
if (options.ResultType == null)
|
||||
{
|
||||
options.ResultType = topElement.ResultType;
|
||||
}
|
||||
|
||||
RootExpressionElement rootElement = new RootExpressionElement(topElement, options.ResultType);
|
||||
|
||||
DynamicMethod dm = this.CreateDynamicMethod();
|
||||
|
||||
FleeILGenerator ilg = new FleeILGenerator(dm.GetILGenerator());
|
||||
|
||||
// Emit the IL
|
||||
rootElement.Emit(ilg, services);
|
||||
if (ilg.NeedsSecondPass())
|
||||
{
|
||||
// second pass required due to long branches.
|
||||
dm = this.CreateDynamicMethod();
|
||||
ilg.PrepareSecondPass(dm.GetILGenerator());
|
||||
rootElement.Emit(ilg, services);
|
||||
}
|
||||
|
||||
ilg.ValidateLength();
|
||||
|
||||
// Emit to an assembly if required
|
||||
if (options.EmitToAssembly == true)
|
||||
{
|
||||
EmitToAssembly(ilg, rootElement, services);
|
||||
}
|
||||
|
||||
Type delegateType = typeof(ExpressionEvaluator<>).MakeGenericType(typeof(T));
|
||||
_myEvaluator = (ExpressionEvaluator<T>)dm.CreateDelegate(delegateType);
|
||||
}
|
||||
|
||||
private DynamicMethod CreateDynamicMethod()
|
||||
{
|
||||
// Create the dynamic method
|
||||
Type[] parameterTypes = {
|
||||
typeof(object),
|
||||
typeof(ExpressionContext),
|
||||
typeof(VariableCollection)
|
||||
};
|
||||
DynamicMethod dm = default(DynamicMethod);
|
||||
|
||||
dm = new DynamicMethod(DynamicMethodName, typeof(T), parameterTypes, _myOptions.OwnerType);
|
||||
|
||||
return dm;
|
||||
}
|
||||
|
||||
private void AddServices(IServiceContainer dest)
|
||||
{
|
||||
dest.AddService(typeof(ExpressionOptions), _myOptions);
|
||||
dest.AddService(typeof(ExpressionParserOptions), _myContext.ParserOptions);
|
||||
dest.AddService(typeof(ExpressionContext), _myContext);
|
||||
dest.AddService(typeof(IExpression), this);
|
||||
dest.AddService(typeof(ExpressionInfo), _myInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit to an assembly. We've already computed long branches at this point,
|
||||
/// so we emit as a second pass
|
||||
/// </summary>
|
||||
/// <param name="ilg"></param>
|
||||
/// <param name="rootElement"></param>
|
||||
/// <param name="services"></param>
|
||||
private static void EmitToAssembly(FleeILGenerator ilg, ExpressionElement rootElement, IServiceContainer services)
|
||||
{
|
||||
AssemblyName assemblyName = new AssemblyName(EmitAssemblyName);
|
||||
|
||||
string assemblyFileName = string.Format("{0}.dll", EmitAssemblyName);
|
||||
|
||||
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
|
||||
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyFileName);
|
||||
|
||||
MethodBuilder mb = moduleBuilder.DefineGlobalMethod("Evaluate", MethodAttributes.Public | MethodAttributes.Static, typeof(T), new Type[] {
|
||||
typeof(object),typeof(ExpressionContext),typeof(VariableCollection)});
|
||||
// already emitted once for local use,
|
||||
ilg.PrepareSecondPass(mb.GetILGenerator());
|
||||
|
||||
rootElement.Emit(ilg, services);
|
||||
|
||||
moduleBuilder.CreateGlobalFunctions();
|
||||
//assemblyBuilder.Save(assemblyFileName);
|
||||
assemblyBuilder.CreateInstance(assemblyFileName);
|
||||
}
|
||||
|
||||
private void ValidateOwner(object owner)
|
||||
{
|
||||
Utility.AssertNotNull(owner, "owner");
|
||||
if (_myOptions.OwnerType.IsAssignableFrom(owner.GetType()) == false)
|
||||
{
|
||||
string msg = Utility.GetGeneralErrorMessage(GeneralErrorResourceKeys.NewOwnerTypeNotAssignableToCurrentOwner);
|
||||
throw new ArgumentException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public object Evaluate()
|
||||
{
|
||||
return _myEvaluator(_myOwner, _myContext, _myContext.Variables);
|
||||
}
|
||||
|
||||
public T EvaluateGeneric()
|
||||
{
|
||||
return _myEvaluator(_myOwner, _myContext, _myContext.Variables);
|
||||
}
|
||||
T IGenericExpression<T>.Evaluate()
|
||||
{
|
||||
return EvaluateGeneric();
|
||||
}
|
||||
|
||||
public IExpression Clone()
|
||||
{
|
||||
Expression<T> copy = (Expression<T>)this.MemberwiseClone();
|
||||
copy._myContext = _myContext.CloneInternal(true);
|
||||
copy._myOptions = copy._myContext.Options;
|
||||
return copy;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _myExpression;
|
||||
}
|
||||
|
||||
internal Type ResultType => _myOptions.ResultType;
|
||||
|
||||
public string Text => _myExpression;
|
||||
|
||||
public ExpressionInfo Info1 => _myInfo;
|
||||
|
||||
ExpressionInfo IExpression.Info => Info1;
|
||||
|
||||
public object Owner
|
||||
{
|
||||
get { return _myOwner; }
|
||||
set
|
||||
{
|
||||
this.ValidateOwner(value);
|
||||
_myOwner = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ExpressionContext Context => _myContext;
|
||||
}
|
||||
}
|
269
InternalTypes/FleeILGenerator.cs
Normal file
269
InternalTypes/FleeILGenerator.cs
Normal file
@@ -0,0 +1,269 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Flee.InternalTypes
|
||||
{
|
||||
internal class FleeILGenerator
|
||||
{
|
||||
private ILGenerator _myIlGenerator;
|
||||
private int _myLength;
|
||||
private int _myLabelCount;
|
||||
private readonly Dictionary<Type, LocalBuilder> _localBuilderTemp;
|
||||
private int _myPass;
|
||||
private int _brContext;
|
||||
private BranchManager _bm;
|
||||
|
||||
public FleeILGenerator(ILGenerator ilg)
|
||||
{
|
||||
_myIlGenerator = ilg;
|
||||
_localBuilderTemp = new Dictionary<Type, LocalBuilder>();
|
||||
_myLength = 0;
|
||||
_myPass = 1;
|
||||
_bm = new BranchManager();
|
||||
}
|
||||
|
||||
public int GetTempLocalIndex(Type localType)
|
||||
{
|
||||
LocalBuilder local = null;
|
||||
|
||||
if (_localBuilderTemp.TryGetValue(localType, out local) == false)
|
||||
{
|
||||
local = _myIlGenerator.DeclareLocal(localType);
|
||||
_localBuilderTemp.Add(localType, local);
|
||||
}
|
||||
|
||||
return local.LocalIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// after first pass, check for long branches.
|
||||
/// If any, we need to generate again.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool NeedsSecondPass()
|
||||
{
|
||||
return _bm.HasLongBranches();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// need a new ILGenerator for 2nd pass. This can also
|
||||
/// get called for a 3rd pass when emitting to assembly.
|
||||
/// </summary>
|
||||
/// <param name="ilg"></param>
|
||||
public void PrepareSecondPass(ILGenerator ilg)
|
||||
{
|
||||
_bm.ComputeBranches();
|
||||
_localBuilderTemp.Clear();
|
||||
_myIlGenerator = ilg;
|
||||
_myLength = 0;
|
||||
_myPass++;
|
||||
}
|
||||
|
||||
public void Emit(OpCode op)
|
||||
{
|
||||
this.RecordOpcode(op);
|
||||
_myIlGenerator.Emit(op);
|
||||
}
|
||||
|
||||
public void Emit(OpCode op, Type arg)
|
||||
{
|
||||
this.RecordOpcode(op);
|
||||
_myIlGenerator.Emit(op, arg);
|
||||
}
|
||||
|
||||
public void Emit(OpCode op, ConstructorInfo arg)
|
||||
{
|
||||
this.RecordOpcode(op);
|
||||
_myIlGenerator.Emit(op, arg);
|
||||
}
|
||||
|
||||
public void Emit(OpCode op, MethodInfo arg)
|
||||
{
|
||||
this.RecordOpcode(op);
|
||||
_myIlGenerator.Emit(op, arg);
|
||||
}
|
||||
|
||||
public void Emit(OpCode op, FieldInfo arg)
|
||||
{
|
||||
this.RecordOpcode(op);
|
||||
_myIlGenerator.Emit(op, arg);
|
||||
}
|
||||
|
||||
public void Emit(OpCode op, byte arg)
|
||||
{
|
||||
this.RecordOpcode(op);
|
||||
_myIlGenerator.Emit(op, arg);
|
||||
}
|
||||
|
||||
public void Emit(OpCode op, sbyte arg)
|
||||
{
|
||||
this.RecordOpcode(op);
|
||||
_myIlGenerator.Emit(op, arg);
|
||||
}
|
||||
|
||||
public void Emit(OpCode op, short arg)
|
||||
{
|
||||
this.RecordOpcode(op);
|
||||
_myIlGenerator.Emit(op, arg);
|
||||
}
|
||||
|
||||
public void Emit(OpCode op, int arg)
|
||||
{
|
||||
this.RecordOpcode(op);
|
||||
_myIlGenerator.Emit(op, arg);
|
||||
}
|
||||
|
||||
public void Emit(OpCode op, long arg)
|
||||
{
|
||||
this.RecordOpcode(op);
|
||||
_myIlGenerator.Emit(op, arg);
|
||||
}
|
||||
|
||||
public void Emit(OpCode op, float arg)
|
||||
{
|
||||
this.RecordOpcode(op);
|
||||
_myIlGenerator.Emit(op, arg);
|
||||
}
|
||||
|
||||
public void Emit(OpCode op, double arg)
|
||||
{
|
||||
this.RecordOpcode(op);
|
||||
_myIlGenerator.Emit(op, arg);
|
||||
}
|
||||
|
||||
public void Emit(OpCode op, string arg)
|
||||
{
|
||||
this.RecordOpcode(op);
|
||||
_myIlGenerator.Emit(op, arg);
|
||||
}
|
||||
|
||||
public void Emit(OpCode op, Label arg)
|
||||
{
|
||||
this.RecordOpcode(op);
|
||||
_myIlGenerator.Emit(op, arg);
|
||||
}
|
||||
|
||||
public void EmitBranch(Label arg)
|
||||
{
|
||||
if (_myPass == 1)
|
||||
{
|
||||
_bm.AddBranch(this, arg);
|
||||
Emit(OpCodes.Br_S, arg);
|
||||
}
|
||||
else if (_bm.IsLongBranch(this) == false)
|
||||
{
|
||||
Emit(OpCodes.Br_S, arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
Emit(OpCodes.Br, arg);
|
||||
}
|
||||
}
|
||||
|
||||
public void EmitBranchFalse(Label arg)
|
||||
{
|
||||
if (_myPass == 1)
|
||||
{
|
||||
_bm.AddBranch(this, arg);
|
||||
Emit(OpCodes.Brfalse_S, arg);
|
||||
}
|
||||
else if (_bm.IsLongBranch(this) == false)
|
||||
{
|
||||
Emit(OpCodes.Brfalse_S, arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
Emit(OpCodes.Brfalse, arg);
|
||||
}
|
||||
}
|
||||
|
||||
public void EmitBranchTrue(Label arg)
|
||||
{
|
||||
if (_myPass == 1)
|
||||
{
|
||||
_bm.AddBranch(this, arg);
|
||||
Emit(OpCodes.Brtrue_S, arg);
|
||||
}
|
||||
else if (_bm.IsLongBranch(this) == false)
|
||||
{
|
||||
Emit(OpCodes.Brtrue_S, arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
Emit(OpCodes.Brtrue, arg);
|
||||
}
|
||||
}
|
||||
|
||||
public void MarkLabel(Label lbl)
|
||||
{
|
||||
_myIlGenerator.MarkLabel(lbl);
|
||||
_bm.MarkLabel(this, lbl);
|
||||
}
|
||||
|
||||
|
||||
public Label DefineLabel()
|
||||
{
|
||||
_myLabelCount += 1;
|
||||
var label = _myIlGenerator.DefineLabel();
|
||||
return label;
|
||||
}
|
||||
|
||||
|
||||
public LocalBuilder DeclareLocal(Type localType)
|
||||
{
|
||||
return _myIlGenerator.DeclareLocal(localType);
|
||||
}
|
||||
|
||||
private void RecordOpcode(OpCode op)
|
||||
{
|
||||
//Trace.WriteLine(String.Format("{0:x}: {1}", MyLength, op.Name))
|
||||
int operandLength = GetOpcodeOperandSize(op.OperandType);
|
||||
_myLength += op.Size + operandLength;
|
||||
}
|
||||
|
||||
private static int GetOpcodeOperandSize(OperandType operand)
|
||||
{
|
||||
switch (operand)
|
||||
{
|
||||
case OperandType.InlineNone:
|
||||
return 0;
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
case OperandType.ShortInlineI:
|
||||
case OperandType.ShortInlineVar:
|
||||
return 1;
|
||||
case OperandType.InlineVar:
|
||||
return 2;
|
||||
case OperandType.InlineBrTarget:
|
||||
case OperandType.InlineField:
|
||||
case OperandType.InlineI:
|
||||
case OperandType.InlineMethod:
|
||||
case OperandType.InlineSig:
|
||||
case OperandType.InlineString:
|
||||
case OperandType.InlineTok:
|
||||
case OperandType.InlineType:
|
||||
case OperandType.ShortInlineR:
|
||||
return 4;
|
||||
case OperandType.InlineI8:
|
||||
case OperandType.InlineR:
|
||||
return 8;
|
||||
default:
|
||||
Debug.Fail("Unknown operand type");
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
public void ValidateLength()
|
||||
{
|
||||
Debug.Assert(this.Length == this.ILGeneratorLength, "ILGenerator length mismatch");
|
||||
}
|
||||
|
||||
public int Length => _myLength;
|
||||
|
||||
public int LabelCount => _myLabelCount;
|
||||
|
||||
private int ILGeneratorLength => Utility.GetILGeneratorLength(_myIlGenerator);
|
||||
}
|
||||
}
|
573
InternalTypes/ImplicitConversions.cs
Normal file
573
InternalTypes/ImplicitConversions.cs
Normal file
@@ -0,0 +1,573 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection.Emit;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Flee.InternalTypes
|
||||
{
|
||||
internal class ImplicitConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Table of results for binary operations using primitives
|
||||
/// </summary>
|
||||
private static readonly Type[,] OurBinaryResultTable;
|
||||
|
||||
/// <summary>
|
||||
/// Primitive types we support
|
||||
/// </summary>
|
||||
private static readonly Type[] OurBinaryTypes;
|
||||
static ImplicitConverter()
|
||||
{
|
||||
// Create a table with all the primitive types
|
||||
Type[] types = {
|
||||
typeof(char),
|
||||
typeof(byte),
|
||||
typeof(sbyte),
|
||||
typeof(Int16),
|
||||
typeof(UInt16),
|
||||
typeof(Int32),
|
||||
typeof(UInt32),
|
||||
typeof(Int64),
|
||||
typeof(UInt64),
|
||||
typeof(float),
|
||||
typeof(double)
|
||||
};
|
||||
OurBinaryTypes = types;
|
||||
Type[,] table = new Type[types.Length, types.Length];
|
||||
OurBinaryResultTable = table;
|
||||
FillIdentities(types, table);
|
||||
|
||||
// Fill the table
|
||||
AddEntry(typeof(UInt32), typeof(UInt64), typeof(UInt64));
|
||||
AddEntry(typeof(Int32), typeof(Int64), typeof(Int64));
|
||||
AddEntry(typeof(UInt32), typeof(Int64), typeof(Int64));
|
||||
AddEntry(typeof(Int32), typeof(UInt32), typeof(Int64));
|
||||
AddEntry(typeof(UInt32), typeof(float), typeof(float));
|
||||
AddEntry(typeof(UInt32), typeof(double), typeof(double));
|
||||
AddEntry(typeof(Int32), typeof(float), typeof(float));
|
||||
AddEntry(typeof(Int32), typeof(double), typeof(double));
|
||||
AddEntry(typeof(Int64), typeof(float), typeof(float));
|
||||
AddEntry(typeof(Int64), typeof(double), typeof(double));
|
||||
AddEntry(typeof(UInt64), typeof(float), typeof(float));
|
||||
AddEntry(typeof(UInt64), typeof(double), typeof(double));
|
||||
AddEntry(typeof(float), typeof(double), typeof(double));
|
||||
|
||||
// Byte
|
||||
AddEntry(typeof(byte), typeof(byte), typeof(Int32));
|
||||
AddEntry(typeof(byte), typeof(sbyte), typeof(Int32));
|
||||
AddEntry(typeof(byte), typeof(Int16), typeof(Int32));
|
||||
AddEntry(typeof(byte), typeof(UInt16), typeof(Int32));
|
||||
AddEntry(typeof(byte), typeof(Int32), typeof(Int32));
|
||||
AddEntry(typeof(byte), typeof(UInt32), typeof(UInt32));
|
||||
AddEntry(typeof(byte), typeof(Int64), typeof(Int64));
|
||||
AddEntry(typeof(byte), typeof(UInt64), typeof(UInt64));
|
||||
AddEntry(typeof(byte), typeof(float), typeof(float));
|
||||
AddEntry(typeof(byte), typeof(double), typeof(double));
|
||||
|
||||
// SByte
|
||||
AddEntry(typeof(sbyte), typeof(sbyte), typeof(Int32));
|
||||
AddEntry(typeof(sbyte), typeof(Int16), typeof(Int32));
|
||||
AddEntry(typeof(sbyte), typeof(UInt16), typeof(Int32));
|
||||
AddEntry(typeof(sbyte), typeof(Int32), typeof(Int32));
|
||||
AddEntry(typeof(sbyte), typeof(UInt32), typeof(long));
|
||||
AddEntry(typeof(sbyte), typeof(Int64), typeof(Int64));
|
||||
//invalid -- AddEntry(GetType(SByte), GetType(UInt64), GetType(UInt64))
|
||||
AddEntry(typeof(sbyte), typeof(float), typeof(float));
|
||||
AddEntry(typeof(sbyte), typeof(double), typeof(double));
|
||||
|
||||
// int16
|
||||
AddEntry(typeof(Int16), typeof(Int16), typeof(Int32));
|
||||
AddEntry(typeof(Int16), typeof(UInt16), typeof(Int32));
|
||||
AddEntry(typeof(Int16), typeof(Int32), typeof(Int32));
|
||||
AddEntry(typeof(Int16), typeof(UInt32), typeof(long));
|
||||
AddEntry(typeof(Int16), typeof(Int64), typeof(Int64));
|
||||
//invalid -- AddEntry(GetType(Int16), GetType(UInt64), GetType(UInt64))
|
||||
AddEntry(typeof(Int16), typeof(float), typeof(float));
|
||||
AddEntry(typeof(Int16), typeof(double), typeof(double));
|
||||
|
||||
// Uint16
|
||||
AddEntry(typeof(UInt16), typeof(UInt16), typeof(Int32));
|
||||
AddEntry(typeof(UInt16), typeof(Int16), typeof(Int32));
|
||||
AddEntry(typeof(UInt16), typeof(Int32), typeof(Int32));
|
||||
AddEntry(typeof(UInt16), typeof(UInt32), typeof(UInt32));
|
||||
AddEntry(typeof(UInt16), typeof(Int64), typeof(Int64));
|
||||
AddEntry(typeof(UInt16), typeof(UInt64), typeof(UInt64));
|
||||
AddEntry(typeof(UInt16), typeof(float), typeof(float));
|
||||
AddEntry(typeof(UInt16), typeof(double), typeof(double));
|
||||
|
||||
// Char
|
||||
AddEntry(typeof(char), typeof(char), typeof(Int32));
|
||||
AddEntry(typeof(char), typeof(UInt16), typeof(UInt16));
|
||||
AddEntry(typeof(char), typeof(Int32), typeof(Int32));
|
||||
AddEntry(typeof(char), typeof(UInt32), typeof(UInt32));
|
||||
AddEntry(typeof(char), typeof(Int64), typeof(Int64));
|
||||
AddEntry(typeof(char), typeof(UInt64), typeof(UInt64));
|
||||
AddEntry(typeof(char), typeof(float), typeof(float));
|
||||
AddEntry(typeof(char), typeof(double), typeof(double));
|
||||
}
|
||||
|
||||
private ImplicitConverter()
|
||||
{
|
||||
}
|
||||
|
||||
private static void FillIdentities(Type[] typeList, Type[,] table)
|
||||
{
|
||||
for (int i = 0; i <= typeList.Length - 1; i++)
|
||||
{
|
||||
Type t = typeList[i];
|
||||
table[i, i] = t;
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddEntry(Type t1, Type t2, Type result)
|
||||
{
|
||||
int index1 = GetTypeIndex(t1);
|
||||
int index2 = GetTypeIndex(t2);
|
||||
OurBinaryResultTable[index1, index2] = result;
|
||||
OurBinaryResultTable[index2, index1] = result;
|
||||
}
|
||||
|
||||
private static int GetTypeIndex(Type t)
|
||||
{
|
||||
return System.Array.IndexOf(OurBinaryTypes, t);
|
||||
}
|
||||
|
||||
public static bool EmitImplicitConvert(Type sourceType, Type destType, FleeILGenerator ilg)
|
||||
{
|
||||
if (object.ReferenceEquals(sourceType, destType))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (EmitOverloadedImplicitConvert(sourceType, destType, ilg) == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (ImplicitConvertToReferenceType(sourceType, destType, ilg) == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ImplicitConvertToValueType(sourceType, destType, ilg);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool EmitOverloadedImplicitConvert(Type sourceType, Type destType, FleeILGenerator ilg)
|
||||
{
|
||||
// Look for an implicit operator on the destination type
|
||||
MethodInfo mi = Utility.GetSimpleOverloadedOperator("Implicit", sourceType, destType);
|
||||
|
||||
if (mi == null)
|
||||
{
|
||||
// No match
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((ilg != null))
|
||||
{
|
||||
ilg.Emit(OpCodes.Call, mi);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool ImplicitConvertToReferenceType(Type sourceType, Type destType, FleeILGenerator ilg)
|
||||
{
|
||||
if (destType.IsValueType == true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (object.ReferenceEquals(sourceType, typeof(Null)))
|
||||
{
|
||||
// Null is always convertible to a reference type
|
||||
return true;
|
||||
}
|
||||
|
||||
if (destType.IsAssignableFrom(sourceType) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sourceType.IsValueType == true)
|
||||
{
|
||||
if ((ilg != null))
|
||||
{
|
||||
ilg.Emit(OpCodes.Box, sourceType);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool ImplicitConvertToValueType(Type sourceType, Type destType, FleeILGenerator ilg)
|
||||
{
|
||||
// We only handle value types
|
||||
if (sourceType.IsValueType == false & destType.IsValueType == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// No implicit conversion to enum. Have to do this check here since calling GetTypeCode on an enum will return the typecode
|
||||
// of the underlying type which screws us up.
|
||||
if (sourceType.IsEnum == true | destType.IsEnum == true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return EmitImplicitNumericConvert(sourceType, destType, ilg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///Emit an implicit conversion (if the ilg is not null) and returns a value that determines whether the implicit conversion
|
||||
/// succeeded
|
||||
/// </summary>
|
||||
/// <param name="sourceType"></param>
|
||||
/// <param name="destType"></param>
|
||||
/// <param name="ilg"></param>
|
||||
/// <returns></returns>
|
||||
public static bool EmitImplicitNumericConvert(Type sourceType, Type destType, FleeILGenerator ilg)
|
||||
{
|
||||
TypeCode sourceTypeCode = Type.GetTypeCode(sourceType);
|
||||
TypeCode destTypeCode = Type.GetTypeCode(destType);
|
||||
|
||||
switch (destTypeCode)
|
||||
{
|
||||
case TypeCode.Int16:
|
||||
return ImplicitConvertToInt16(sourceTypeCode, ilg);
|
||||
case TypeCode.UInt16:
|
||||
return ImplicitConvertToUInt16(sourceTypeCode, ilg);
|
||||
case TypeCode.Int32:
|
||||
return ImplicitConvertToInt32(sourceTypeCode, ilg);
|
||||
case TypeCode.UInt32:
|
||||
return ImplicitConvertToUInt32(sourceTypeCode, ilg);
|
||||
case TypeCode.Double:
|
||||
return ImplicitConvertToDouble(sourceTypeCode, ilg);
|
||||
case TypeCode.Single:
|
||||
return ImplicitConvertToSingle(sourceTypeCode, ilg);
|
||||
case TypeCode.Int64:
|
||||
return ImplicitConvertToInt64(sourceTypeCode, ilg);
|
||||
case TypeCode.UInt64:
|
||||
return ImplicitConvertToUInt64(sourceTypeCode, ilg);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static bool ImplicitConvertToInt16(TypeCode sourceTypeCode, FleeILGenerator ilg)
|
||||
{
|
||||
switch (sourceTypeCode)
|
||||
{
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ImplicitConvertToUInt16(TypeCode sourceTypeCode, FleeILGenerator ilg)
|
||||
{
|
||||
switch (sourceTypeCode)
|
||||
{
|
||||
case TypeCode.Char:
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.UInt16:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ImplicitConvertToInt32(TypeCode sourceTypeCode, FleeILGenerator ilg)
|
||||
{
|
||||
switch (sourceTypeCode)
|
||||
{
|
||||
case TypeCode.Char:
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.Int32:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ImplicitConvertToUInt32(TypeCode sourceTypeCode, FleeILGenerator ilg)
|
||||
{
|
||||
switch (sourceTypeCode)
|
||||
{
|
||||
case TypeCode.Char:
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.UInt32:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ImplicitConvertToDouble(TypeCode sourceTypeCode, FleeILGenerator ilg)
|
||||
{
|
||||
switch (sourceTypeCode)
|
||||
{
|
||||
case TypeCode.Char:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.Int32:
|
||||
case TypeCode.Single:
|
||||
case TypeCode.Int64:
|
||||
EmitConvert(ilg, OpCodes.Conv_R8);
|
||||
break;
|
||||
case TypeCode.UInt32:
|
||||
case TypeCode.UInt64:
|
||||
EmitConvert(ilg, OpCodes.Conv_R_Un);
|
||||
EmitConvert(ilg, OpCodes.Conv_R8);
|
||||
break;
|
||||
case TypeCode.Double:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool ImplicitConvertToSingle(TypeCode sourceTypeCode, FleeILGenerator ilg)
|
||||
{
|
||||
switch (sourceTypeCode)
|
||||
{
|
||||
case TypeCode.Char:
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.Int32:
|
||||
case TypeCode.Int64:
|
||||
EmitConvert(ilg, OpCodes.Conv_R4);
|
||||
break;
|
||||
case TypeCode.UInt32:
|
||||
case TypeCode.UInt64:
|
||||
EmitConvert(ilg, OpCodes.Conv_R_Un);
|
||||
EmitConvert(ilg, OpCodes.Conv_R4);
|
||||
break;
|
||||
case TypeCode.Single:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool ImplicitConvertToInt64(TypeCode sourceTypeCode, FleeILGenerator ilg)
|
||||
{
|
||||
switch (sourceTypeCode)
|
||||
{
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.Int32:
|
||||
EmitConvert(ilg, OpCodes.Conv_I8);
|
||||
break;
|
||||
case TypeCode.Char:
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.UInt32:
|
||||
EmitConvert(ilg, OpCodes.Conv_U8);
|
||||
break;
|
||||
case TypeCode.Int64:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool ImplicitConvertToUInt64(TypeCode sourceTypeCode, FleeILGenerator ilg)
|
||||
{
|
||||
switch (sourceTypeCode)
|
||||
{
|
||||
case TypeCode.Char:
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.UInt32:
|
||||
EmitConvert(ilg, OpCodes.Conv_U8);
|
||||
break;
|
||||
case TypeCode.UInt64:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void EmitConvert(FleeILGenerator ilg, OpCode convertOpcode)
|
||||
{
|
||||
if ((ilg != null))
|
||||
{
|
||||
ilg.Emit(convertOpcode);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the result type for a binary operation
|
||||
/// </summary>
|
||||
/// <param name="t1"></param>
|
||||
/// <param name="t2"></param>
|
||||
/// <returns></returns>
|
||||
public static Type GetBinaryResultType(Type t1, Type t2)
|
||||
{
|
||||
int index1 = GetTypeIndex(t1);
|
||||
int index2 = GetTypeIndex(t2);
|
||||
|
||||
if (index1 == -1 | index2 == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return OurBinaryResultTable[index1, index2];
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetImplicitConvertScore(Type sourceType, Type destType)
|
||||
{
|
||||
if (object.ReferenceEquals(sourceType, destType))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (object.ReferenceEquals(sourceType, typeof(Null)))
|
||||
{
|
||||
return GetInverseDistanceToObject(destType);
|
||||
}
|
||||
|
||||
if (Utility.GetSimpleOverloadedOperator("Implicit", sourceType, destType) != null)
|
||||
{
|
||||
// Implicit operator conversion, score it at 1 so it's just above the minimum
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (sourceType.IsValueType == true)
|
||||
{
|
||||
if (destType.IsValueType == true)
|
||||
{
|
||||
// Value type -> value type
|
||||
int sourceScore = GetValueTypeImplicitConvertScore(sourceType);
|
||||
int destScore = GetValueTypeImplicitConvertScore(destType);
|
||||
|
||||
return destScore - sourceScore;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Value type -> reference type
|
||||
return GetReferenceTypeImplicitConvertScore(sourceType, destType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (destType.IsValueType == true)
|
||||
{
|
||||
// Reference type -> value type
|
||||
// Reference types can never be implicitly converted to value types
|
||||
Debug.Fail("No implicit conversion from reference type to value type");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reference type -> reference type
|
||||
return GetReferenceTypeImplicitConvertScore(sourceType, destType);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int GetValueTypeImplicitConvertScore(Type t)
|
||||
{
|
||||
TypeCode tc = Type.GetTypeCode(t);
|
||||
|
||||
switch (tc)
|
||||
{
|
||||
case TypeCode.Byte:
|
||||
return 1;
|
||||
case TypeCode.SByte:
|
||||
return 2;
|
||||
case TypeCode.Char:
|
||||
return 3;
|
||||
case TypeCode.Int16:
|
||||
return 4;
|
||||
case TypeCode.UInt16:
|
||||
return 5;
|
||||
case TypeCode.Int32:
|
||||
return 6;
|
||||
case TypeCode.UInt32:
|
||||
return 7;
|
||||
case TypeCode.Int64:
|
||||
return 8;
|
||||
case TypeCode.UInt64:
|
||||
return 9;
|
||||
case TypeCode.Single:
|
||||
return 10;
|
||||
case TypeCode.Double:
|
||||
return 11;
|
||||
case TypeCode.Decimal:
|
||||
return 11;
|
||||
case TypeCode.Boolean:
|
||||
return 12;
|
||||
case TypeCode.DateTime:
|
||||
return 13;
|
||||
default:
|
||||
Debug.Assert(false, "unknown value type");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetReferenceTypeImplicitConvertScore(Type sourceType, Type destType)
|
||||
{
|
||||
if (destType.IsInterface == true)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetInheritanceDistance(sourceType, destType);
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetInheritanceDistance(Type sourceType, Type destType)
|
||||
{
|
||||
int count = 0;
|
||||
Type current = sourceType;
|
||||
|
||||
while ((!object.ReferenceEquals(current, destType)))
|
||||
{
|
||||
count += 1;
|
||||
current = current.BaseType;
|
||||
}
|
||||
|
||||
return count * 1000;
|
||||
}
|
||||
|
||||
private static int GetInverseDistanceToObject(Type t)
|
||||
{
|
||||
int score = 1000;
|
||||
Type current = t.BaseType;
|
||||
|
||||
while ((current != null))
|
||||
{
|
||||
score -= 100;
|
||||
current = current.BaseType;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
}
|
||||
}
|
562
InternalTypes/Miscellaneous.cs
Normal file
562
InternalTypes/Miscellaneous.cs
Normal file
@@ -0,0 +1,562 @@
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.PublicTypes;
|
||||
|
||||
namespace Flee.InternalTypes
|
||||
{
|
||||
internal enum BinaryArithmeticOperation
|
||||
{
|
||||
Add,
|
||||
Subtract,
|
||||
Multiply,
|
||||
Divide,
|
||||
Mod,
|
||||
Power
|
||||
}
|
||||
|
||||
internal enum LogicalCompareOperation
|
||||
{
|
||||
LessThan,
|
||||
GreaterThan,
|
||||
Equal,
|
||||
NotEqual,
|
||||
LessThanOrEqual,
|
||||
GreaterThanOrEqual
|
||||
}
|
||||
|
||||
internal enum AndOrOperation
|
||||
{
|
||||
And,
|
||||
Or
|
||||
}
|
||||
|
||||
internal enum ShiftOperation
|
||||
{
|
||||
LeftShift,
|
||||
RightShift
|
||||
}
|
||||
|
||||
internal delegate T ExpressionEvaluator<T>(object owner, ExpressionContext context, VariableCollection variables);
|
||||
|
||||
internal abstract class CustomBinder : Binder
|
||||
{
|
||||
|
||||
public override System.Reflection.FieldInfo BindToField(System.Reflection.BindingFlags bindingAttr, System.Reflection.FieldInfo[] match, object value, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public System.Reflection.MethodBase BindToMethod(System.Reflection.BindingFlags bindingAttr, System.Reflection.MethodBase[] match, ref object[] args, System.Reflection.ParameterModifier[] modifiers, System.Globalization.CultureInfo culture, string[] names, ref object state)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public override object ChangeType(object value, System.Type type, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public override void ReorderArgumentArray(ref object[] args, object state)
|
||||
{
|
||||
}
|
||||
|
||||
public override System.Reflection.PropertyInfo SelectProperty(System.Reflection.BindingFlags bindingAttr, System.Reflection.PropertyInfo[] match, System.Type returnType, System.Type[] indexes, System.Reflection.ParameterModifier[] modifiers)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ExplicitOperatorMethodBinder : CustomBinder
|
||||
{
|
||||
private readonly Type _myReturnType;
|
||||
private readonly Type _myArgType;
|
||||
private CustomBinder _customBinderImplementation;
|
||||
|
||||
public ExplicitOperatorMethodBinder(Type returnType, Type argType)
|
||||
{
|
||||
_myReturnType = returnType;
|
||||
_myArgType = argType;
|
||||
}
|
||||
|
||||
public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers,
|
||||
CultureInfo culture, string[] names, out object state)
|
||||
{
|
||||
return _customBinderImplementation.BindToMethod(bindingAttr, match, ref args, modifiers, culture, names, out state);
|
||||
}
|
||||
|
||||
public override System.Reflection.MethodBase SelectMethod(System.Reflection.BindingFlags bindingAttr, System.Reflection.MethodBase[] match, System.Type[] types, System.Reflection.ParameterModifier[] modifiers)
|
||||
{
|
||||
foreach (MethodInfo mi in match)
|
||||
{
|
||||
ParameterInfo[] parameters = mi.GetParameters();
|
||||
ParameterInfo firstParameter = parameters[0];
|
||||
if (object.ReferenceEquals(firstParameter.ParameterType, _myArgType) & object.ReferenceEquals(mi.ReturnType, _myReturnType))
|
||||
{
|
||||
return mi;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal class BinaryOperatorBinder : CustomBinder
|
||||
{
|
||||
|
||||
private readonly Type _myLeftType;
|
||||
private readonly Type _myRightType;
|
||||
private CustomBinder _customBinderImplementation;
|
||||
|
||||
public BinaryOperatorBinder(Type leftType, Type rightType)
|
||||
{
|
||||
_myLeftType = leftType;
|
||||
_myRightType = rightType;
|
||||
}
|
||||
|
||||
public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers,
|
||||
CultureInfo culture, string[] names, out object state)
|
||||
{
|
||||
return _customBinderImplementation.BindToMethod(bindingAttr, match, ref args, modifiers, culture, names, out state);
|
||||
}
|
||||
|
||||
public override System.Reflection.MethodBase SelectMethod(System.Reflection.BindingFlags bindingAttr, System.Reflection.MethodBase[] match, System.Type[] types, System.Reflection.ParameterModifier[] modifiers)
|
||||
{
|
||||
foreach (MethodInfo mi in match)
|
||||
{
|
||||
ParameterInfo[] parameters = mi.GetParameters();
|
||||
bool leftValid = ImplicitConverter.EmitImplicitConvert(_myLeftType, parameters[0].ParameterType, null);
|
||||
bool rightValid = ImplicitConverter.EmitImplicitConvert(_myRightType, parameters[1].ParameterType, null);
|
||||
|
||||
if (leftValid == true & rightValid == true)
|
||||
{
|
||||
return mi;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal class Null
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
internal class DefaultExpressionOwner
|
||||
{
|
||||
|
||||
|
||||
private static readonly DefaultExpressionOwner OurInstance = new DefaultExpressionOwner();
|
||||
|
||||
private DefaultExpressionOwner()
|
||||
{
|
||||
}
|
||||
|
||||
public static object Instance => OurInstance;
|
||||
}
|
||||
|
||||
[Obsolete("Helper class to resolve overloads")]
|
||||
internal class CustomMethodInfo : IComparable<CustomMethodInfo>, IEquatable<CustomMethodInfo>
|
||||
{
|
||||
/// <summary>
|
||||
/// Method we are wrapping
|
||||
/// </summary>
|
||||
private readonly MethodInfo _myTarget;
|
||||
/// <summary>
|
||||
/// The rating of how close the method matches the given arguments (0 is best)
|
||||
/// </summary>
|
||||
private float _myScore;
|
||||
public bool IsParamArray;
|
||||
public Type[] MyFixedArgTypes;
|
||||
public Type[] MyParamArrayArgTypes;
|
||||
public bool IsExtensionMethod;
|
||||
public Type ParamArrayElementType;
|
||||
public CustomMethodInfo(MethodInfo target)
|
||||
{
|
||||
_myTarget = target;
|
||||
}
|
||||
|
||||
public void ComputeScore(Type[] argTypes)
|
||||
{
|
||||
ParameterInfo[] @params = _myTarget.GetParameters();
|
||||
|
||||
if (@params.Length == 0)
|
||||
{
|
||||
_myScore = 0.0F;
|
||||
}
|
||||
else if (@params.Length == 1 && argTypes.Length == 0)//extension method without parameter support -> prefer members
|
||||
{
|
||||
_myScore = 0.1F;
|
||||
}
|
||||
else if (IsParamArray == true)
|
||||
{
|
||||
_myScore = this.ComputeScoreForParamArray(@params, argTypes);
|
||||
}
|
||||
else if (IsExtensionMethod == true)
|
||||
{
|
||||
_myScore = this.ComputeScoreExtensionMethodInternal(@params, argTypes);
|
||||
}
|
||||
else
|
||||
{
|
||||
_myScore = this.ComputeScoreInternal(@params, argTypes);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute a score showing how close our method matches the given argument types (for extension methods)
|
||||
/// </summary>
|
||||
/// <param name="parameters"></param>
|
||||
/// <param name="argTypes"></param>
|
||||
/// <returns></returns>
|
||||
private float ComputeScoreExtensionMethodInternal(ParameterInfo[] parameters, Type[] argTypes)
|
||||
{
|
||||
Debug.Assert(parameters.Length == argTypes.Length + 1);
|
||||
int sum = 0;
|
||||
|
||||
for (int i = 0; i <= argTypes.Length - 1; i++)
|
||||
{
|
||||
sum += ImplicitConverter.GetImplicitConvertScore(argTypes[i], parameters[i + 1].ParameterType);
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute a score showing how close our method matches the given argument types
|
||||
/// </summary>
|
||||
/// <param name="parameters"></param>
|
||||
/// <param name="argTypes"></param>
|
||||
/// <returns></returns>
|
||||
private float ComputeScoreInternal(ParameterInfo[] parameters, Type[] argTypes)
|
||||
{
|
||||
// Our score is the average of the scores of each parameter. The lower the score, the better the match.
|
||||
int sum = ComputeSum(parameters, argTypes);
|
||||
|
||||
return (float)sum / (float)argTypes.Length;
|
||||
}
|
||||
|
||||
private static int ComputeSum(ParameterInfo[] parameters, Type[] argTypes)
|
||||
{
|
||||
Debug.Assert(parameters.Length == argTypes.Length);
|
||||
int sum = 0;
|
||||
|
||||
for (int i = 0; i <= parameters.Length - 1; i++)
|
||||
{
|
||||
sum += ImplicitConverter.GetImplicitConvertScore(argTypes[i], parameters[i].ParameterType);
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
private float ComputeScoreForParamArray(ParameterInfo[] parameters, Type[] argTypes)
|
||||
{
|
||||
ParameterInfo paramArrayParameter = parameters[parameters.Length - 1];
|
||||
int fixedParameterCount = paramArrayParameter.Position;
|
||||
|
||||
ParameterInfo[] fixedParameters = new ParameterInfo[fixedParameterCount];
|
||||
|
||||
System.Array.Copy(parameters, fixedParameters, fixedParameterCount);
|
||||
|
||||
int fixedSum = ComputeSum(fixedParameters, MyFixedArgTypes);
|
||||
|
||||
Type paramArrayElementType = paramArrayParameter.ParameterType.GetElementType();
|
||||
|
||||
int paramArraySum = 0;
|
||||
|
||||
foreach (Type argType in MyParamArrayArgTypes)
|
||||
{
|
||||
paramArraySum += ImplicitConverter.GetImplicitConvertScore(argType, paramArrayElementType);
|
||||
}
|
||||
|
||||
float score = 0;
|
||||
|
||||
if (argTypes.Length > 0)
|
||||
{
|
||||
score = (fixedSum + paramArraySum) / argTypes.Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
score = 0;
|
||||
}
|
||||
|
||||
// The param array score gets a slight penalty so that it scores worse than direct matches
|
||||
return score + 1;
|
||||
}
|
||||
|
||||
public bool IsAccessible(MemberElement owner)
|
||||
{
|
||||
return owner.IsMemberAccessible(_myTarget);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the given MethodInfo usable as an overload?
|
||||
/// </summary>
|
||||
/// <param name="argTypes"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsMatch(Type[] argTypes, MemberElement previous, ExpressionContext context)
|
||||
{
|
||||
ParameterInfo[] parameters = _myTarget.GetParameters();
|
||||
|
||||
// If there are no parameters and no arguments were passed, then we are a match.
|
||||
if (parameters.Length == 0 & argTypes.Length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If there are no parameters but there are arguments, we cannot be a match
|
||||
if (parameters.Length == 0 & argTypes.Length > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is the last parameter a paramArray?
|
||||
ParameterInfo lastParam = parameters[parameters.Length - 1];
|
||||
|
||||
if (lastParam.IsDefined(typeof(ParamArrayAttribute), false) == false)
|
||||
{
|
||||
//Extension method support
|
||||
if (parameters.Length == argTypes.Length + 1)
|
||||
{
|
||||
IsExtensionMethod = true;
|
||||
return AreValidExtensionMethodArgumentsForParameters(argTypes, parameters, previous, context);
|
||||
}
|
||||
if ((parameters.Length != argTypes.Length))
|
||||
{
|
||||
// Not a paramArray and parameter and argument counts don't match
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Regular method call, do the test
|
||||
return AreValidArgumentsForParameters(argTypes, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, we are dealing with a paramArray call
|
||||
|
||||
// If the parameter and argument counts are equal and there is an implicit conversion from one to the other, we are a match.
|
||||
if (parameters.Length == argTypes.Length && AreValidArgumentsForParameters(argTypes, parameters) == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (this.IsParamArrayMatch(argTypes, parameters, lastParam) == true)
|
||||
{
|
||||
IsParamArray = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsParamArrayMatch(Type[] argTypes, ParameterInfo[] parameters, ParameterInfo paramArrayParameter)
|
||||
{
|
||||
// Get the count of arguments before the paramArray parameter
|
||||
int fixedParameterCount = paramArrayParameter.Position;
|
||||
Type[] fixedArgTypes = new Type[fixedParameterCount];
|
||||
ParameterInfo[] fixedParameters = new ParameterInfo[fixedParameterCount];
|
||||
|
||||
// Get the argument types and parameters before the paramArray
|
||||
System.Array.Copy(argTypes, fixedArgTypes, fixedParameterCount);
|
||||
System.Array.Copy(parameters, fixedParameters, fixedParameterCount);
|
||||
|
||||
// If the fixed arguments don't match, we are not a match
|
||||
if (AreValidArgumentsForParameters(fixedArgTypes, fixedParameters) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the type of the paramArray
|
||||
ParamArrayElementType = paramArrayParameter.ParameterType.GetElementType();
|
||||
|
||||
// Get the types of the arguments passed to the paramArray
|
||||
Type[] paramArrayArgTypes = new Type[argTypes.Length - fixedParameterCount];
|
||||
System.Array.Copy(argTypes, fixedParameterCount, paramArrayArgTypes, 0, paramArrayArgTypes.Length);
|
||||
|
||||
// Check each argument
|
||||
foreach (Type argType in paramArrayArgTypes)
|
||||
{
|
||||
if (ImplicitConverter.EmitImplicitConvert(argType, ParamArrayElementType, null) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MyFixedArgTypes = fixedArgTypes;
|
||||
MyParamArrayArgTypes = paramArrayArgTypes;
|
||||
|
||||
// They all match, so we are a match
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool AreValidExtensionMethodArgumentsForParameters(Type[] argTypes, ParameterInfo[] parameters, MemberElement previous, ExpressionContext context)
|
||||
{
|
||||
Debug.Assert(argTypes.Length + 1 == parameters.Length);
|
||||
|
||||
if (previous != null)
|
||||
{
|
||||
if (ImplicitConverter.EmitImplicitConvert(previous.ResultType, parameters[0].ParameterType, null) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (context.ExpressionOwner != null)
|
||||
{
|
||||
if (ImplicitConverter.EmitImplicitConvert(context.ExpressionOwner.GetType(), parameters[0].ParameterType, null) == false)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
//Match if every given argument is implicitly convertible to the method's corresponding parameter
|
||||
for (int i = 0; i <= argTypes.Length - 1; i++)
|
||||
{
|
||||
if (ImplicitConverter.EmitImplicitConvert(argTypes[i], parameters[i + 1].ParameterType, null) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool AreValidArgumentsForParameters(Type[] argTypes, ParameterInfo[] parameters)
|
||||
{
|
||||
Debug.Assert(argTypes.Length == parameters.Length);
|
||||
// Match if every given argument is implicitly convertible to the method's corresponding parameter
|
||||
for (int i = 0; i <= argTypes.Length - 1; i++)
|
||||
{
|
||||
if (ImplicitConverter.EmitImplicitConvert(argTypes[i], parameters[i].ParameterType, null) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int CompareTo(CustomMethodInfo other)
|
||||
{
|
||||
return _myScore.CompareTo(other._myScore);
|
||||
}
|
||||
|
||||
private bool Equals1(CustomMethodInfo other)
|
||||
{
|
||||
return _myScore == other._myScore;
|
||||
}
|
||||
bool System.IEquatable<CustomMethodInfo>.Equals(CustomMethodInfo other)
|
||||
{
|
||||
return Equals1(other);
|
||||
}
|
||||
|
||||
public MethodInfo Target => _myTarget;
|
||||
}
|
||||
|
||||
internal class ShortCircuitInfo
|
||||
{
|
||||
|
||||
public Stack Operands;
|
||||
public Stack Operators;
|
||||
private Dictionary<object, Label> Labels;
|
||||
|
||||
public ShortCircuitInfo()
|
||||
{
|
||||
this.Operands = new Stack();
|
||||
this.Operators = new Stack();
|
||||
this.Labels = new Dictionary<object, Label>();
|
||||
}
|
||||
|
||||
public void ClearTempState()
|
||||
{
|
||||
this.Operands.Clear();
|
||||
this.Operators.Clear();
|
||||
}
|
||||
|
||||
public Label AddLabel(object key, Label lbl)
|
||||
{
|
||||
Labels.Add(key, lbl);
|
||||
return lbl;
|
||||
}
|
||||
|
||||
public bool HasLabel(object key)
|
||||
{
|
||||
return Labels.ContainsKey(key);
|
||||
}
|
||||
|
||||
public Label FindLabel(object key)
|
||||
{
|
||||
return Labels[key];
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Wraps an expression element so that it is loaded from a local slot")]
|
||||
internal class LocalBasedElement : ExpressionElement
|
||||
{
|
||||
private readonly int _myIndex;
|
||||
|
||||
private readonly ExpressionElement _myTarget;
|
||||
public LocalBasedElement(ExpressionElement target, int index)
|
||||
{
|
||||
_myTarget = target;
|
||||
_myIndex = index;
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
Utility.EmitLoadLocal(ilg, _myIndex);
|
||||
}
|
||||
|
||||
public override System.Type ResultType => _myTarget.ResultType;
|
||||
}
|
||||
|
||||
[Obsolete("Helper class for storing strongly-typed properties")]
|
||||
internal class PropertyDictionary
|
||||
{
|
||||
private readonly Dictionary<string, object> _myProperties;
|
||||
public PropertyDictionary()
|
||||
{
|
||||
_myProperties = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public PropertyDictionary Clone()
|
||||
{
|
||||
PropertyDictionary copy = new PropertyDictionary();
|
||||
|
||||
foreach (KeyValuePair<string, object> pair in _myProperties)
|
||||
{
|
||||
copy.SetValue(pair.Key, pair.Value);
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
public T GetValue<T>(string name)
|
||||
{
|
||||
object value = default(T);
|
||||
if (_myProperties.TryGetValue(name, out value) == false)
|
||||
{
|
||||
Debug.Fail($"Unknown property '{name}'");
|
||||
}
|
||||
return (T)value;
|
||||
}
|
||||
|
||||
public void SetToDefault<T>(string name)
|
||||
{
|
||||
T value = default(T);
|
||||
this.SetValue(name, value);
|
||||
}
|
||||
|
||||
public void SetValue(string name, object value)
|
||||
{
|
||||
_myProperties[name] = value;
|
||||
}
|
||||
|
||||
public bool Contains(string name)
|
||||
{
|
||||
return _myProperties.ContainsKey(name);
|
||||
}
|
||||
}
|
||||
}
|
350
InternalTypes/Utility.cs
Normal file
350
InternalTypes/Utility.cs
Normal file
@@ -0,0 +1,350 @@
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using Flee.Resources;
|
||||
|
||||
namespace Flee.InternalTypes
|
||||
{
|
||||
[Obsolete("Holds various shared utility methods")]
|
||||
internal class Utility
|
||||
{
|
||||
private Utility()
|
||||
{
|
||||
}
|
||||
|
||||
public static void AssertNotNull(object o, string paramName)
|
||||
{
|
||||
if (o == null)
|
||||
{
|
||||
throw new ArgumentNullException(paramName);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitStoreLocal(FleeILGenerator ilg, int index)
|
||||
{
|
||||
if (index >= 0 & index <= 3)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
ilg.Emit(OpCodes.Stloc_0);
|
||||
break;
|
||||
case 1:
|
||||
ilg.Emit(OpCodes.Stloc_1);
|
||||
break;
|
||||
case 2:
|
||||
ilg.Emit(OpCodes.Stloc_2);
|
||||
break;
|
||||
case 3:
|
||||
ilg.Emit(OpCodes.Stloc_3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (index < 256)
|
||||
{
|
||||
ilg.Emit(OpCodes.Stloc_S, Convert.ToByte(index));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(index < 65535, "local index too large");
|
||||
ilg.Emit(OpCodes.Stloc, unchecked((short)Convert.ToUInt16(index)));
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitLoadLocal(FleeILGenerator ilg, int index)
|
||||
{
|
||||
Debug.Assert(index >= 0, "Invalid index");
|
||||
|
||||
if (index >= 0 & index <= 3)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
ilg.Emit(OpCodes.Ldloc_0);
|
||||
break;
|
||||
case 1:
|
||||
ilg.Emit(OpCodes.Ldloc_1);
|
||||
break;
|
||||
case 2:
|
||||
ilg.Emit(OpCodes.Ldloc_2);
|
||||
break;
|
||||
case 3:
|
||||
ilg.Emit(OpCodes.Ldloc_3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (index < 256)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldloc_S, Convert.ToByte(index));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(index < 65535, "local index too large");
|
||||
ilg.Emit(OpCodes.Ldloc, unchecked((short)Convert.ToUInt16(index)));
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitLoadLocalAddress(FleeILGenerator ilg, int index)
|
||||
{
|
||||
Debug.Assert(index >= 0, "Invalid index");
|
||||
|
||||
if (index <= byte.MaxValue)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldloca_S, Convert.ToByte(index));
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldloca, index);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitArrayLoad(FleeILGenerator ilg, Type elementType)
|
||||
{
|
||||
TypeCode tc = Type.GetTypeCode(elementType);
|
||||
|
||||
switch (tc)
|
||||
{
|
||||
case TypeCode.Byte:
|
||||
ilg.Emit(OpCodes.Ldelem_U1);
|
||||
break;
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Boolean:
|
||||
ilg.Emit(OpCodes.Ldelem_I1);
|
||||
break;
|
||||
case TypeCode.Int16:
|
||||
ilg.Emit(OpCodes.Ldelem_I2);
|
||||
break;
|
||||
case TypeCode.UInt16:
|
||||
ilg.Emit(OpCodes.Ldelem_U2);
|
||||
break;
|
||||
case TypeCode.Int32:
|
||||
ilg.Emit(OpCodes.Ldelem_I4);
|
||||
break;
|
||||
case TypeCode.UInt32:
|
||||
ilg.Emit(OpCodes.Ldelem_U4);
|
||||
break;
|
||||
case TypeCode.Int64:
|
||||
case TypeCode.UInt64:
|
||||
ilg.Emit(OpCodes.Ldelem_I8);
|
||||
break;
|
||||
case TypeCode.Single:
|
||||
ilg.Emit(OpCodes.Ldelem_R4);
|
||||
break;
|
||||
case TypeCode.Double:
|
||||
ilg.Emit(OpCodes.Ldelem_R8);
|
||||
break;
|
||||
case TypeCode.Object:
|
||||
case TypeCode.String:
|
||||
ilg.Emit(OpCodes.Ldelem_Ref);
|
||||
break;
|
||||
default:
|
||||
// Must be a non-primitive value type
|
||||
ilg.Emit(OpCodes.Ldelema, elementType);
|
||||
ilg.Emit(OpCodes.Ldobj, elementType);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitArrayStore(FleeILGenerator ilg, Type elementType)
|
||||
{
|
||||
TypeCode tc = Type.GetTypeCode(elementType);
|
||||
|
||||
switch (tc)
|
||||
{
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Boolean:
|
||||
ilg.Emit(OpCodes.Stelem_I1);
|
||||
break;
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.UInt16:
|
||||
ilg.Emit(OpCodes.Stelem_I2);
|
||||
break;
|
||||
case TypeCode.Int32:
|
||||
case TypeCode.UInt32:
|
||||
ilg.Emit(OpCodes.Stelem_I4);
|
||||
break;
|
||||
case TypeCode.Int64:
|
||||
case TypeCode.UInt64:
|
||||
ilg.Emit(OpCodes.Stelem_I8);
|
||||
break;
|
||||
case TypeCode.Single:
|
||||
ilg.Emit(OpCodes.Stelem_R4);
|
||||
break;
|
||||
case TypeCode.Double:
|
||||
ilg.Emit(OpCodes.Stelem_R8);
|
||||
break;
|
||||
case TypeCode.Object:
|
||||
case TypeCode.String:
|
||||
ilg.Emit(OpCodes.Stelem_Ref);
|
||||
break;
|
||||
default:
|
||||
// Must be a non-primitive value type
|
||||
ilg.Emit(OpCodes.Stelem, elementType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static bool IsIntegralType(Type t)
|
||||
{
|
||||
TypeCode tc = Type.GetTypeCode(t);
|
||||
switch (tc)
|
||||
{
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.Int32:
|
||||
case TypeCode.UInt32:
|
||||
case TypeCode.Int64:
|
||||
case TypeCode.UInt64:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static Type GetBitwiseOpType(Type leftType, Type rightType)
|
||||
{
|
||||
if (IsIntegralType(leftType) == false || IsIntegralType(rightType) == false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ImplicitConverter.GetBinaryResultType(leftType, rightType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find a simple (unary) overloaded operator
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the operator</param>
|
||||
/// <param name="sourceType">The type to convert from</param>
|
||||
/// <param name="destType">The type to convert to (can be null if it's not known beforehand)</param>
|
||||
/// <returns>The operator's method or null of no match is found</returns>
|
||||
public static MethodInfo GetSimpleOverloadedOperator(string name, Type sourceType, Type destType)
|
||||
{
|
||||
Hashtable data = new Hashtable();
|
||||
data.Add("Name", string.Concat("op_", name));
|
||||
data.Add("sourceType", sourceType);
|
||||
data.Add("destType", destType);
|
||||
|
||||
const BindingFlags flags = BindingFlags.Public | BindingFlags.Static;
|
||||
|
||||
// Look on the source type and its ancestors
|
||||
MemberInfo[] members = new MemberInfo[0];
|
||||
do
|
||||
{
|
||||
members = sourceType.FindMembers(MemberTypes.Method, flags, SimpleOverloadedOperatorFilter, data);
|
||||
} while (members.Length == 0 && (sourceType = sourceType.BaseType) != null);
|
||||
|
||||
if (members.Length == 0 && destType != null)
|
||||
{
|
||||
// Look on the dest type and its ancestors
|
||||
do
|
||||
{
|
||||
members = destType.FindMembers(MemberTypes.Method, flags, SimpleOverloadedOperatorFilter, data);
|
||||
} while (members.Length == 0 && (destType = destType.BaseType) != null);
|
||||
}
|
||||
|
||||
Debug.Assert(members.Length < 2, "Multiple overloaded operators found");
|
||||
|
||||
if (members.Length == 0)
|
||||
{
|
||||
// No match
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (MethodInfo)members[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Matches simple overloaded operators
|
||||
/// </summary>
|
||||
/// <param name="member"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks></remarks>
|
||||
private static bool SimpleOverloadedOperatorFilter(MemberInfo member, object value)
|
||||
{
|
||||
IDictionary data = (IDictionary)value;
|
||||
MethodInfo method = (MethodInfo)member;
|
||||
|
||||
bool nameMatch = method.IsSpecialName == true && method.Name.Equals((string)data["Name"], StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (nameMatch == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// destination type might not be known
|
||||
Type destType = (Type)data["destType"];
|
||||
|
||||
if (destType != null)
|
||||
{
|
||||
bool returnTypeMatch = object.ReferenceEquals(destType, method.ReturnType);
|
||||
|
||||
if (returnTypeMatch == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ParameterInfo[] parameters = method.GetParameters();
|
||||
bool argumentMatch = parameters.Length > 0 && parameters[0].ParameterType.IsAssignableFrom((Type)data["sourceType"]);
|
||||
|
||||
return argumentMatch;
|
||||
}
|
||||
|
||||
public static MethodInfo GetOverloadedOperator(string name, Type sourceType, Binder binder, params Type[] argumentTypes)
|
||||
{
|
||||
name = string.Concat("op_", name);
|
||||
MethodInfo mi = null;
|
||||
do
|
||||
{
|
||||
mi = sourceType.GetMethod(name, BindingFlags.Public | BindingFlags.Static, binder, CallingConventions.Any, argumentTypes, null);
|
||||
if (mi != null && mi.IsSpecialName == true)
|
||||
{
|
||||
return mi;
|
||||
}
|
||||
} while ((sourceType = sourceType.BaseType) != null);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int GetILGeneratorLength(ILGenerator ilg)
|
||||
{
|
||||
System.Reflection.FieldInfo fi = typeof(ILGenerator).GetField("m_length", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
return (int)fi.GetValue(ilg);
|
||||
}
|
||||
|
||||
public static bool IsLongBranch(int startPosition, int endPosition)
|
||||
{
|
||||
return (endPosition - startPosition) > sbyte.MaxValue;
|
||||
}
|
||||
|
||||
public static string FormatList(string[] items)
|
||||
{
|
||||
string separator = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator + " ";
|
||||
return string.Join(separator, items);
|
||||
}
|
||||
|
||||
public static string GetGeneralErrorMessage(string key, params object[] args)
|
||||
{
|
||||
string msg = FleeResourceManager.Instance.GetGeneralErrorString(key);
|
||||
return string.Format(msg, args);
|
||||
}
|
||||
|
||||
public static string GetCompileErrorMessage(string key, params object[] args)
|
||||
{
|
||||
string msg = FleeResourceManager.Instance.GetCompileErrorString(key);
|
||||
return string.Format(msg, args);
|
||||
}
|
||||
}
|
||||
}
|
100
InternalTypes/VariableTypes.cs
Normal file
100
InternalTypes/VariableTypes.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using Flee.PublicTypes;
|
||||
|
||||
namespace Flee.InternalTypes
|
||||
{
|
||||
internal interface IVariable
|
||||
{
|
||||
IVariable Clone();
|
||||
Type VariableType { get; }
|
||||
object ValueAsObject { get; set; }
|
||||
}
|
||||
|
||||
internal interface IGenericVariable<T>
|
||||
{
|
||||
object GetValue();
|
||||
}
|
||||
|
||||
internal class DynamicExpressionVariable<T> : IVariable, IGenericVariable<T>
|
||||
{
|
||||
private IDynamicExpression _myExpression;
|
||||
public IVariable Clone()
|
||||
{
|
||||
DynamicExpressionVariable<T> copy = new DynamicExpressionVariable<T>();
|
||||
copy._myExpression = _myExpression;
|
||||
return copy;
|
||||
}
|
||||
|
||||
public object GetValue()
|
||||
{
|
||||
return (T)_myExpression.Evaluate();
|
||||
}
|
||||
|
||||
public object ValueAsObject
|
||||
{
|
||||
get { return _myExpression; }
|
||||
set { _myExpression = value as IDynamicExpression; }
|
||||
}
|
||||
|
||||
public System.Type VariableType => _myExpression.Context.Options.ResultType;
|
||||
}
|
||||
|
||||
internal class GenericExpressionVariable<T> : IVariable, IGenericVariable<T>
|
||||
{
|
||||
private IGenericExpression<T> _myExpression;
|
||||
public IVariable Clone()
|
||||
{
|
||||
GenericExpressionVariable<T> copy = new GenericExpressionVariable<T>();
|
||||
copy._myExpression = _myExpression;
|
||||
return copy;
|
||||
}
|
||||
|
||||
public object GetValue()
|
||||
{
|
||||
return _myExpression.Evaluate();
|
||||
}
|
||||
|
||||
public object ValueAsObject
|
||||
{
|
||||
get { return _myExpression; }
|
||||
set { _myExpression = (IGenericExpression<T>)value; }
|
||||
}
|
||||
|
||||
public System.Type VariableType => _myExpression.Context.Options.ResultType;
|
||||
}
|
||||
|
||||
internal class GenericVariable<T> : IVariable, IGenericVariable<T>
|
||||
{
|
||||
|
||||
|
||||
public object MyValue;
|
||||
public IVariable Clone()
|
||||
{
|
||||
GenericVariable<T> copy = new GenericVariable<T> { MyValue = MyValue };
|
||||
return copy;
|
||||
}
|
||||
|
||||
public object GetValue()
|
||||
{
|
||||
return MyValue;
|
||||
}
|
||||
|
||||
public System.Type VariableType => typeof(T);
|
||||
|
||||
public object ValueAsObject
|
||||
{
|
||||
get { return MyValue; }
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
MyValue = default(T);
|
||||
}
|
||||
else
|
||||
{
|
||||
MyValue = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user