Rework of function calling to handle generics better
This commit is contained in:
parent
43d9360145
commit
422de5d4eb
|
@ -49,13 +49,11 @@ namespace Upsilon.BaseTypes.ScriptFunction
|
||||||
objects.Add(script);
|
objects.Add(script);
|
||||||
if (_passScopeReference)
|
if (_passScopeReference)
|
||||||
objects.Add(scope);
|
objects.Add(scope);
|
||||||
objects.AddRange(_directTypeManipulation
|
objects.AddRange(variables.Select(x => (object) x).ToList() );
|
||||||
? variables.Select(x => (object) x).ToList()
|
|
||||||
: variables.Select(x => x.ToCSharpObject()).ToList());
|
|
||||||
object result;
|
object result;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
result = _object.GetType().InvokeMember(_method.Name, BindingFlags.InvokeMethod, System.Type.DefaultBinder,
|
result = _object.GetType().InvokeMember(_method.Name, BindingFlags.InvokeMethod, UpsilonBinder.Default,
|
||||||
_object, objects.ToArray());
|
_object, objects.ToArray());
|
||||||
}
|
}
|
||||||
catch (TargetInvocationException e)
|
catch (TargetInvocationException e)
|
||||||
|
|
|
@ -22,6 +22,10 @@ namespace Upsilon.BaseTypes
|
||||||
return new ScriptNumberLong(i);
|
return new ScriptNumberLong(i);
|
||||||
case short s:
|
case short s:
|
||||||
return new ScriptNumberLong(s);
|
return new ScriptNumberLong(s);
|
||||||
|
case sbyte s:
|
||||||
|
return new ScriptNumberLong(s);
|
||||||
|
case byte s:
|
||||||
|
return new ScriptNumberLong(s);
|
||||||
case long i:
|
case long i:
|
||||||
return new ScriptNumberLong(i);
|
return new ScriptNumberLong(i);
|
||||||
case float f:
|
case float f:
|
||||||
|
|
|
@ -0,0 +1,185 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Globalization;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
var validMatch = true;
|
||||||
|
var parameters = match.GetParameters();
|
||||||
|
for (var i = 0; i < parameters.Length; i++)
|
||||||
|
{
|
||||||
|
var matchParameter = parameters[i];
|
||||||
|
if (args.Length <= i)
|
||||||
|
{
|
||||||
|
if (matchParameter.IsOptional)
|
||||||
|
return match;
|
||||||
|
validMatch = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var argument = args[i];
|
||||||
|
var argumentType = argument.GetType();
|
||||||
|
|
||||||
|
var typeCode = System.Type.GetTypeCode(matchParameter.ParameterType);
|
||||||
|
switch (typeCode)
|
||||||
|
{
|
||||||
|
case TypeCode.Boolean:
|
||||||
|
if (argument is ScriptBoolean b)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TypeCode.Char:
|
||||||
|
case TypeCode.String:
|
||||||
|
if (argument is ScriptString s)
|
||||||
|
{
|
||||||
|
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 (argument is ScriptNumber numeric)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TypeCode.Object:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matchParameter.ParameterType.IsAssignableFrom(argumentType))
|
||||||
|
{
|
||||||
|
validMatch = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validMatch)
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object ChangeType(object value, System.Type type, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (type.IsInstanceOfType(value))
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var typeCode = System.Type.GetTypeCode(type);
|
||||||
|
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.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 (value is ScriptNumber numeric)
|
||||||
|
{
|
||||||
|
return Convert.ChangeType(numeric.ToCSharpObject(), typeCode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TypeCode.Object:
|
||||||
|
if (value is ScriptType t)
|
||||||
|
{
|
||||||
|
return t.ToCSharpObject();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var isScriptTypeRequired = typeof(ScriptType).IsAssignableFrom(type);
|
||||||
|
var isScriptType = value is ScriptType;
|
||||||
|
if (!isScriptTypeRequired && isScriptType)
|
||||||
|
{
|
||||||
|
return ((ScriptType)value).ToCSharpObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isScriptTypeRequired && !isScriptType)
|
||||||
|
{
|
||||||
|
return value.ToScriptType();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == typeof(IIterable))
|
||||||
|
{
|
||||||
|
var valueType = value.GetType();
|
||||||
|
if (typeof(IDictionary).IsAssignableFrom(valueType))
|
||||||
|
{
|
||||||
|
return new DictionaryUserData((IDictionary) value);
|
||||||
|
}
|
||||||
|
if (typeof(IList).IsAssignableFrom(valueType))
|
||||||
|
{
|
||||||
|
return new ListUserData((IList) value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, System.Type returnType, System.Type[] indexes,
|
||||||
|
ParameterModifier[] modifiers)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Upsilon.BaseTypes.ScriptFunction;
|
using Upsilon.BaseTypes.ScriptFunction;
|
||||||
|
@ -59,11 +60,17 @@ namespace Upsilon.BaseTypes.UserData
|
||||||
member = member.ToLowerInvariant();
|
member = member.ToLowerInvariant();
|
||||||
if (Variables.TryGetValue(member, out var info))
|
if (Variables.TryGetValue(member, out var info))
|
||||||
{
|
{
|
||||||
return (info.GetValue(value).ToScriptType(), false, null);
|
var field = value.GetType().GetField(info.Name);
|
||||||
|
return (field.GetValue(value).ToScriptType(), false, null);
|
||||||
}
|
}
|
||||||
if (Properties.TryGetValue(member, out var property))
|
if (Properties.TryGetValue(member, out var prop))
|
||||||
{
|
{
|
||||||
return (property.GetValue(value).ToScriptType(), false, null);
|
var property = value.GetType().GetProperty(prop.Name);
|
||||||
|
if (property != null)
|
||||||
|
{
|
||||||
|
return (property.GetValue(value, BindingFlags.GetProperty, UpsilonBinder.Default, null,
|
||||||
|
CultureInfo.InvariantCulture).ToScriptType(), false, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (Methods.TryGetValue(member, out var method))
|
if (Methods.TryGetValue(member, out var method))
|
||||||
{
|
{
|
||||||
|
@ -80,18 +87,26 @@ namespace Upsilon.BaseTypes.UserData
|
||||||
return (true, "Invalid Type");
|
return (true, "Invalid Type");
|
||||||
if (Variables.TryGetValue(member, out var info))
|
if (Variables.TryGetValue(member, out var info))
|
||||||
{
|
{
|
||||||
info.SetValue(value, newValue.ToCSharpObject());
|
var field = value.GetType().GetField(info.Name);
|
||||||
|
field.SetValue(value, newValue, BindingFlags.SetField,
|
||||||
|
UpsilonBinder.Default, CultureInfo.InvariantCulture);
|
||||||
return (false, null);
|
return (false, null);
|
||||||
}
|
}
|
||||||
if (Properties.TryGetValue(member, out var property))
|
if (Properties.TryGetValue(member, out var prop))
|
||||||
|
{
|
||||||
|
var property = value.GetType().GetProperty(prop.Name);
|
||||||
|
if (property != null)
|
||||||
{
|
{
|
||||||
if (property.SetMethod == null || property.SetMethod.IsPrivate)
|
if (property.SetMethod == null || property.SetMethod.IsPrivate)
|
||||||
{
|
{
|
||||||
return (true, $"Property '{member}' on type '{Type}' does not have a publicly available setter.");
|
return (true, $"Property '{member}' on type '{Type}' does not have a publicly available setter.");
|
||||||
}
|
}
|
||||||
property.SetValue(value, newValue.ToCSharpObject());
|
|
||||||
|
property.SetValue(value, newValue, BindingFlags.SetProperty, UpsilonBinder.Default, null,
|
||||||
|
CultureInfo.InvariantCulture);
|
||||||
return (false, null);
|
return (false, null);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (true, $"Cannot find member '{member}' on type '{Type}'");
|
return (true, $"Cannot find member '{member}' on type '{Type}'");
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,18 @@ namespace Upsilon.StandardLibraries
|
||||||
[ScriptFunction("assert", "Asserts that the parameter passed is true. Throws an exception if it is not true. " +
|
[ScriptFunction("assert", "Asserts that the parameter passed is true. Throws an exception if it is not true. " +
|
||||||
"Can take a message to show in the exception, otherwise throws with message \"assertion failed!\"",
|
"Can take a message to show in the exception, otherwise throws with message \"assertion failed!\"",
|
||||||
directScriptManipulation: true)]
|
directScriptManipulation: true)]
|
||||||
public void Assert(ScriptBoolean boolean, ScriptString message = null)
|
public void Assert(ScriptBoolean boolean)
|
||||||
{
|
{
|
||||||
if (!boolean)
|
if (!boolean)
|
||||||
{
|
{
|
||||||
Error(message ?? new ScriptString("assertion failed!"));
|
Error(new ScriptString("assertion failed!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void Assert(ScriptBoolean boolean, ScriptString message)
|
||||||
|
{
|
||||||
|
if (!boolean)
|
||||||
|
{
|
||||||
|
Error(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue