Added support for UserData

This commit is contained in:
Deukhoofd 2019-06-14 22:22:42 +02:00
parent d8bf2c2994
commit 4a91629f98
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
8 changed files with 226 additions and 2 deletions

View File

@ -17,5 +17,10 @@ namespace PorygonSharp.DiagnosticHandling
ExpressionIsNotAFunction,
ParameterCountMismatch,
ParameterTypeMismatch,
CantIndex,
InvalidReturnType,
ConditionNotABool,
InvalidTableValueType,
InvalidTypeName,
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
using PorygonSharp.UserData;
namespace PorygonSharp
{
@ -40,10 +41,14 @@ namespace PorygonSharp
case TypeCode.Single:
_handle = CreateFloatEvalValue(Convert.ToDouble(o));
break;
case TypeCode.Object:
var typeHash = UserDataHandler.GetTypeId(type);
var handle = GCHandle.Alloc(o, GCHandleType.WeakTrackResurrection);
_handle = CreateUserDataEvalValue(typeHash, GCHandle.ToIntPtr(handle));
break;
case TypeCode.DateTime:
case TypeCode.DBNull:
case TypeCode.Empty:
case TypeCode.Object:
default:
throw new Exception($"Type {type} is not currently available as EvalValue");
}
@ -84,6 +89,27 @@ namespace PorygonSharp
return Marshal.PtrToStringUTF8(ptr);
}
public object GetObjectValue()
{
switch (GetTypeClass())
{
case TypeClass.Nil:
return null;
case TypeClass.Number:
return EvaluateInteger();
case TypeClass.Bool:
return EvaluateBool();
case TypeClass.String:
return EvaluateString();
case TypeClass.Function:
case TypeClass.UserData:
case TypeClass.Table:
case TypeClass.Error:
default:
throw new ArgumentOutOfRangeException();
}
}
internal IntPtr GetPointer()
{
return _handle;
@ -110,5 +136,7 @@ namespace PorygonSharp
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);
}
}

View File

