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; return str;
} }
public object EvaluateGenericObject() private object EvaluateGenericObject()
{ {
var ptr = EvaluateUserDataObj(_handle); var ptr = EvaluateUserDataObj(_handle);
return GCHandle.FromIntPtr(ptr).Target; return GCHandle.FromIntPtr(ptr).Target;

View File

@ -131,9 +131,11 @@ namespace PorygonSharp
public string GetBoundTreeString() public string GetBoundTreeString()
{ {
var length = GetTreeStringLength(_internalScriptHandle); var length = GetTreeStringLength(_internalScriptHandle);
var sb = new StringBuilder(length - 4); var sb = new StringBuilder(length);
GetTreeString(_internalScriptHandle, sb); GetTreeString(_internalScriptHandle, sb);
return sb.ToString(); var s = sb.ToString();
s = s.Substring(0, length);
return s;
} }
[DllImport("PorygonLang", EntryPoint = "CreateScript", CallingConvention = CallingConvention.Cdecl)] [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.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using PorygonSharp.EvalValues;
using PorygonSharp.Utilities; using PorygonSharp.Utilities;
namespace PorygonSharp.UserData namespace PorygonSharp.UserData
@ -43,6 +44,7 @@ namespace PorygonSharp.UserData
SetIsCastableFunc(id, Marshal.GetFunctionPointerForDelegate(_isCastableFunc)); SetIsCastableFunc(id, Marshal.GetFunctionPointerForDelegate(_isCastableFunc));
SetCastFunc(id, Marshal.GetFunctionPointerForDelegate(_castFunc)); SetCastFunc(id, Marshal.GetFunctionPointerForDelegate(_castFunc));
} }
internal void RegisterFields() internal void RegisterFields()
@ -79,6 +81,8 @@ namespace PorygonSharp.UserData
{ {
RegisterFunction(method); RegisterFunction(method);
} }
RegisterOperations();
} }
private void RegisterField(FieldInfo field) private void RegisterField(FieldInfo field)
@ -150,9 +154,9 @@ namespace PorygonSharp.UserData
var obj = GCHandle.FromIntPtr(objPtr).Target; var obj = GCHandle.FromIntPtr(objPtr).Target;
var objType = obj.GetType(); var objType = obj.GetType();
if (expectedType.IsAssignableFrom(objType)) if (expectedType.IsAssignableFrom(objType))
return objPtr; return EvalValueCreator.CreateValue(obj).GetPointer();
if (objType.IsAssignableFrom(expectedType)) if (objType.IsAssignableFrom(expectedType))
return objPtr; return EvalValueCreator.CreateValue(obj).GetPointer();
if (_implicitCasts.TryGetValue(expectedType, out var func)) if (_implicitCasts.TryGetValue(expectedType, out var func))
{ {
var castVal = func.Invoke(null, new[] {obj}); var castVal = func.Invoke(null, new[] {obj});
@ -168,6 +172,54 @@ namespace PorygonSharp.UserData
throw new Exception("Invalid cast kind"); 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)] [DllImport("PorygonLang", EntryPoint = "RegisterUserDataField", CallingConvention = CallingConvention.Cdecl)]
internal static extern void RegisterUserDataField(uint hashId, uint fieldId, IntPtr field); 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)] [DllImport("PorygonLang", EntryPoint = "SetCastFunc", CallingConvention = CallingConvention.Cdecl)]
private static extern void SetCastFunc(uint id, IntPtr castFunc); 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 eval = new EvalValue(parameters[i]);
var val = eval.GetObjectValue(); var val = eval.GetObjectValue();
var desiredType = parameterTypes[i]; 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); var convertedType = Convert.ChangeType(val, desiredType);
evaluatedParameters[i] = convertedType; evaluatedParameters[i] = convertedType;

Binary file not shown.

View File

@ -27,6 +27,18 @@ namespace PorygonSharpTests
{ {
return a + b; 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] [Test]
@ -311,5 +323,53 @@ end
Assert.AreEqual(347807, result.EvaluateInteger()); 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);
}
}
} }
} }