Rework of function calling to handle generics better

This commit is contained in:
Deukhoofd 2018-12-09 11:28:27 +01:00
parent 43d9360145
commit 422de5d4eb
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
5 changed files with 224 additions and 15 deletions

View File

@ -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)

View File

@ -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:

View File

@ -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();
}
}
}

View File

@ -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,17 +87,25 @@ 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))
{ {
if (property.SetMethod == null || property.SetMethod.IsPrivate) var property = value.GetType().GetProperty(prop.Name);
if (property != null)
{ {
return (true, $"Property '{member}' on type '{Type}' does not have a publicly available setter."); if (property.SetMethod == null || property.SetMethod.IsPrivate)
{
return (true, $"Property '{member}' on type '{Type}' does not have a publicly available setter.");
}
property.SetValue(value, newValue, BindingFlags.SetProperty, UpsilonBinder.Default, null,
CultureInfo.InvariantCulture);
return (false, null);
} }
property.SetValue(value, newValue.ToCSharpObject());
return (false, null);
} }
return (true, $"Cannot find member '{member}' on type '{Type}'"); return (true, $"Cannot find member '{member}' on type '{Type}'");

View File

@ -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);
} }
} }