From 8d16c1fb35c63a670430c9841b458f64f875a167 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Thu, 12 Sep 2019 20:23:25 +0200 Subject: [PATCH] Support for turning delegates into script functions --- PorygonSharp/EvalValues/DelegateEvalValue.cs | 41 +++++++++++++++++++ PorygonSharp/EvalValues/EvalValueCreator.cs | 15 +++++-- PorygonSharp/ScriptType/ScriptTypeHandler.cs | 13 ++++++ PorygonSharp/StaticScope.cs | 7 +++- PorygonSharp/TypeClass.cs | 1 + .../UserData/UserDataBinaryOperation.cs | 13 +----- PorygonSharp/UserData/UserDataField.cs | 18 +------- PorygonSharp/Utilities/Caster.cs | 23 +++++++++++ 8 files changed, 99 insertions(+), 32 deletions(-) create mode 100644 PorygonSharp/EvalValues/DelegateEvalValue.cs create mode 100644 PorygonSharp/Utilities/Caster.cs diff --git a/PorygonSharp/EvalValues/DelegateEvalValue.cs b/PorygonSharp/EvalValues/DelegateEvalValue.cs new file mode 100644 index 0000000..da20220 --- /dev/null +++ b/PorygonSharp/EvalValues/DelegateEvalValue.cs @@ -0,0 +1,41 @@ +using System; +using System.Linq; +using System.Runtime.InteropServices; +using PorygonSharp.Utilities; + +namespace PorygonSharp.EvalValues +{ + public class DelegateEvalValue + { + private delegate IntPtr InvokeDelegate(IntPtr _, IntPtr options, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)]IntPtr[] parameters, int parameterCount); + + private readonly Delegate _delegate; + private readonly InvokeDelegate _handler; + public readonly IntPtr Pointer; + + public DelegateEvalValue(Delegate del) + { + _delegate = del; + _handler = Invoke; + var funcPtr = Marshal.GetFunctionPointerForDelegate(_handler); + Pointer = EvalValueCreator.CreateFunctionEvalValue(funcPtr, IntPtr.Zero); + } + + private IntPtr Invoke(IntPtr _, IntPtr options, IntPtr[] parPtrs, int parameterCount) + { + var parameters = new object[parameterCount]; + var parametersTypes = _delegate.Method.GetParameters().Select(x => x.ParameterType).ToArray(); + for (var i = 0; i < parameterCount; i++) + { + var val = new EvalValue(parPtrs[i]); + parameters[i] = Caster.Cast(val, parametersTypes[i]); + } + + var result = _delegate.DynamicInvoke(parameters); + return EvalValueCreator.CreateValue(result).GetPointer(); + } + + + } +} \ No newline at end of file diff --git a/PorygonSharp/EvalValues/EvalValueCreator.cs b/PorygonSharp/EvalValues/EvalValueCreator.cs index b29d4f3..f3eb749 100644 --- a/PorygonSharp/EvalValues/EvalValueCreator.cs +++ b/PorygonSharp/EvalValues/EvalValueCreator.cs @@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Collections.Generic; using System.Runtime.InteropServices; using PorygonSharp.UserData; @@ -43,6 +42,10 @@ namespace PorygonSharp.EvalValues case TypeCode.Object: if (typeof(IList).IsAssignableFrom(type)) return CreateListEvalValue((IList)o, type); + if (typeof(Delegate).IsAssignableFrom(type)) + { + return CreateDelegateEvalValue((Delegate) o); + } if (!UserDataHandler.IsTypeRegistered(type) && !UserDataHandler.TryResolveType(type)) { throw new Exception($"Type is not registered for use: {type.FullName}"); @@ -67,9 +70,15 @@ namespace PorygonSharp.EvalValues helper.GetIteratorPtr, helper.GetLengthPtr); return new EvalValue(ptr); } + + private static EvalValue CreateDelegateEvalValue(Delegate del) + { + var val = new DelegateEvalValue(del); + return new EvalValue(val.Pointer); + } [DllImport("PorygonLang", EntryPoint = "CreateNilEvalValue",CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr CreateNilEvalValue(); + private static extern IntPtr CreateNilEvalValue(); [DllImport("PorygonLang", EntryPoint = "CreateIntegerEvalValue",CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr CreateIntegerEvalValue(long l); [DllImport("PorygonLang", EntryPoint = "CreateFloatEvalValue",CallingConvention = CallingConvention.Cdecl)] @@ -82,7 +91,7 @@ namespace PorygonSharp.EvalValues private static extern IntPtr CreateUserDataEvalValue(uint typeHash, IntPtr obj); [DllImport("PorygonLang", EntryPoint = "CreateFunctionEvalValue",CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr CreateFunctionEvalValue(IntPtr func, IntPtr parent); + internal static extern IntPtr CreateFunctionEvalValue(IntPtr func, IntPtr parent); [DllImport("PorygonLang", EntryPoint = "CreateCollectionValue", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr CreateCollectionValue(IntPtr type, IntPtr parent, IntPtr getter, IntPtr setter, diff --git a/PorygonSharp/ScriptType/ScriptTypeHandler.cs b/PorygonSharp/ScriptType/ScriptTypeHandler.cs index 055ebd8..3256752 100644 --- a/PorygonSharp/ScriptType/ScriptTypeHandler.cs +++ b/PorygonSharp/ScriptType/ScriptTypeHandler.cs @@ -10,6 +10,10 @@ namespace PorygonSharp.ScriptType { internal static IntPtr? GetScriptType(Type t) { + if (t == null) + { + return CreateScriptType(TypeClass.Nil); + } if (t.IsEnum && !t.IsGenericParameter) { if (UserDataHandler.IsTypeRegistered(t)) @@ -42,6 +46,10 @@ namespace PorygonSharp.ScriptType { return CreateScriptType(TypeClass.Nil); } + if (t == typeof(object)) + { + return CreateScriptType(TypeClass.Any); + } if (UserDataHandler.IsTypeRegistered(t)) { return UserDataHandler.CreateUserDataType(t); @@ -55,6 +63,11 @@ namespace PorygonSharp.ScriptType return CreateUserDataDictionaryType(t); } + if (typeof(Delegate).IsAssignableFrom(t)) + { + return GetFunctionScriptType(t.GetMethod("Invoke")); + } + var attr = t.GetCustomAttribute(); if (attr != null) { diff --git a/PorygonSharp/StaticScope.cs b/PorygonSharp/StaticScope.cs index 95e21d0..4e2326c 100644 --- a/PorygonSharp/StaticScope.cs +++ b/PorygonSharp/StaticScope.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Runtime.InteropServices; using PorygonSharp.EvalValues; using PorygonSharp.Utilities; @@ -7,14 +8,16 @@ namespace PorygonSharp { public static class StaticScope { + private static List _variables = new List(); public static void RegisterStaticVariable(string name, object o) { var type = o.GetType(); var scriptType = ScriptType.ScriptTypeHandler.GetScriptType(type); - var hash = name.ScriptHash(); - var value = EvalValueCreator.CreateValue(o); if (!scriptType.HasValue) return; + var hash = name.ScriptHash(); + var value = EvalValueCreator.CreateValue(o); + _variables.Add(o); RegisterStaticVariable(hash, scriptType.Value, value.GetPointer()); } diff --git a/PorygonSharp/TypeClass.cs b/PorygonSharp/TypeClass.cs index 9c1271a..c4da3df 100644 --- a/PorygonSharp/TypeClass.cs +++ b/PorygonSharp/TypeClass.cs @@ -10,5 +10,6 @@ namespace PorygonSharp Function, UserData, Table, + Any } } \ No newline at end of file diff --git a/PorygonSharp/UserData/UserDataBinaryOperation.cs b/PorygonSharp/UserData/UserDataBinaryOperation.cs index e3fd74d..33d4cc9 100644 --- a/PorygonSharp/UserData/UserDataBinaryOperation.cs +++ b/PorygonSharp/UserData/UserDataBinaryOperation.cs @@ -2,6 +2,7 @@ using System; using System.Reflection; using System.Runtime.InteropServices; using PorygonSharp.EvalValues; +using PorygonSharp.Utilities; namespace PorygonSharp.UserData { @@ -38,17 +39,7 @@ namespace PorygonSharp.UserData { var a = GCHandle.FromIntPtr(objectPtr).Target; var b = new EvalValue(variable); - var bVal = b.GetObjectValue(); - if (_parameterType.IsEnum) - { - var baseType = _parameterType.GetEnumUnderlyingType(); - bVal = Convert.ChangeType(bVal, baseType); - } - else if (bVal is IConvertible) - { - var convertedType = Convert.ChangeType(bVal, _parameterType); - bVal = convertedType; - } + var bVal = Caster.Cast(b, _parameterType); var o = _info.Invoke(null, new[] {a, bVal}); return EvalValueCreator.CreateValue(o).GetPointer(); } diff --git a/PorygonSharp/UserData/UserDataField.cs b/PorygonSharp/UserData/UserDataField.cs index ca269f9..80a9b55 100644 --- a/PorygonSharp/UserData/UserDataField.cs +++ b/PorygonSharp/UserData/UserDataField.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using PorygonSharp.EvalValues; +using PorygonSharp.Utilities; namespace PorygonSharp.UserData { @@ -70,22 +71,7 @@ namespace PorygonSharp.UserData for (var i = 0; i < size; i++) { var eval = new EvalValue(parameters[i]); - var val = eval.GetObjectValue(); - var desiredType = parameterTypes[i]; - if (desiredType.IsEnum) - { - var baseType = desiredType.GetEnumUnderlyingType(); - evaluatedParameters[i] = Convert.ChangeType(val, baseType); - } - else if (val is IConvertible) - { - var convertedType = Convert.ChangeType(val, desiredType); - evaluatedParameters[i] = convertedType; - } - else - { - evaluatedParameters[i] = val; - } + evaluatedParameters[i] = Caster.Cast(eval, parameterTypes[i]); } var parentObj = GCHandle.FromIntPtr(parent).Target; diff --git a/PorygonSharp/Utilities/Caster.cs b/PorygonSharp/Utilities/Caster.cs new file mode 100644 index 0000000..2089069 --- /dev/null +++ b/PorygonSharp/Utilities/Caster.cs @@ -0,0 +1,23 @@ +using System; +using PorygonSharp.EvalValues; + +namespace PorygonSharp.Utilities +{ + public static class Caster + { + public static object Cast(EvalValue val, Type t) + { + var eval = val.GetObjectValue(); + if (t.IsEnum) + { + var underlying = t.GetEnumUnderlyingType(); + return Convert.ChangeType(eval, underlying); + } + if (eval is IConvertible) + { + return Convert.ChangeType(eval, t); + } + return eval; + } + } +} \ No newline at end of file