@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using PorygonSharp.DiagnosticHandling;
@ -13,9 +15,17 @@ namespace PorygonSharp
private readonly IntPtr _evaluator;
private readonly IntPtr _scriptVariables;
private readonly IntPtr _boundScript;
private readonly SharedPointer _returnValue;
internal readonly IntPtr Diagnostics;
}
[StructLayout(LayoutKind.Sequential)]
internal struct SharedPointer
{
private IntPtr _pointer;
private int _references;
}
public class Script : IDisposable
{
private readonly IntPtr _internalScriptHandle;
@ -27,7 +37,21 @@ namespace PorygonSharp
public Script(string s)
{
_internalScriptHandle = Create(new StringBuilder(s));
_internalScript = Marshal.PtrToStructure<InternalScript>(_internalScriptHandle);
_internalScript = Marshal.PtrToStructure<InternalScript>(_internalScriptHandle);
}
[HandleProcessCorruptedStateExceptions]
[SecurityCritical]
public static Script CreateScript(string s)
{
try
{
return new Script(s);
}
catch
{
throw new Exception("This");
}
}
public void Dispose()

View File

@ -0,0 +1,46 @@
using System;
using System.Runtime.InteropServices;
namespace PorygonSharp.ScriptType
{
internal static class ScriptType
{
internal static IntPtr GetScriptType(Type t)
{
var typeCode = Type.GetTypeCode(t);
switch (typeCode)
{
case TypeCode.Boolean:
return CreateScriptType(TypeClass.Bool);
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 CreateNumericScriptType(true, false);
case TypeCode.Decimal:
case TypeCode.Double:
case TypeCode.Single:
return CreateNumericScriptType(true, true);
case TypeCode.Char:
case TypeCode.String:
return CreateStringScriptType(false, 0);
default:
throw new ArgumentOutOfRangeException();
}
}
[DllImport("libPorygonLang", EntryPoint = "CreateScriptType", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateScriptType(TypeClass t);
[DllImport("libPorygonLang", EntryPoint = "CreateNumericScriptType", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateNumericScriptType(bool isAware, bool isFloat);
[DllImport("libPorygonLang", EntryPoint = "CreateStringScriptType", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateStringScriptType(bool knownAtBind, uint hash);
}
}

View File

@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using PorygonSharp.Utilities;
namespace PorygonSharp.UserData
{
public static class UserDataHandler
{
private static readonly Dictionary<Type, uint> UserDataLookup = new Dictionary<Type, uint>();
private delegate IntPtr GetterDelegate(IntPtr ptr);
private delegate void SetterDelegate(IntPtr ptr, IntPtr val);
public static void RegisterType(string name, Type type)
{
var hash = name.ScriptHash();
RegisterUserDataType(hash);
UserDataLookup.Add(type, hash);
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
foreach (var field in fields)
{
var getter = new GetterDelegate(ptr =>
{
var obj = GCHandle.FromIntPtr(ptr).Target;
var value = field.GetValue(obj);
return new EvalValue(value).GetPointer();
});
var setter = new SetterDelegate((ptr, val) =>
{
var evalValue = new EvalValue(val).GetObjectValue();
var obj = GCHandle.FromIntPtr(ptr).Target;
field.SetValue(obj, evalValue);
});
var scriptType = ScriptType.ScriptType.GetScriptType(field.FieldType);
var userDataField = CreateUserDataField(scriptType, Marshal.GetFunctionPointerForDelegate(getter),
Marshal.GetFunctionPointerForDelegate(setter));
var fieldName = field.Name.ScriptHash();
RegisterUserDataField(hash, fieldName, userDataField);
}
}
public static uint GetTypeId(Type t)
{
return UserDataLookup[t];
}
[DllImport("libPorygonLang", EntryPoint = "RegisterUserDataType", CallingConvention = CallingConvention.Cdecl)]
private static extern void RegisterUserDataType(uint hashId);
[DllImport("libPorygonLang", EntryPoint = "RegisterUserDataField", CallingConvention = CallingConvention.Cdecl)]
private static extern void RegisterUserDataField(uint hashId, uint fieldId, IntPtr field);
[DllImport("libPorygonLang", EntryPoint = "CreateUserDataField", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateUserDataField(IntPtr type, IntPtr getter, IntPtr setter);
[DllImport("libPorygonLang", EntryPoint = "GetUserDataFieldCount", CallingConvention = CallingConvention.Cdecl)]
private static extern int GetUserDataFieldCount(uint hashId);
}
}

View File

@ -0,0 +1,18 @@
namespace PorygonSharp.Utilities
{
public static class HashedString
{
public static uint ScriptHash(this string input)
{
uint h = 5381;
for (var index = input.Length - 1; index >= 0; index--)
{
var c = input[index];
h *= 33;
h += c;
}
return h;
}
}
}

View File

@ -1,6 +1,8 @@
using System;
using NUnit.Framework;
using PorygonSharp;
using PorygonSharp.UserData;
using PorygonSharp.Utilities;
namespace PorygonSharpTests
{
@ -107,5 +109,41 @@ namespace PorygonSharpTests
}
}
private class UserDataTestObject
{
public int Foo = 200;
}
[Test]
public void Test9()
{
UserDataHandler.RegisterType("testObject", typeof(UserDataTestObject));
using (var script = Script.CreateScript(@"
function test(testObject v)
result = v['Foo']
end
"))
{
var diags = script.Diagnostics.GetDiagnostics();
foreach (var diag in diags)
{
throw new Exception(diag.GetCode().ToString());
}
script.Evaluate();
script.CallFunction("test", new UserDataTestObject());
var variable = script.GetVariable("result");
Assert.AreEqual(200, variable.EvaluateInteger());
}
}
[Test]
public void TestHash()
{
var hash = HashedString.ScriptHash("Foo");
Assert.AreEqual(193501609, hash);
}
}
}

Binary file not shown.