Added support for UserData
This commit is contained in:
		| @@ -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() | ||||
|   | ||||
							
								
								
									
										46
									
								
								PorygonSharp/ScriptType/ScriptType.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								PorygonSharp/ScriptType/ScriptType.cs
									
									
									
									
									
										Normal 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); | ||||
|  | ||||
|     } | ||||
| } | ||||
							
								
								
									
										65
									
								
								PorygonSharp/UserData/UserDataHandler.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								PorygonSharp/UserData/UserDataHandler.cs
									
									
									
									
									
										Normal 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); | ||||
|  | ||||
|     } | ||||
| } | ||||
							
								
								
									
										18
									
								
								PorygonSharp/Utilities/HashedString.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								PorygonSharp/Utilities/HashedString.cs
									
									
									
									
									
										Normal 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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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.
										
									
								
							
		Reference in New Issue
	
	Block a user