From 422de5d4ebd0f8c7954bbdc3dbd4e106f6babda4 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sun, 9 Dec 2018 11:28:27 +0100 Subject: [PATCH] Rework of function calling to handle generics better --- .../ScriptMethodInfoFunction.cs | 6 +- Upsilon/BaseTypes/TypeConversion.cs | 4 + Upsilon/BaseTypes/UserData/UpsilonBinder.cs | 185 ++++++++++++++++++ Upsilon/BaseTypes/UserData/UserDataType.cs | 33 +++- Upsilon/StandardLibraries/BasicFunctions.cs | 11 +- 5 files changed, 224 insertions(+), 15 deletions(-) create mode 100644 Upsilon/BaseTypes/UserData/UpsilonBinder.cs diff --git a/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs b/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs index 3787024..4147ce0 100644 --- a/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs +++ b/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs @@ -49,13 +49,11 @@ namespace Upsilon.BaseTypes.ScriptFunction objects.Add(script); if (_passScopeReference) objects.Add(scope); - objects.AddRange(_directTypeManipulation - ? variables.Select(x => (object) x).ToList() - : variables.Select(x => x.ToCSharpObject()).ToList()); + objects.AddRange(variables.Select(x => (object) x).ToList() ); object result; 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()); } catch (TargetInvocationException e) diff --git a/Upsilon/BaseTypes/TypeConversion.cs b/Upsilon/BaseTypes/TypeConversion.cs index 8d4fc82..9f4b8ff 100644 --- a/Upsilon/BaseTypes/TypeConversion.cs +++ b/Upsilon/BaseTypes/TypeConversion.cs @@ -22,6 +22,10 @@ namespace Upsilon.BaseTypes return new ScriptNumberLong(i); case short s: return new ScriptNumberLong(s); + case sbyte s: + return new ScriptNumberLong(s); + case byte s: + return new ScriptNumberLong(s); case long i: return new ScriptNumberLong(i); case float f: diff --git a/Upsilon/BaseTypes/UserData/UpsilonBinder.cs b/Upsilon/BaseTypes/UserData/UpsilonBinder.cs new file mode 100644 index 0000000..b5ece23 --- /dev/null +++ b/Upsilon/BaseTypes/UserData/UpsilonBinder.cs @@ -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(); + } + } +} \ No newline at end of file diff --git a/Upsilon/BaseTypes/UserData/UserDataType.cs b/Upsilon/BaseTypes/UserData/UserDataType.cs index f1d0b67..d4861f3 100644 --- a/Upsilon/BaseTypes/UserData/UserDataType.cs +++ b/Upsilon/BaseTypes/UserData/UserDataType.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Reflection; using Upsilon.BaseTypes.ScriptFunction; @@ -59,11 +60,17 @@ namespace Upsilon.BaseTypes.UserData member = member.ToLowerInvariant(); 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)) { @@ -80,17 +87,25 @@ namespace Upsilon.BaseTypes.UserData return (true, "Invalid Type"); 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); } - 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}'"); diff --git a/Upsilon/StandardLibraries/BasicFunctions.cs b/Upsilon/StandardLibraries/BasicFunctions.cs index d862eef..82437d1 100644 --- a/Upsilon/StandardLibraries/BasicFunctions.cs +++ b/Upsilon/StandardLibraries/BasicFunctions.cs @@ -16,11 +16,18 @@ namespace Upsilon.StandardLibraries [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!\"", directScriptManipulation: true)] - public void Assert(ScriptBoolean boolean, ScriptString message = null) + public void Assert(ScriptBoolean 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); } }