From e57129e116eef257088563578ad6f574e1f6798d Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Tue, 11 Dec 2018 15:50:24 +0100 Subject: [PATCH] Work on calling CSharp methods --- .../ScriptFunction/ScriptFunction.cs | 3 +- .../ScriptMethodInfoFunction.cs | 67 ++++++++++++++++++- .../ScriptFunction/ScriptRuntimeFunction.cs | 3 +- Upsilon/BaseTypes/UserData/UpsilonBinder.cs | 47 ++++++++----- Upsilon/BaseTypes/UserData/UserDataMethod.cs | 3 +- Upsilon/Evaluator/Evaluator.cs | 2 +- 6 files changed, 100 insertions(+), 25 deletions(-) diff --git a/Upsilon/BaseTypes/ScriptFunction/ScriptFunction.cs b/Upsilon/BaseTypes/ScriptFunction/ScriptFunction.cs index 3229361..0ffd3e6 100644 --- a/Upsilon/BaseTypes/ScriptFunction/ScriptFunction.cs +++ b/Upsilon/BaseTypes/ScriptFunction/ScriptFunction.cs @@ -1,4 +1,5 @@ using Upsilon.Evaluator; +using Upsilon.Text; namespace Upsilon.BaseTypes.ScriptFunction { @@ -15,6 +16,6 @@ namespace Upsilon.BaseTypes.ScriptFunction return null; } - public abstract ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope); + public abstract ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, TextSpan span); } } \ No newline at end of file diff --git a/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs b/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs index bb6f58a..9557ad1 100644 --- a/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs +++ b/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs @@ -1,9 +1,12 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Reflection; using Upsilon.BaseTypes.UserData; using Upsilon.Evaluator; +using Upsilon.Exceptions; +using Upsilon.Text; namespace Upsilon.BaseTypes.ScriptFunction { @@ -42,7 +45,7 @@ namespace Upsilon.BaseTypes.ScriptFunction return _method.GetMethods().First().Parameters; } - public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope) + public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, TextSpan span) { var objects = new List(); if (_passScriptReference) @@ -53,18 +56,76 @@ namespace Upsilon.BaseTypes.ScriptFunction object result; try { + // If no parent object for the function was given, this is likely a lambda function, these need to be static if (_object == null) { + // grab the parameters, and just invoke it var array = objects.ToArray(); var methodInfo = _method.GetMethod(ref array); result = methodInfo.Invoke(null, array); } else { - result = _object.GetType().InvokeMember(_method.Name, BindingFlags.InvokeMethod, UpsilonBinder.Default, - _object, objects.ToArray()); + // we create an array for the types of the objects + var convertedTypes = new System.Type[objects.Count]; + for (var index = 0; index < objects.Count; index++) + { + // we grab the type of each parameter, and grab the underlying type if its a userdata object, as we need these + var arg = objects[index]; + if (arg is IUserData ud) + { + convertedTypes[index] = ud.GetCSharpType(); + } + else + { + convertedTypes[index] = arg.GetType(); + } + } + + // grab all public methods + var methods = _object.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance); + MethodInfo method = null; + var numberOfParametersFilled = -1; + // iterate over the methods on the object + foreach (var match in methods) + { + // if the method does not have the name we're looking for, continue + if (!string.Equals(match.Name, _method.Name)) continue; + // Use the type binder to check if the match is allowed + if (UpsilonBinder.Default.IsValidMatch(match, convertedTypes)) + { + // grab the number of parameters on the match + var parCount = match.GetParameters().Length; + // We always try to grab the function with the most parameters + if (parCount > numberOfParametersFilled) + { + method = match; + numberOfParametersFilled = parCount; + } + } + } + // if we haven't found a method, we throw an exception + if (method == null) + { + var (i, pos) = diagnostics.ScriptString.GetLinePosition(span.Start); + var line = diagnostics.ScriptString.GetLine(i); + throw new ScriptRuntimeException( + $"Can't find method {_method.Name} with parameter types {string.Join(", ", convertedTypes.Select(x => x.Name))} on type {_object.GetType().Name}", + i, pos, line); + } + // get the method parameters + var parameters = method.GetParameters(); + var arguments = objects.ToArray(); + // if the number of parameters does not match, we need to resize our given parameters + if (parameters.Length != arguments.Length) + { + Array.Resize(ref arguments, parameters.Length); + } + // invoke the method + result = method.Invoke(_object, BindingFlags.InvokeMethod, UpsilonBinder.Default, arguments, CultureInfo.InvariantCulture); } } + // Catch any exception inside the invocation catch (TargetInvocationException e) { if (e.InnerException != null) throw e.InnerException; diff --git a/Upsilon/BaseTypes/ScriptFunction/ScriptRuntimeFunction.cs b/Upsilon/BaseTypes/ScriptFunction/ScriptRuntimeFunction.cs index a52bfcd..3dd3263 100644 --- a/Upsilon/BaseTypes/ScriptFunction/ScriptRuntimeFunction.cs +++ b/Upsilon/BaseTypes/ScriptFunction/ScriptRuntimeFunction.cs @@ -2,6 +2,7 @@ using System.Collections.Immutable; using Upsilon.BaseTypes.ScriptTypeInterfaces; using Upsilon.Binder; using Upsilon.Evaluator; +using Upsilon.Text; namespace Upsilon.BaseTypes.ScriptFunction { @@ -19,7 +20,7 @@ namespace Upsilon.BaseTypes.ScriptFunction EvaluationScope = evaluationScope; } - public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope) + public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, TextSpan span) { var innerEvaluator = new Evaluator.Evaluator(diagnostics, EvaluationScope, script); for (var i = 0; i < Parameters.Length; i++) diff --git a/Upsilon/BaseTypes/UserData/UpsilonBinder.cs b/Upsilon/BaseTypes/UserData/UpsilonBinder.cs index 233781e..54c145e 100644 --- a/Upsilon/BaseTypes/UserData/UpsilonBinder.cs +++ b/Upsilon/BaseTypes/UserData/UpsilonBinder.cs @@ -25,7 +25,7 @@ namespace Upsilon.BaseTypes.UserData foreach (var match in matches) { - if (IsValidMatch(match, ref args)) + if (IsValidMatch(match, args.Select(x => x.GetType()).ToArray())) return match; } @@ -33,27 +33,26 @@ namespace Upsilon.BaseTypes.UserData return null; } - public bool IsValidMatch(MethodBase match, ref object[] args) + 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 (args.Length <= i) + if (argumentTypes.Length <= i) { if (matchParameter.IsOptional) return true; return false; } - var argument = args[i]; - var argumentType = argument.GetType(); + var argumentType = argumentTypes[i]; var typeCode = System.Type.GetTypeCode(matchParameter.ParameterType); switch (typeCode) { case TypeCode.Boolean: - if (argument is ScriptBoolean) + if (argumentType == typeof(ScriptBoolean)) { continue; } @@ -61,7 +60,7 @@ namespace Upsilon.BaseTypes.UserData break; case TypeCode.Char: case TypeCode.String: - if (argument is ScriptString) + if (argumentType == typeof(ScriptString)) { continue; } @@ -78,7 +77,7 @@ namespace Upsilon.BaseTypes.UserData case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: - if (argument is ScriptNumber) + if (typeof(ScriptNumber).IsAssignableFrom(argumentType)) { continue; } @@ -88,14 +87,6 @@ namespace Upsilon.BaseTypes.UserData continue; } - if (!typeof(ScriptType).IsAssignableFrom(matchParameter.ParameterType) && - argument is IUserData ud) - { - var csharpType = ud.GetCSharpType(); - if (matchParameter.ParameterType.IsAssignableFrom(csharpType)) - continue; - } - if (argumentType == typeof(ScriptNull)) { continue; @@ -195,7 +186,20 @@ namespace Upsilon.BaseTypes.UserData if (typeof(IList).IsAssignableFrom(type)) { if (value is ListUserData d) - return d.List; + { + if (d.List.GetType() == type) + return d.List; + + var generics = type.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 = type.GetGenericArguments(); @@ -262,8 +266,15 @@ namespace Upsilon.BaseTypes.UserData public override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] matches, System.Type[] types, ParameterModifier[] modifiers) { - throw new System.NotImplementedException(); + 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, diff --git a/Upsilon/BaseTypes/UserData/UserDataMethod.cs b/Upsilon/BaseTypes/UserData/UserDataMethod.cs index 160635d..7cf595a 100644 --- a/Upsilon/BaseTypes/UserData/UserDataMethod.cs +++ b/Upsilon/BaseTypes/UserData/UserDataMethod.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Reflection; using Upsilon.Evaluator; using Upsilon.StandardLibraries; @@ -78,7 +79,7 @@ namespace Upsilon.BaseTypes.UserData foreach (var userDataMethodPart in MethodParts) { var methodBase = userDataMethodPart.Method; - if (UpsilonBinder.Default.IsValidMatch(methodBase, ref arguments)) + if (UpsilonBinder.Default.IsValidMatch(methodBase, arguments.Select(x => x.GetType()).ToArray())) { var parameters = methodBase.GetParameters(); for (var i = 0; i < parameters.Length; i++) diff --git a/Upsilon/Evaluator/Evaluator.cs b/Upsilon/Evaluator/Evaluator.cs index d9ccd71..6670f9a 100644 --- a/Upsilon/Evaluator/Evaluator.cs +++ b/Upsilon/Evaluator/Evaluator.cs @@ -538,7 +538,7 @@ namespace Upsilon.Evaluator ls.Add(evaluate); } - var val = function.Run(_diagnostics, ls.ToArray(), _script, Scope); + var val = function.Run(_diagnostics, ls.ToArray(), _script, Scope, boundFunctionCallExpression.Span); return val; }