308 lines
12 KiB
C#
308 lines
12 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using Upsilon.BaseTypes.Number;
|
|
using Upsilon.BaseTypes.ScriptTypeInterfaces;
|
|
|
|
namespace Upsilon.BaseTypes.UserData
|
|
{
|
|
public class UpsilonBinder : System.Reflection.Binder
|
|
{
|
|
public static readonly UpsilonBinder Default = new UpsilonBinder();
|
|
|
|
public override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo culture)
|
|
{
|
|
throw new System.NotImplementedException();
|
|
}
|
|
|
|
public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] matches, ref object[] args, ParameterModifier[] modifiers,
|
|
CultureInfo culture, string[] names, out object state)
|
|
{
|
|
state = null;
|
|
|
|
foreach (var match in matches)
|
|
{
|
|
if (IsValidMatch(match, args.Select(x => x.GetType()).ToArray()))
|
|
return match;
|
|
}
|
|
|
|
state = null;
|
|
return null;
|
|
}
|
|
|
|
public bool IsValidMatch(MethodBase match, System.Type[] argumentTypes)
|
|
{
|
|
var parameters = match.GetParameters();
|
|
for (var i = 0; i < parameters.Length; i++)
|
|
{
|
|
var matchParameter = parameters[i];
|
|
if (argumentTypes.Length <= i)
|
|
{
|
|
if (matchParameter.IsOptional)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
var argumentType = argumentTypes[i];
|
|
|
|
var typeCode = System.Type.GetTypeCode(matchParameter.ParameterType);
|
|
switch (typeCode)
|
|
{
|
|
case TypeCode.Boolean:
|
|
if (argumentType == typeof(ScriptBoolean))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
case TypeCode.Char:
|
|
case TypeCode.String:
|
|
if (argumentType == typeof(ScriptString))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
case TypeCode.Byte:
|
|
case TypeCode.Decimal:
|
|
case TypeCode.Double:
|
|
case TypeCode.Int16:
|
|
case TypeCode.Int32:
|
|
case TypeCode.Int64:
|
|
case TypeCode.SByte:
|
|
case TypeCode.Single:
|
|
case TypeCode.UInt16:
|
|
case TypeCode.UInt32:
|
|
case TypeCode.UInt64:
|
|
if (typeof(ScriptNumber).IsAssignableFrom(argumentType))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
case TypeCode.Object:
|
|
continue;
|
|
}
|
|
|
|
if (argumentType == typeof(ScriptNull))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!matchParameter.ParameterType.IsAssignableFrom(argumentType))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public override object ChangeType(object value, System.Type requiredType, CultureInfo culture)
|
|
{
|
|
if (requiredType.IsInstanceOfType(value))
|
|
{
|
|
return value;
|
|
}
|
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
|
if (value == null)
|
|
// ReSharper disable once HeuristicUnreachableCode
|
|
{
|
|
// ReSharper disable once HeuristicUnreachableCode
|
|
return null;
|
|
}
|
|
|
|
var typeCode = System.Type.GetTypeCode(requiredType);
|
|
switch (typeCode)
|
|
{
|
|
case TypeCode.Boolean:
|
|
if (value is ScriptBoolean b)
|
|
{
|
|
return b.Value;
|
|
}
|
|
break;
|
|
case TypeCode.Char:
|
|
case TypeCode.String:
|
|
if (value is ScriptString s)
|
|
{
|
|
return s.Value;
|
|
}
|
|
break;
|
|
case TypeCode.Single:
|
|
case TypeCode.Decimal:
|
|
case TypeCode.Double:
|
|
if (value is ScriptNumber number)
|
|
{
|
|
return Convert.ChangeType(number.ToCSharpObject(), typeCode);
|
|
}
|
|
break;
|
|
case TypeCode.Byte:
|
|
case TypeCode.Int16:
|
|
case TypeCode.Int32:
|
|
case TypeCode.Int64:
|
|
case TypeCode.SByte:
|
|
case TypeCode.UInt16:
|
|
case TypeCode.UInt32:
|
|
case TypeCode.UInt64:
|
|
if (value is ScriptNumber numeric)
|
|
{
|
|
if (numeric.IsFloat)
|
|
{
|
|
// We expect a conversion from a float to an integer to just lose its precision, however
|
|
// Convert.ChangeType will round it to the nearest integer. Therefore we floor the value
|
|
// before converting it
|
|
return Convert.ChangeType(Math.Floor((double) numeric.ToCSharpObject()), typeCode);
|
|
}
|
|
return Convert.ChangeType(numeric.ToCSharpObject(), typeCode);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (value is ScriptNull)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var valueType = value.GetType();
|
|
if (requiredType == typeof(IIterable))
|
|
{
|
|
if (typeof(IDictionary).IsAssignableFrom(valueType))
|
|
{
|
|
return new DictionaryUserData((IDictionary) value);
|
|
}
|
|
if (typeof(IList).IsAssignableFrom(valueType))
|
|
{
|
|
return new ListUserData((IList) value);
|
|
}
|
|
}
|
|
|
|
if (typeof(IDictionary).IsAssignableFrom(requiredType))
|
|
{
|
|
if (value is DictionaryUserData d)
|
|
return d.Dictionary;
|
|
if (value is ScriptTable.ScriptTable table)
|
|
{
|
|
var generics = requiredType.GetGenericArguments();
|
|
var d1 = typeof(Dictionary<,>);
|
|
var requiredDictionaryType = d1.MakeGenericType(typeof(string), generics[1]);
|
|
var dictionary = (IDictionary)Activator.CreateInstance(requiredDictionaryType);
|
|
foreach (var variable in table.EvaluationScope.Variables)
|
|
{
|
|
dictionary.Add(variable.Key, ChangeType(variable.Value, requiredDictionaryType, culture));
|
|
}
|
|
return dictionary;
|
|
}
|
|
}
|
|
if (typeof(IList).IsAssignableFrom(requiredType))
|
|
{
|
|
if (value is ListUserData d)
|
|
{
|
|
if (d.List.GetType() == requiredType)
|
|
return d.List;
|
|
|
|
var generics = requiredType.GetGenericArguments();
|
|
var l1 = typeof(List<>);
|
|
var requiredListType = l1.MakeGenericType(generics[0]);
|
|
var list = (IList)Activator.CreateInstance(requiredListType);
|
|
foreach (var variable in d.List)
|
|
{
|
|
list.Add(ChangeType(variable, requiredListType, culture));
|
|
}
|
|
return list;
|
|
}
|
|
if (value is ScriptTable.ScriptTable table)
|
|
{
|
|
var generics = requiredType.GetGenericArguments();
|
|
if (generics.Length > 0)
|
|
{
|
|
var l1 = typeof(List<>);
|
|
var requiredListType = l1.MakeGenericType(generics[0]);
|
|
var list = (IList)Activator.CreateInstance(requiredListType);
|
|
foreach (var variable in table.EvaluationScope.Variables)
|
|
{
|
|
list.Add(ChangeType(variable.Value, requiredListType, culture));
|
|
}
|
|
return list;
|
|
}
|
|
else
|
|
{
|
|
var elementType = requiredType.GetElementType();
|
|
var input = table.EvaluationScope.Variables.Select(x => ChangeType(x.Value, elementType, culture)).ToArray();
|
|
var array = Array.CreateInstance(elementType, input.Length);
|
|
input.CopyTo(array, 0);
|
|
return array;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (typeof(IEnumerable).IsAssignableFrom(requiredType))
|
|
{
|
|
if (value is ListUserData d)
|
|
{
|
|
var generics = requiredType.GetGenericArguments();
|
|
var l1 = typeof(List<>);
|
|
var requiredListType = l1.MakeGenericType(generics[0]);
|
|
var list = (IList)Activator.CreateInstance(requiredListType);
|
|
foreach (var variable in d.List)
|
|
{
|
|
list.Add(variable);
|
|
}
|
|
return list;
|
|
}
|
|
if (value is ScriptTable.ScriptTable table)
|
|
{
|
|
var generics = requiredType.GetGenericArguments();
|
|
var l1 = typeof(List<>);
|
|
var requiredListType = l1.MakeGenericType(generics[0]);
|
|
var list = (IList)Activator.CreateInstance(requiredListType);
|
|
foreach (var variable in table.EvaluationScope.Variables)
|
|
{
|
|
list.Add(variable.Value.ToCSharpObject());
|
|
}
|
|
return list;
|
|
}
|
|
}
|
|
|
|
var isScriptTypeRequired = typeof(ScriptType).IsAssignableFrom(requiredType);
|
|
var isScriptType = value is ScriptType;
|
|
if (!isScriptTypeRequired && isScriptType)
|
|
{
|
|
return ((ScriptType)value).ToCSharpObject();
|
|
}
|
|
|
|
if (isScriptTypeRequired && !isScriptType)
|
|
{
|
|
return value.ToScriptType();
|
|
}
|
|
|
|
|
|
return value;
|
|
}
|
|
|
|
public override void ReorderArgumentArray(ref object[] args, object state)
|
|
{
|
|
throw new System.NotImplementedException();
|
|
}
|
|
|
|
public override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] matches, System.Type[] types, ParameterModifier[] modifiers)
|
|
{
|
|
foreach (var match in matches)
|
|
{
|
|
if (IsValidMatch(match, types))
|
|
{
|
|
return match;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public override PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, System.Type returnType, System.Type[] indexes,
|
|
ParameterModifier[] modifiers)
|
|
{
|
|
throw new System.NotImplementedException();
|
|
}
|
|
}
|
|
} |