Added support for UserData
This commit is contained in:
parent
d8bf2c2994
commit
4a91629f98
|
@ -17,5 +17,10 @@ namespace PorygonSharp.DiagnosticHandling
|
|||
ExpressionIsNotAFunction,
|
||||
ParameterCountMismatch,
|
||||
ParameterTypeMismatch,
|
||||
CantIndex,
|
||||
InvalidReturnType,
|
||||
ConditionNotABool,
|
||||
InvalidTableValueType,
|
||||
InvalidTypeName,
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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 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.
Loading…
Reference in New Issue