Support for calling userdata functions
This commit is contained in:
parent
0ff2815694
commit
f23f682d52
|
@ -13,15 +13,14 @@ namespace PorygonSharp
|
|||
_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);
|
||||
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;
|
||||
var handle = GCHandle.Alloc(o, GCHandleType.WeakTrackResurrection);
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
|
|
|
@ -2,7 +2,6 @@ using System;
|
|||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using PorygonSharp;
|
||||
using PorygonSharp.UserData;
|
||||
using PorygonSharp.Utilities;
|
||||
|
||||
namespace PorygonSharpTests
|
||||
|
|
|
@ -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.
Loading…
Reference in New Issue