Support for setting binary operations

This commit is contained in:
Deukhoofd 2019-09-12 15:21:32 +02:00
parent 8c3db0373b
commit ae2080f145
Signed by: Deukhoofd
GPG Key ID: ADF2E9256009EDCE
8 changed files with 212 additions and 6 deletions

View File

@ -57,7 +57,7 @@ namespace PorygonSharp.EvalValues
return str;
}
public object EvaluateGenericObject()
private object EvaluateGenericObject()
{
var ptr = EvaluateUserDataObj(_handle);
return GCHandle.FromIntPtr(ptr).Target;

View File

@ -131,9 +131,11 @@ namespace PorygonSharp
public string GetBoundTreeString()
{
var length = GetTreeStringLength(_internalScriptHandle);
var sb = new StringBuilder(length - 4);
var sb = new StringBuilder(length);
GetTreeString(_internalScriptHandle, sb);
return sb.ToString();
var s = sb.ToString();
s = s.Substring(0, length);
return s;
}
[DllImport("PorygonLang", EntryPoint = "CreateScript", CallingConvention = CallingConvention.Cdecl)]

View File

@ -0,0 +1,23 @@
namespace PorygonSharp.UserData
{
internal enum BinaryOperationKind : byte
{
// Math
Addition,
Subtraction,
Multiplication,
Division,
// Equality
Equality,
Inequality,
Less,
LessOrEquals,
Greater,
GreaterOrEquals,
// Logical
LogicalAnd,
LogicalOr,
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using PorygonSharp.EvalValues;
using PorygonSharp.Utilities;
namespace PorygonSharp.UserData
@ -43,6 +44,7 @@ namespace PorygonSharp.UserData
SetIsCastableFunc(id, Marshal.GetFunctionPointerForDelegate(_isCastableFunc));
SetCastFunc(id, Marshal.GetFunctionPointerForDelegate(_castFunc));
}
internal void RegisterFields()
@ -79,6 +81,8 @@ namespace PorygonSharp.UserData
{
RegisterFunction(method);
}
RegisterOperations();
}
private void RegisterField(FieldInfo field)
@ -150,9 +154,9 @@ namespace PorygonSharp.UserData
var obj = GCHandle.FromIntPtr(objPtr).Target;
var objType = obj.GetType();
if (expectedType.IsAssignableFrom(objType))
return objPtr;
return EvalValueCreator.CreateValue(obj).GetPointer();
if (objType.IsAssignableFrom(expectedType))
return objPtr;
return EvalValueCreator.CreateValue(obj).GetPointer();
if (_implicitCasts.TryGetValue(expectedType, out var func))
{
var castVal = func.Invoke(null, new[] {obj});
@ -168,6 +172,54 @@ namespace PorygonSharp.UserData
throw new Exception("Invalid cast kind");
}
private void RegisterOperations()
{
var methods = Type.GetMethods(BindingFlags.Public | BindingFlags.Static);
foreach (var method in methods)
{
switch (method.Name)
{
case "op_Addition":
RegisterOperation(BinaryOperationKind.Addition, method);
break;
case "op_Subtraction":
RegisterOperation(BinaryOperationKind.Subtraction, method);
break;
case "op_Multiply":
RegisterOperation(BinaryOperationKind.Multiplication, method);
break;
case "op_Division":
RegisterOperation(BinaryOperationKind.Division, method);
break;
case "op_Equality":
RegisterOperation(BinaryOperationKind.Equality, method);
break;
case "op_Inequality":
RegisterOperation(BinaryOperationKind.Inequality, method);
break;
case "op_LessThan":
RegisterOperation(BinaryOperationKind.Less, method);
break;
case "op_GreaterThan":
RegisterOperation(BinaryOperationKind.Greater, method);
break;
case "op_LessThanOrEqual":
RegisterOperation(BinaryOperationKind.LessOrEquals, method);
break;
case "op_GreaterThanOrEqual":
RegisterOperation(BinaryOperationKind.GreaterOrEquals, method);
break;
default: continue;
}
}
}
private void RegisterOperation(BinaryOperationKind kind, MethodInfo info)
{
var operation = UserDataBinaryOperation.Create(info);
AddUserdataBinaryOperation(Id, (byte)kind, operation.Handle);
}
[DllImport("PorygonLang", EntryPoint = "RegisterUserDataField", CallingConvention = CallingConvention.Cdecl)]
internal static extern void RegisterUserDataField(uint hashId, uint fieldId, IntPtr field);
@ -179,6 +231,10 @@ namespace PorygonSharp.UserData
[DllImport("PorygonLang", EntryPoint = "SetCastFunc", CallingConvention = CallingConvention.Cdecl)]
private static extern void SetCastFunc(uint id, IntPtr castFunc);
[DllImport("PorygonLang", EntryPoint = "AddUserdataBinaryOperation", CallingConvention = CallingConvention.Cdecl)]
private static extern void AddUserdataBinaryOperation(uint id, byte kind, IntPtr operation);
}
}

