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

View File

@ -1,4 +1,5 @@
using System;
using System.Reflection;
using System.Runtime.InteropServices;
namespace PorygonSharp.ScriptType
@ -28,8 +29,35 @@ namespace PorygonSharp.ScriptType
case TypeCode.Char:
case TypeCode.String:
return CreateStringScriptType(false, 0);
case TypeCode.Object:
if (t == typeof(void))
{
return CreateScriptType(TypeClass.Nil);
}
goto 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)]
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.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using PorygonSharp.Utilities;
@ -12,6 +13,8 @@ namespace PorygonSharp.UserData
private delegate IntPtr GetterDelegate(IntPtr ptr);
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)
{
@ -34,6 +37,12 @@ namespace PorygonSharp.UserData
{
RegisterProperty(property, hash);
}
var methods = type.GetMethods(bindingFlags);
foreach (var method in methods)
{
RegisterFunction(method, hash);
}
}
private static void RegisterField(FieldInfo field, uint typeHash)
@ -42,7 +51,7 @@ namespace PorygonSharp.UserData
{
var obj = GCHandle.FromIntPtr(ptr).Target;
var value = field.GetValue(obj);
return new EvalValue(value).GetPointer();
return EvalValue.CreateValue(value).GetPointer();
});
var setterPtr = IntPtr.Zero;
@ -73,7 +82,7 @@ namespace PorygonSharp.UserData
{
var obj = GCHandle.FromIntPtr(ptr).Target;
var value = property.GetValue(obj);
return new EvalValue(value).GetPointer();
return EvalValue.CreateValue(value).GetPointer();
});
getterPtr = Marshal.GetFunctionPointerForDelegate(getter);
}
@ -95,6 +104,41 @@ namespace PorygonSharp.UserData
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)
{
return UserDataLookup[t];

View File

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

View File

@ -15,6 +15,16 @@ namespace PorygonSharpTests
public int Bar { get; set; }
public int GetOnly { get; } = 865;
public readonly int ReadOnly = 684;
public int TestFunc()
{
return 345435;
}
public int Add(int a, int b)
{
return a + b;
}
}
[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.