Added support for UserData
This commit is contained in:
parent
d8bf2c2994
commit
4a91629f98
|
@ -17,5 +17,10 @@ namespace PorygonSharp.DiagnosticHandling
|
||||||
ExpressionIsNotAFunction,
|
ExpressionIsNotAFunction,
|
||||||
ParameterCountMismatch,
|
ParameterCountMismatch,
|
||||||
ParameterTypeMismatch,
|
ParameterTypeMismatch,
|
||||||
|
CantIndex,
|
||||||
|
InvalidReturnType,
|
||||||
|
ConditionNotABool,
|
||||||
|
InvalidTableValueType,
|
||||||
|
InvalidTypeName,
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using PorygonSharp.UserData;
|
||||||
|
|
||||||
namespace PorygonSharp
|
namespace PorygonSharp
|
||||||
{
|
{
|
||||||
|
@ -40,10 +41,14 @@ namespace PorygonSharp
|
||||||
case TypeCode.Single:
|
case TypeCode.Single:
|
||||||
_handle = CreateFloatEvalValue(Convert.ToDouble(o));
|
_handle = CreateFloatEvalValue(Convert.ToDouble(o));
|
||||||
break;
|
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.DateTime:
|
||||||
case TypeCode.DBNull:
|
case TypeCode.DBNull:
|
||||||
case TypeCode.Empty:
|
case TypeCode.Empty:
|
||||||
case TypeCode.Object:
|
|
||||||
default:
|
default:
|
||||||
throw new Exception($"Type {type} is not currently available as EvalValue");
|
throw new Exception($"Type {type} is not currently available as EvalValue");
|
||||||
}
|
}
|
||||||
|
@ -84,6 +89,27 @@ namespace PorygonSharp
|
||||||
return Marshal.PtrToStringUTF8(ptr);
|
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()
|
internal IntPtr GetPointer()
|
||||||
{
|
{
|
||||||
return _handle;
|
return _handle;
|
||||||
|
@ -110,5 +136,7 @@ namespace PorygonSharp
|
||||||
private static extern IntPtr CreateBoolEvalValue(bool b);
|
private static extern IntPtr CreateBoolEvalValue(bool b);
|
||||||
[DllImport("libPorygonLang", EntryPoint = "CreateStringEvalValue",CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("libPorygonLang", EntryPoint = "CreateStringEvalValue",CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern IntPtr CreateStringEvalValue(string s);
|
private static extern IntPtr CreateStringEvalValue(string s);
|
||||||
|
[DllImport("libPorygonLang", EntryPoint = "CreateUserDataEvalValue",CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern IntPtr CreateUserDataEvalValue(uint typeHash, IntPtr obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using PorygonSharp.DiagnosticHandling;
|
using PorygonSharp.DiagnosticHandling;
|
||||||
|
|
||||||
|
@ -13,9 +15,17 @@ namespace PorygonSharp
|
||||||
private readonly IntPtr _evaluator;
|
private readonly IntPtr _evaluator;
|
||||||
private readonly IntPtr _scriptVariables;
|
private readonly IntPtr _scriptVariables;
|
||||||
private readonly IntPtr _boundScript;
|
private readonly IntPtr _boundScript;
|
||||||
|
private readonly SharedPointer _returnValue;
|
||||||
internal readonly IntPtr Diagnostics;
|
internal readonly IntPtr Diagnostics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct SharedPointer
|
||||||
|
{
|
||||||
|
private IntPtr _pointer;
|
||||||
|
private int _references;
|
||||||
|
}
|
||||||
|
|
||||||
public class Script : IDisposable
|
public class Script : IDisposable
|
||||||
{
|
{
|
||||||
private readonly IntPtr _internalScriptHandle;
|
private readonly IntPtr _internalScriptHandle;
|
||||||
|
@ -27,7 +37,21 @@ namespace PorygonSharp
|
||||||
public Script(string s)
|
public Script(string s)
|
||||||
{
|
{
|
||||||
_internalScriptHandle = Create(new StringBuilder(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()
|
public void Dispose()
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using PorygonSharp;
|
using PorygonSharp;
|
||||||
|
using PorygonSharp.UserData;
|
||||||
|
using PorygonSharp.Utilities;
|
||||||
|
|
||||||
namespace PorygonSharpTests
|
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.
Loading…
Reference in New Issue