Support for calling userdata functions

This commit is contained in:
Deukhoofd 2019-06-21 23:07:42 +02:00
parent 0ff2815694
commit f23f682d52
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
7 changed files with 170 additions and 19 deletions

View File

@ -13,15 +13,14 @@ namespace PorygonSharp
_handle = handle; _handle = handle;
} }
public EvalValue(object o) public static EvalValue CreateValue(object o)
{ {
var type = o.GetType(); var type = o.GetType();
var typeCode = Type.GetTypeCode(type); var typeCode = Type.GetTypeCode(type);
switch (typeCode) switch (typeCode)
{ {
case TypeCode.Boolean: case TypeCode.Boolean:
_handle = CreateBoolEvalValue((bool)o); return new EvalValue(CreateBoolEvalValue((bool)o));
break;
case TypeCode.Byte: case TypeCode.Byte:
case TypeCode.SByte: case TypeCode.SByte:
case TypeCode.Int16: case TypeCode.Int16:
@ -30,22 +29,18 @@ namespace PorygonSharp
case TypeCode.UInt16: case TypeCode.UInt16:
case TypeCode.UInt32: case TypeCode.UInt32:
case TypeCode.UInt64: case TypeCode.UInt64:
_handle = CreateIntegerEvalValue(Convert.ToInt64(o)); return new EvalValue(CreateIntegerEvalValue(Convert.ToInt64(o)));
break;
case TypeCode.Char: case TypeCode.Char:
case TypeCode.String: case TypeCode.String:
_handle = CreateStringEvalValue(o.ToString()); return new EvalValue(CreateStringEvalValue(o.ToString()));
break;
case TypeCode.Decimal: case TypeCode.Decimal:
case TypeCode.Double: case TypeCode.Double:
case TypeCode.Single: case TypeCode.Single:
_handle = CreateFloatEvalValue(Convert.ToDouble(o)); return new EvalValue(CreateFloatEvalValue(Convert.ToDouble(o)));
break;
case TypeCode.Object: case TypeCode.Object:
var typeHash = UserDataHandler.GetTypeId(type); var typeHash = UserDataHandler.GetTypeId(type);
var handle = GCHandle.Alloc(o, GCHandleType.WeakTrackResurrection); var handle = GCHandle.Alloc(o, GCHandleType.WeakTrackResurrection);
_handle = CreateUserDataEvalValue(typeHash, GCHandle.ToIntPtr(handle)); return new EvalValue(CreateUserDataEvalValue(typeHash, GCHandle.ToIntPtr(handle)));
break;
case TypeCode.DateTime: case TypeCode.DateTime:
case TypeCode.DBNull: case TypeCode.DBNull:
case TypeCode.Empty: case TypeCode.Empty:
@ -54,6 +49,12 @@ namespace PorygonSharp
} }
} }
public static EvalValue FunctionEvalValue(IntPtr func, IntPtr parent)
{
return new EvalValue(CreateFunctionEvalValue(func, parent));
}
public void Dispose() public void Dispose()
{ {
if (_handle != IntPtr.Zero) if (_handle != IntPtr.Zero)
@ -89,6 +90,12 @@ namespace PorygonSharp
return Marshal.PtrToStringUni(ptr); return Marshal.PtrToStringUni(ptr);
} }
public object EvaluateGenericObject()
{
var ptr = EvaluateUserDataObj(_handle);
return GCHandle.FromIntPtr(ptr).Target;
}
public object GetObjectValue() public object GetObjectValue()
{ {
switch (GetTypeClass()) switch (GetTypeClass())
@ -101,8 +108,9 @@ namespace PorygonSharp
return EvaluateBool(); return EvaluateBool();
case TypeClass.String: case TypeClass.String:
return EvaluateString(); return EvaluateString();
case TypeClass.Function:
case TypeClass.UserData: case TypeClass.UserData:
return EvaluateGenericObject();
case TypeClass.Function:
case TypeClass.Table: case TypeClass.Table:
case TypeClass.Error: case TypeClass.Error:
default: default:
@ -127,6 +135,8 @@ namespace PorygonSharp
private static extern bool EvaluateBool(IntPtr ptr); private static extern bool EvaluateBool(IntPtr ptr);
[DllImport("libPorygonLang", EntryPoint = "EvaluateEvalValueString",CallingConvention = CallingConvention.Cdecl)] [DllImport("libPorygonLang", EntryPoint = "EvaluateEvalValueString",CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr EvaluateString(IntPtr ptr); private static extern IntPtr EvaluateString(IntPtr ptr);
[DllImport("libPorygonLang", EntryPoint = "EvaluateUserDataObj",CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr EvaluateUserDataObj(IntPtr ptr);
[DllImport("libPorygonLang", EntryPoint = "CreateIntegerEvalValue",CallingConvention = CallingConvention.Cdecl)] [DllImport("libPorygonLang", EntryPoint = "CreateIntegerEvalValue",CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateIntegerEvalValue(long l); private static extern IntPtr CreateIntegerEvalValue(long l);
@ -138,5 +148,9 @@ namespace PorygonSharp
private static extern IntPtr CreateStringEvalValue(string s); private static extern IntPtr CreateStringEvalValue(string s);
[DllImport("libPorygonLang", EntryPoint = "CreateUserDataEvalValue",CallingConvention = CallingConvention.Cdecl)] [DllImport("libPorygonLang", EntryPoint = "CreateUserDataEvalValue",CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateUserDataEvalValue(uint typeHash, IntPtr obj); private static extern IntPtr CreateUserDataEvalValue(uint typeHash, IntPtr obj);
[DllImport("libPorygonLang", EntryPoint = "CreateFunctionEvalValue",CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateFunctionEvalValue(IntPtr func, IntPtr parent);
} }
} }

View File

@ -75,7 +75,7 @@ namespace PorygonSharp
public EvalValue CallFunction(string key, params object[] parameters) public EvalValue CallFunction(string key, params object[] parameters)
{ {
var scriptParameters = parameters.Select(x => new EvalValue(x).GetPointer()).ToArray(); var scriptParameters = parameters.Select(x => EvalValue.CreateValue(x).GetPointer()).ToArray();
var ptr = CallFunction(_internalScriptHandle, key, scriptParameters, scriptParameters.Length); var ptr = CallFunction(_internalScriptHandle, key, scriptParameters, scriptParameters.Length);
return new EvalValue(ptr); return new EvalValue(ptr);
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace PorygonSharp.ScriptType namespace PorygonSharp.ScriptType
@ -28,8 +29,35 @@ namespace PorygonSharp.ScriptType
case TypeCode.Char: case TypeCode.Char:
case TypeCode.String: case TypeCode.String:
return CreateStringScriptType(false, 0); return CreateStringScriptType(false, 0);
case TypeCode.Object:
if (t == typeof(void))
{
return CreateScriptType(TypeClass.Nil);
}
goto default;
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException(t.FullName);
}
}
internal static IntPtr GetFunctionScriptType(MethodInfo info)
{
try
{
var returnType = GetScriptType(info.ReturnType);
var parameters = info.GetParameters();
var parameterFuncs = new IntPtr[parameters.Length];
for (var index = 0; index < parameters.Length; index++)
{
var parameter = parameters[index];
parameterFuncs[index] = GetScriptType(parameter.ParameterType);
}
return CreateUserDataFunctionScriptType(returnType, parameterFuncs, parameters.Length);
}
catch
{
return IntPtr.Zero;
} }
} }
@ -42,5 +70,7 @@ namespace PorygonSharp.ScriptType
[DllImport("libPorygonLang", EntryPoint = "CreateStringScriptType", CallingConvention = CallingConvention.Cdecl)] [DllImport("libPorygonLang", EntryPoint = "CreateStringScriptType", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateStringScriptType(bool knownAtBind, uint hash); private static extern IntPtr CreateStringScriptType(bool knownAtBind, uint hash);
[DllImport("libPorygonLang", EntryPoint = "CreateUserDataFunctionScriptType", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateUserDataFunctionScriptType(IntPtr returnType, IntPtr[] parameters, int parameterCount);
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using PorygonSharp.Utilities; using PorygonSharp.Utilities;
@ -12,6 +13,8 @@ namespace PorygonSharp.UserData
private delegate IntPtr GetterDelegate(IntPtr ptr); private delegate IntPtr GetterDelegate(IntPtr ptr);
private delegate void SetterDelegate(IntPtr ptr, IntPtr val); private delegate void SetterDelegate(IntPtr ptr, IntPtr val);
private delegate IntPtr CallerDelegate(IntPtr parent,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)]IntPtr[] parameters, int size);
public static void RegisterType(string name, Type type) public static void RegisterType(string name, Type type)
{ {
@ -34,6 +37,12 @@ namespace PorygonSharp.UserData
{ {
RegisterProperty(property, hash); RegisterProperty(property, hash);
} }
var methods = type.GetMethods(bindingFlags);
foreach (var method in methods)
{
RegisterFunction(method, hash);
}
} }
private static void RegisterField(FieldInfo field, uint typeHash) private static void RegisterField(FieldInfo field, uint typeHash)
@ -42,7 +51,7 @@ namespace PorygonSharp.UserData
{ {
var obj = GCHandle.FromIntPtr(ptr).Target; var obj = GCHandle.FromIntPtr(ptr).Target;
var value = field.GetValue(obj); var value = field.GetValue(obj);
return new EvalValue(value).GetPointer(); return EvalValue.CreateValue(value).GetPointer();
}); });
var setterPtr = IntPtr.Zero; var setterPtr = IntPtr.Zero;
@ -73,7 +82,7 @@ namespace PorygonSharp.UserData
{ {
var obj = GCHandle.FromIntPtr(ptr).Target; var obj = GCHandle.FromIntPtr(ptr).Target;
var value = property.GetValue(obj); var value = property.GetValue(obj);
return new EvalValue(value).GetPointer(); return EvalValue.CreateValue(value).GetPointer();
}); });
getterPtr = Marshal.GetFunctionPointerForDelegate(getter); getterPtr = Marshal.GetFunctionPointerForDelegate(getter);
} }
@ -95,6 +104,41 @@ namespace PorygonSharp.UserData
RegisterUserDataField(typeHash, fieldName, userDataField); RegisterUserDataField(typeHash, fieldName, userDataField);
} }
private static void RegisterFunction(MethodInfo method, uint typeHash)
{
var parameterTypes = method.GetParameters().Select(x => x.ParameterType).ToArray();
var getter = new GetterDelegate(ptr =>
{
var func = new CallerDelegate((parent, parameters, size) =>
{
var evaluatedParameters = new object[size];
for (var i = 0; i < size; i++)
{
var eval = new EvalValue(parameters[i]);
var val = eval.GetObjectValue();
var convertedType = Convert.ChangeType(val, parameterTypes[i]);
evaluatedParameters[i] = convertedType;
}
var parentObj = GCHandle.FromIntPtr(parent).Target;
var result = method.Invoke(parentObj, evaluatedParameters);
return EvalValue.CreateValue(result).GetPointer();
});
var funcPtr = Marshal.GetFunctionPointerForDelegate(func);
return EvalValue.FunctionEvalValue(funcPtr, ptr).GetPointer();
});
var getterPtr = Marshal.GetFunctionPointerForDelegate(getter);
var type = ScriptType.ScriptType.GetFunctionScriptType(method);
if (type == IntPtr.Zero)
return;
var userDataField = CreateUserDataField(type, getterPtr, IntPtr.Zero);
var fieldName = method.Name.ScriptHash();
RegisterUserDataField(typeHash, fieldName, userDataField);
}
public static uint GetTypeId(Type t) public static uint GetTypeId(Type t)
{ {
return UserDataLookup[t]; return UserDataLookup[t];

View File

@ -2,7 +2,6 @@ using System;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using PorygonSharp; using PorygonSharp;
using PorygonSharp.UserData;
using PorygonSharp.Utilities; using PorygonSharp.Utilities;
namespace PorygonSharpTests namespace PorygonSharpTests

View File

@ -15,6 +15,16 @@ namespace PorygonSharpTests
public int Bar { get; set; } public int Bar { get; set; }
public int GetOnly { get; } = 865; public int GetOnly { get; } = 865;
public readonly int ReadOnly = 684; public readonly int ReadOnly = 684;
public int TestFunc()
{
return 345435;
}
public int Add(int a, int b)
{
return a + b;
}
} }
[Test] [Test]
@ -146,5 +156,59 @@ end
} }
} }
[Test]
public void CanCallUserdataFunction()
{
UserDataHandler.RegisterType("testObject", typeof(UserDataTestObject));
using (var script = new Script(@"
function test(testObject v)
result = v.TestFunc()
end
"))
{
var diags = script.Diagnostics.GetDiagnostics();
foreach (var diag in diags)
{
throw new Exception(script.Diagnostics.GetFullDiagnosticMessage(diag));
}
script.Evaluate();
var parameter = new UserDataTestObject();
script.CallFunction("test", parameter);
var variable = script.GetVariable("result");
Assert.AreEqual(TypeClass.Number ,variable.GetTypeClass());
Assert.AreEqual(345435, script.GetVariable("result").EvaluateInteger());
}
}
[Test]
public void CanCallUserdataFunctionWithArguments()
{
UserDataHandler.RegisterType("testObject", typeof(UserDataTestObject));
using (var script = new Script(@"
function test(testObject v)
result = v.Add(5, 200)
end
"))
{
var diags = script.Diagnostics.GetDiagnostics();
foreach (var diag in diags)
{
throw new Exception(script.Diagnostics.GetFullDiagnosticMessage(diag));
}
script.Evaluate();
var parameter = new UserDataTestObject();
script.CallFunction("test", parameter);
var variable = script.GetVariable("result");
Assert.AreEqual(TypeClass.Number ,variable.GetTypeClass());
Assert.AreEqual(205, script.GetVariable("result").EvaluateInteger());
}
}
} }
} }

Binary file not shown.