diff --git a/PorygonSharp/DiagnosticHandling/DiagnosticCode.cs b/PorygonSharp/DiagnosticHandling/DiagnosticCode.cs index 37f5dd4..7bfb46d 100644 --- a/PorygonSharp/DiagnosticHandling/DiagnosticCode.cs +++ b/PorygonSharp/DiagnosticHandling/DiagnosticCode.cs @@ -17,5 +17,10 @@ namespace PorygonSharp.DiagnosticHandling ExpressionIsNotAFunction, ParameterCountMismatch, ParameterTypeMismatch, + CantIndex, + InvalidReturnType, + ConditionNotABool, + InvalidTableValueType, + InvalidTypeName, } } \ No newline at end of file diff --git a/PorygonSharp/EvalValue.cs b/PorygonSharp/EvalValue.cs index 2742c8d..24d40cd 100644 --- a/PorygonSharp/EvalValue.cs +++ b/PorygonSharp/EvalValue.cs @@ -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); } } \ No newline at end of file diff --git a/PorygonSharp/Script.cs b/PorygonSharp/Script.cs index 223399b..22418f7 100644 --- a/PorygonSharp/Script.cs +++ b/PorygonSharp/Script.cs @@ -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(_internalScriptHandle); + _internalScript = Marshal.PtrToStructure(_internalScriptHandle); + } + + [HandleProcessCorruptedStateExceptions] + [SecurityCritical] + public static Script CreateScript(string s) + { + try + { + return new Script(s); + } + catch + { + throw new Exception("This"); + } } public void Dispose() diff --git a/PorygonSharp/ScriptType/ScriptType.cs b/PorygonSharp/ScriptType/ScriptType.cs new file mode 100644 index 0000000..9fbfe05 --- /dev/null +++ b/PorygonSharp/ScriptType/ScriptType.cs @@ -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); + + } +} \ No newline at end of file diff --git a/PorygonSharp/UserData/UserDataHandler.cs b/PorygonSharp/UserData/UserDataHandler.cs new file mode 100644 index 0000000..7a17121 --- /dev/null +++ b/PorygonSharp/UserData/UserDataHandler.cs @@ -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 UserDataLookup = new Dictionary(); + + 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); + + } +} \ No newline at end of file diff --git a/PorygonSharp/Utilities/HashedString.cs b/PorygonSharp/Utilities/HashedString.cs new file mode 100644 index 0000000..ce87bd1 --- /dev/null +++ b/PorygonSharp/Utilities/HashedString.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/PorygonSharpTests/UnitTest1.cs b/PorygonSharpTests/UnitTest1.cs index 4535bbf..7a8f20e 100644 --- a/PorygonSharpTests/UnitTest1.cs +++ b/PorygonSharpTests/UnitTest1.cs @@ -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); + } + } } \ No newline at end of file diff --git a/libPorygonLang.so b/libPorygonLang.so index e2fa29f..202e97d 100755 Binary files a/libPorygonLang.so and b/libPorygonLang.so differ