View File

@ -0,0 +1,60 @@
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using PorygonSharp.EvalValues;
namespace PorygonSharp.UserData
{
public class UserDataBinaryOperation
{
private delegate IntPtr BinaryFunctionDelegate(IntPtr objectPtr, IntPtr variable);
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
private readonly BinaryFunctionDelegate _func;
private readonly MethodInfo _info;
private readonly Type _parameterType;
internal IntPtr Handle { get; private set; }
private UserDataBinaryOperation(MethodInfo info)
{
_info = info;
_func = Invoke;
_parameterType = info.GetParameters()[1].ParameterType;
}
public static UserDataBinaryOperation Create(MethodInfo info)
{
var op = new UserDataBinaryOperation(info);
var funcPtr = Marshal.GetFunctionPointerForDelegate(op._func);
var secParType = ScriptType.ScriptTypeHandler.GetScriptType(op._parameterType);
var returnType = ScriptType.ScriptTypeHandler.GetScriptType(op._info.ReturnType);
if (!secParType.HasValue || !returnType.HasValue)
{
return null;
}
op.Handle = CreateUserdataBinaryOperation(funcPtr, secParType.Value, returnType.Value);
return op;
}
private IntPtr Invoke(IntPtr objectPtr, IntPtr variable)
{
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 o = _info.Invoke(null, new[] {a, bVal});
return EvalValueCreator.CreateValue(o).GetPointer();
}
[DllImport("PorygonLang", EntryPoint = "CreateUserdataBinaryOperation", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateUserdataBinaryOperation(IntPtr func, IntPtr secondParameterType, IntPtr returnType);
}
}

View File

@ -72,7 +72,12 @@ namespace PorygonSharp.UserData
var eval = new EvalValue(parameters[i]);
var val = eval.GetObjectValue();
var desiredType = parameterTypes[i];
if (val is IConvertible)
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;

Binary file not shown.

View File

@ -27,6 +27,18 @@ namespace PorygonSharpTests
{
return a + b;
}
public static UserDataTestObject operator + (UserDataTestObject a, UserDataTestObject b)
{
a.Foo += b.Foo;
return a;
}
public static UserDataTestObject operator + (UserDataTestObject a, int b)
{
a.Foo += b;
return a;
}
}
[Test]
@ -311,5 +323,53 @@ end
Assert.AreEqual(347807, result.EvaluateInteger());
}
}
[Test]
public void CanUseBinaryOperations()
{
UserDataHandler.RegisterType("testObject", typeof(UserDataTestObject));
using (var script = new Script(@"
function test(testObject a, testObject b)
return a + b
end
"))
{
var diags = script.Diagnostics.GetDiagnostics();
foreach (var diag in diags)
{
throw new Exception(script.Diagnostics.GetFullDiagnosticMessage(diag));
}
script.Evaluate();
var a = new UserDataTestObject();
var b = new UserDataTestObject();
var result = script.CallFunction("test", a, b);
Assert.AreEqual(400, ((UserDataTestObject)result.GetObjectValue()).Foo);
}
}
[Test]
public void CanUseBinaryOperationsWithScriptTypes()
{
UserDataHandler.RegisterType("testObject", typeof(UserDataTestObject));
using (var script = new Script(@"
function test(testObject a)
return a + 500
end
"))
{
var diags = script.Diagnostics.GetDiagnostics();
foreach (var diag in diags)
{
throw new Exception(script.Diagnostics.GetFullDiagnosticMessage(diag));
}
script.Evaluate();
var a = new UserDataTestObject();
var result = script.CallFunction("test", a);
Assert.AreEqual(700, ((UserDataTestObject)result.GetObjectValue()).Foo);
}
}
}
}