Fixes support for newer version, added support for userdata collections

This commit is contained in:
Deukhoofd 2019-08-15 16:56:20 +02:00
parent 4a42254b62
commit d2cc82dc21
Signed by: Deukhoofd
GPG Key ID: ADF2E9256009EDCE
13 changed files with 327 additions and 79 deletions

View File

@ -27,7 +27,7 @@ namespace PorygonSharp
Print(message);
}
[DllImport("libPorygonLang", EntryPoint = "SetPrintFunc", CallingConvention = CallingConvention.Cdecl)]
[DllImport("libPorygonLang", EntryPoint = "SetDefaultPrintFunc", CallingConvention = CallingConvention.Cdecl)]
private static extern double InternalSetPrintFunc(IntPtr ptr);
}
}

View File

@ -1,9 +1,8 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
using PorygonSharp.UserData;
namespace PorygonSharp
namespace PorygonSharp.EvalValues
{
public class EvalValue : IDisposable
{
@ -14,48 +13,7 @@ namespace PorygonSharp
_handle = handle;
}
public static EvalValue CreateValue(object o)
{
var type = o.GetType();
var typeCode = Type.GetTypeCode(type);
switch (typeCode)
{
case TypeCode.Boolean:
return new EvalValue(CreateBoolEvalValue((bool)o));
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
return new EvalValue(CreateIntegerEvalValue(Convert.ToInt64(o)));
case TypeCode.Char:
case TypeCode.String:
return new EvalValue(CreateStringEvalValue(o.ToString()));
case TypeCode.Decimal:
case TypeCode.Double:
case TypeCode.Single:
return new EvalValue(CreateFloatEvalValue(Convert.ToDouble(o)));
case TypeCode.Object:
var typeHash = UserDataHandler.GetTypeId(type);
var handle = GCHandle.Alloc(o, GCHandleType.WeakTrackResurrection);
return new EvalValue(CreateUserDataEvalValue(typeHash, GCHandle.ToIntPtr(handle)));
case TypeCode.DateTime:
case TypeCode.DBNull:
case TypeCode.Empty:
default:
throw new Exception($"Type {type} is not currently available as EvalValue");
}
}
public static EvalValue FunctionEvalValue(IntPtr func, IntPtr parent)
{
return new EvalValue(CreateFunctionEvalValue(func, parent));
}
public void Dispose()
{
if (_handle != IntPtr.Zero)
@ -119,9 +77,6 @@ namespace PorygonSharp
return EvaluateString();
case TypeClass.UserData:
return EvaluateGenericObject();
case TypeClass.Function:
case TypeClass.Table:
case TypeClass.Error:
default:
throw new ArgumentOutOfRangeException();
}
@ -150,19 +105,5 @@ namespace PorygonSharp
[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);
[DllImport("libPorygonLang", EntryPoint = "CreateFloatEvalValue",CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateFloatEvalValue(double d);
[DllImport("libPorygonLang", EntryPoint = "CreateBoolEvalValue",CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateBoolEvalValue(bool b);
[DllImport("libPorygonLang", EntryPoint = "CreateStringEvalValue",CallingConvention = CallingConvention.Cdecl)]
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

@ -0,0 +1,77 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using PorygonSharp.UserData;
namespace PorygonSharp.EvalValues
{
public static class EvalValueCreator
{
public static EvalValue CreateValue(object o)
{
var type = o.GetType();
var typeCode = Type.GetTypeCode(type);
switch (typeCode)
{
case TypeCode.Boolean:
return new EvalValue(CreateBoolEvalValue((bool)o));
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
return new EvalValue(CreateIntegerEvalValue(Convert.ToInt64(o)));
case TypeCode.Char:
case TypeCode.String:
return new EvalValue(CreateStringEvalValue(o.ToString()));
case TypeCode.Decimal:
case TypeCode.Double:
case TypeCode.Single:
return new EvalValue(CreateFloatEvalValue(Convert.ToDouble(o)));
case TypeCode.Object:
if (typeof(IList).IsAssignableFrom(type))
return CreateListEvalValue((IList)o, type);
var typeHash = UserDataHandler.GetTypeId(type);
var handle = GCHandle.Alloc(o, GCHandleType.WeakTrackResurrection);
return new EvalValue(CreateUserDataEvalValue(typeHash, GCHandle.ToIntPtr(handle)));
default:
throw new Exception($"Type {type} is not currently available as EvalValue");
}
}
public static EvalValue FunctionEvalValue(IntPtr func, IntPtr parent)
{
return new EvalValue(CreateFunctionEvalValue(func, parent));
}
private static EvalValue CreateListEvalValue(IList collection, Type type)
{
var helper = new ListEvalValue(collection, type);
var ptr = CreateCollectionValue(IntPtr.Zero, helper.ParentCollection, helper.Getter, helper.Setter,
helper.GetIterator);
return new EvalValue(ptr);
}
[DllImport("libPorygonLang", EntryPoint = "CreateIntegerEvalValue",CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateIntegerEvalValue(long l);
[DllImport("libPorygonLang", EntryPoint = "CreateFloatEvalValue",CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateFloatEvalValue(double d);
[DllImport("libPorygonLang", EntryPoint = "CreateBoolEvalValue",CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateBoolEvalValue(bool b);
[DllImport("libPorygonLang", EntryPoint = "CreateStringEvalValue",CallingConvention = CallingConvention.Cdecl)]
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);
[DllImport("libPorygonLang", EntryPoint = "CreateCollectionValue", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateCollectionValue(IntPtr type, IntPtr parent, IntPtr getter, IntPtr setter,
IntPtr iterator);
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.Collections;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace PorygonSharp.EvalValues
{
internal class ListEvalValue
{
private delegate IntPtr GetterDelegate(IntPtr parent, IntPtr index);
private delegate void SetterDelegate(IntPtr parent, IntPtr index, IntPtr value);
public delegate IntPtr GetIteratorDelegate(IntPtr parent);
public readonly IntPtr ParentCollection;
public readonly IntPtr Getter;
public readonly IntPtr Setter;
public readonly IntPtr GetIterator;
public ListEvalValue(IList list, Type type)
{
var valueType = type.IsArray ? type.GetElementType() : type.GetGenericArguments()[0];
if (valueType == null)
throw new ArgumentNullException();
var parentHandle = GCHandle.Alloc(list, GCHandleType.WeakTrackResurrection);
ParentCollection = GCHandle.ToIntPtr(parentHandle);
Getter = Marshal.GetFunctionPointerForDelegate(new GetterDelegate((parent, index) =>
{
var val = new EvalValue(index).EvaluateInteger();
return EvalValueCreator.CreateValue(list[(int) val - 1]).GetPointer();
}));
Setter = Marshal.GetFunctionPointerForDelegate(new SetterDelegate((parent, index, value) =>
{
var key = new EvalValue(index).EvaluateInteger();
var val = new EvalValue(value).GetObjectValue();
list[(int) key - 1] = Convert.ChangeType(val, valueType);
}));
GetIterator =
Marshal.GetFunctionPointerForDelegate(new GetIteratorDelegate(parent =>
ListIterator.CreateListIterator(list)));
}
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections;
using System.Runtime.InteropServices;
namespace PorygonSharp.EvalValues
{
public static class ListIterator
{
public static IntPtr CreateListIterator(IList list)
{
return CreateCollectionRangeIterator(0, list.Count);
}
[DllImport("libPorygonLang", EntryPoint = "CreateCollectionRangeIterator", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateCollectionRangeIterator(int start, int end);
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
using PorygonSharp.EvalValues;
namespace PorygonSharp
{
@ -9,7 +10,7 @@ namespace PorygonSharp
private struct EvaluateResultInternal
{
public readonly IntPtr Value;
public readonly byte Result;
public readonly bool Result;
}
private readonly IntPtr _ptr;
@ -22,12 +23,12 @@ namespace PorygonSharp
public EvalValue GetValue()
{
return !IsSuccess() ? null : new EvalValue(_internal.Value);
return new EvalValue(_internal.Value);
}
public bool IsSuccess()
{
return _internal.Result == 0;
return !_internal.Result;
}
public string GetError()

View File

@ -5,4 +5,10 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<None Update="libPorygonLang.so">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -1,10 +1,9 @@
using System;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Security;
using PorygonSharp.DiagnosticHandling;
using PorygonSharp.EvalValues;
namespace PorygonSharp
{
@ -13,8 +12,10 @@ namespace PorygonSharp
{
private readonly IntPtr _evaluator;
private readonly IntPtr _scriptVariables;
private readonly IntPtr _scriptTypes;
private readonly SharedPointer<object> _boundScript;
private readonly SharedPointer<object> _returnType;
private readonly IntPtr _scriptOptions;
internal readonly SharedPointer<Diagnostics> Diagnostics;
}
@ -43,12 +44,12 @@ namespace PorygonSharp
private Diagnostics _diagnostics;
public Diagnostics Diagnostics => _diagnostics ?? (_diagnostics = new Diagnostics(_internalScript.Diagnostics));
private static readonly RuntimeTypeHandle _setupHandle = typeof(CoreSetup).TypeHandle;
private static readonly RuntimeTypeHandle SetupHandle = typeof(CoreSetup).TypeHandle;
public Script(string s)
{
// Ensure core setup has been called
RuntimeHelpers.RunClassConstructor(_setupHandle);
RuntimeHelpers.RunClassConstructor(SetupHandle);
_internalScriptHandle = Create(s);
_internalScript = Marshal.PtrToStructure<InternalScript>(_internalScriptHandle);
@ -90,9 +91,20 @@ namespace PorygonSharp
public EvalValue CallFunction(string key, params object[] parameters)
{
var scriptParameters = parameters.Select(x => EvalValue.CreateValue(x).GetPointer()).ToArray();
var scriptParameters = parameters.Select(x => EvalValueCreator.CreateValue(x).GetPointer()).ToArray();
var ptr = CallFunction(_internalScriptHandle, key, scriptParameters, scriptParameters.Length);
return new EvalValue(ptr);
foreach (var parameter in scriptParameters)
{
Marshal.FreeHGlobal(parameter);
}
using (var result = new EvaluateResult(ptr))
{
if (result.IsSuccess())
{
return result.GetValue();
}
throw new EvaluationException(result.GetError());
}
}
[DllImport("libPorygonLang", EntryPoint = "CreateScript", CallingConvention = CallingConvention.Cdecl)]
@ -106,8 +118,10 @@ namespace PorygonSharp
[DllImport("libPorygonLang", EntryPoint = "HasFunction", CallingConvention = CallingConvention.Cdecl)]
private static extern bool HasFunction(IntPtr script, [MarshalAs(UnmanagedType.LPWStr)]string key);
[DllImport("libPorygonLang", EntryPoint = "CallFunction", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CallFunction(IntPtr script, [MarshalAs(UnmanagedType.LPWStr)]string key, IntPtr[] parameters, int parameterCount);
private static extern IntPtr CallFunction(IntPtr script, [MarshalAs(UnmanagedType.LPWStr)]string key,
IntPtr[] parameters, int parameterCount);
}
}

View File

@ -1,6 +1,9 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using PorygonSharp.UserData;
namespace PorygonSharp.ScriptType
{
@ -34,6 +37,18 @@ namespace PorygonSharp.ScriptType
{
return CreateScriptType(TypeClass.Nil);
}
if (UserDataHandler.IsTypeRegistered(t))
{
return UserDataHandler.CreateUserDataType(t);
}
if (typeof(IList).IsAssignableFrom(t))
{
return CreateUserDataListType(t);
}
if (typeof(IDictionary<,>).IsAssignableFrom(t))
{
return CreateUserDataDictionaryType(t);
}
goto default;
default:
throw new ArgumentOutOfRangeException(t.FullName);
@ -61,6 +76,21 @@ namespace PorygonSharp.ScriptType
}
}
private static IntPtr CreateUserDataListType(Type t)
{
var keyType = CreateNumericScriptType(true, false);
var valType = t.IsArray ? t.GetElementType() : t.GenericTypeArguments[0];
var valueType = GetScriptType(valType);
return CreateCollectionType(keyType, valueType);
}
private static IntPtr CreateUserDataDictionaryType(Type t)
{
var keyType = GetScriptType(t.GenericTypeArguments[0]);
var valueType = GetScriptType(t.GenericTypeArguments[1]);
return CreateCollectionType(keyType, valueType);
}
[DllImport("libPorygonLang", EntryPoint = "CreateScriptType", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateScriptType(TypeClass t);
@ -72,5 +102,8 @@ namespace PorygonSharp.ScriptType
[DllImport("libPorygonLang", EntryPoint = "CreateUserDataFunctionScriptType", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateUserDataFunctionScriptType(IntPtr returnType, IntPtr[] parameters, int parameterCount);
[DllImport("libPorygonLang", EntryPoint = "CreateCollectionType", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateCollectionType(IntPtr keyType, IntPtr valueType);
}
}

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
@ -13,8 +14,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);
private delegate IntPtr CallerDelegate(IntPtr parent, IntPtr scriptOption,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)]IntPtr[] parameters, int size);
public static void RegisterType(string name, Type type)
{
@ -51,7 +52,7 @@ namespace PorygonSharp.UserData
{
var obj = GCHandle.FromIntPtr(ptr).Target;
var value = field.GetValue(obj);
return EvalValue.CreateValue(value).GetPointer();
return EvalValueCreator.CreateValue(value).GetPointer();
});
var setterPtr = IntPtr.Zero;
@ -82,7 +83,7 @@ namespace PorygonSharp.UserData
{
var obj = GCHandle.FromIntPtr(ptr).Target;
var value = property.GetValue(obj);
return EvalValue.CreateValue(value).GetPointer();
return EvalValueCreator.CreateValue(value).GetPointer();
});
getterPtr = Marshal.GetFunctionPointerForDelegate(getter);
}
@ -110,7 +111,7 @@ namespace PorygonSharp.UserData
var getter = new GetterDelegate(ptr =>
{
var func = new CallerDelegate((parent, parameters, size) =>
var func = new CallerDelegate((parent, scriptOptions, parameters, size) =>
{
var evaluatedParameters = new object[size];
@ -124,10 +125,10 @@ namespace PorygonSharp.UserData
var parentObj = GCHandle.FromIntPtr(parent).Target;
var result = method.Invoke(parentObj, evaluatedParameters);
return EvalValue.CreateValue(result).GetPointer();
return EvalValueCreator.CreateValue(result).GetPointer();
});
var funcPtr = Marshal.GetFunctionPointerForDelegate(func);
return EvalValue.FunctionEvalValue(funcPtr, ptr).GetPointer();
return EvalValueCreator.FunctionEvalValue(funcPtr, ptr).GetPointer();
});
var getterPtr = Marshal.GetFunctionPointerForDelegate(getter);
var type = ScriptType.ScriptType.GetFunctionScriptType(method);
@ -144,12 +145,23 @@ namespace PorygonSharp.UserData
return UserDataLookup[t];
}
public static bool IsTypeRegistered(Type t)
{
return UserDataLookup.ContainsKey(t);
}
public static int GetUserDataFieldCount(Type t)
{
var hash = GetTypeId(t);
return GetUserDataFieldCount(hash);
}
internal static IntPtr CreateUserDataType(Type t)
{
var hash = GetTypeId(t);
return CreateUserDataType(hash);
}
[DllImport("libPorygonLang", EntryPoint = "RegisterUserDataType", CallingConvention = CallingConvention.Cdecl)]
private static extern void RegisterUserDataType(uint hashId);
@ -161,6 +173,9 @@ namespace PorygonSharp.UserData
[DllImport("libPorygonLang", EntryPoint = "GetUserDataFieldCount", CallingConvention = CallingConvention.Cdecl)]
private static extern int GetUserDataFieldCount(uint hashId);
[DllImport("libPorygonLang", EntryPoint = "CreateUserDataType", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateUserDataType(uint hashId);
}
}

BIN
PorygonSharp/libPorygonLang.so Executable file

Binary file not shown.

View File

@ -16,6 +16,8 @@ namespace PorygonSharpTests
public int GetOnly { get; } = 865;
public readonly int ReadOnly = 684;
public int[] IntArray = {55, 60, 846, 346846};
public int TestFunc()
{
return 345435;
@ -210,5 +212,104 @@ end
}
}
[Test]
public void CanGetFromArray()
{
UserDataHandler.RegisterType("testObject", typeof(UserDataTestObject));
using (var script = new Script(@"
function test(testObject v)
return v.IntArray[3]
end
"))
{
var diags = script.Diagnostics.GetDiagnostics();
foreach (var diag in diags)
{
throw new Exception(script.Diagnostics.GetFullDiagnosticMessage(diag));
}
script.Evaluate();
var parameter = new UserDataTestObject();
var result = script.CallFunction("test", parameter);
Assert.AreEqual(846, result.EvaluateInteger());
}
}
[Test]
public void CanSetToArray()
{
UserDataHandler.RegisterType("testObject", typeof(UserDataTestObject));
using (var script = new Script(@"
function test(testObject v)
v.IntArray[2] = 256
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);
Assert.AreEqual(256, parameter.IntArray[1]);
}
}
[Test]
public void CanIterateOverArrayKeys()
{
UserDataHandler.RegisterType("testObject", typeof(UserDataTestObject));
using (var script = new Script(@"
function test(testObject v)
value = 0
for i in v.IntArray do
value = value + i
end
return value
end
"))
{
var diags = script.Diagnostics.GetDiagnostics();
foreach (var diag in diags)
{
throw new Exception(script.Diagnostics.GetFullDiagnosticMessage(diag));
}
script.Evaluate();
var parameter = new UserDataTestObject();
var result = script.CallFunction("test", parameter);
Assert.AreEqual(10, result.EvaluateInteger());
}
}
[Test]
public void CanIterateOverArrayValues()
{
UserDataHandler.RegisterType("testObject", typeof(UserDataTestObject));
using (var script = new Script(@"
function test(testObject v)
value = 0
for i, j in v.IntArray do
value = value + j
end
return value
end
"))
{
var diags = script.Diagnostics.GetDiagnostics();
foreach (var diag in diags)
{
throw new Exception(script.Diagnostics.GetFullDiagnosticMessage(diag));
}
script.Evaluate();
var parameter = new UserDataTestObject();
var result = script.CallFunction("test", parameter);
Assert.AreEqual(347807, result.EvaluateInteger());
}
}
}
}

Binary file not shown.