diff --git a/PorygonSharp/UserData/UserDataHandler.cs b/PorygonSharp/UserData/UserDataHandler.cs index 7ca5f9d..0452d47 100644 --- a/PorygonSharp/UserData/UserDataHandler.cs +++ b/PorygonSharp/UserData/UserDataHandler.cs @@ -17,31 +17,82 @@ namespace PorygonSharp.UserData { var hash = name.ScriptHash(); RegisterUserDataType(hash); + if (UserDataLookup.ContainsKey(type)) + return; UserDataLookup.Add(type, hash); - var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); + const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | + BindingFlags.FlattenHierarchy | BindingFlags.Static; + var fields = type.GetFields(bindingFlags); 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(); - }); + RegisterField(field, hash); + } + + var properties = type.GetProperties(bindingFlags); + foreach (var property in properties) + { + RegisterProperty(property, hash); + } + } + + private static void RegisterField(FieldInfo field, uint typeHash) + { + var getter = new GetterDelegate(ptr => + { + var obj = GCHandle.FromIntPtr(ptr).Target; + var value = field.GetValue(obj); + return new EvalValue(value).GetPointer(); + }); + + var setterPtr = IntPtr.Zero; + if (!field.IsInitOnly) + { var setter = new SetterDelegate((ptr, val) => { - var obj = GCHandle.FromIntPtr(ptr).Target; + var obj = GCHandle.FromIntPtr(ptr).Target; var evalValue = Convert.ChangeType(new EvalValue(val).GetObjectValue(), field.FieldType); 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); + setterPtr = Marshal.GetFunctionPointerForDelegate(setter); } + var scriptType = ScriptType.ScriptType.GetScriptType(field.FieldType); + var userDataField = CreateUserDataField(scriptType, Marshal.GetFunctionPointerForDelegate(getter), + setterPtr); + + var fieldName = field.Name.ScriptHash(); + RegisterUserDataField(typeHash, fieldName, userDataField); + } + + private static void RegisterProperty(PropertyInfo property, uint typeHash) + { + var getterPtr = IntPtr.Zero; + if (property.GetGetMethod(false) != null) + { + var getter = new GetterDelegate(ptr => + { + var obj = GCHandle.FromIntPtr(ptr).Target; + var value = property.GetValue(obj); + return new EvalValue(value).GetPointer(); + }); + getterPtr = Marshal.GetFunctionPointerForDelegate(getter); + } + var setterPtr = IntPtr.Zero; + if (property.GetSetMethod(false) != null) + { + var setter = new SetterDelegate((ptr, val) => + { + var obj = GCHandle.FromIntPtr(ptr).Target; + var evalValue = Convert.ChangeType(new EvalValue(val).GetObjectValue(), property.PropertyType); + property.SetValue(obj, evalValue); + }); + setterPtr = Marshal.GetFunctionPointerForDelegate(setter); + } + var scriptType = ScriptType.ScriptType.GetScriptType(property.PropertyType); + var userDataField = CreateUserDataField(scriptType, getterPtr, setterPtr); + + var fieldName = property.Name.ScriptHash(); + RegisterUserDataField(typeHash, fieldName, userDataField); } public static uint GetTypeId(Type t) @@ -49,6 +100,12 @@ namespace PorygonSharp.UserData return UserDataLookup[t]; } + public static int GetUserDataFieldCount(Type t) + { + var hash = GetTypeId(t); + return GetUserDataFieldCount(hash); + } + [DllImport("libPorygonLang", EntryPoint = "RegisterUserDataType", CallingConvention = CallingConvention.Cdecl)] private static extern void RegisterUserDataType(uint hashId); diff --git a/PorygonSharpTests/UnitTest1.cs b/PorygonSharpTests/UnitTest1.cs index deba67a..e96056a 100644 --- a/PorygonSharpTests/UnitTest1.cs +++ b/PorygonSharpTests/UnitTest1.cs @@ -109,59 +109,6 @@ 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 Test10() - { - UserDataHandler.RegisterType("testObject", typeof(UserDataTestObject)); - using (var script = Script.CreateScript(@" -function test(testObject v) - v['Foo'] = 20000 -end -")) - { - var diags = script.Diagnostics.GetDiagnostics(); - foreach (var diag in diags) - { - throw new Exception(diag.GetCode().ToString()); - } - - script.Evaluate(); - - var parameter = new UserDataTestObject(); - script.CallFunction("test", parameter); - Assert.AreEqual(20000, parameter.Foo); - } - } - [Test] public void TestHash() { diff --git a/PorygonSharpTests/UserDataTests.cs b/PorygonSharpTests/UserDataTests.cs new file mode 100644 index 0000000..734c293 --- /dev/null +++ b/PorygonSharpTests/UserDataTests.cs @@ -0,0 +1,150 @@ +using System; +using System.Linq; +using NUnit.Framework; +using PorygonSharp; +using PorygonSharp.UserData; + +namespace PorygonSharpTests +{ + [TestFixture] + public class UserDataTests + { + private class UserDataTestObject + { + public int Foo = 200; + public int Bar { get; set; } + public int GetOnly { get; } = 865; + public readonly int ReadOnly = 684; + } + + [Test] + public void CanGetFromUserDataField() + { + 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 CanSetToUserDataField() + { + UserDataHandler.RegisterType("testObject", typeof(UserDataTestObject)); + using (var script = Script.CreateScript(@" +function test(testObject v) + v['Foo'] = 20000 +end +")) + { + var diags = script.Diagnostics.GetDiagnostics(); + foreach (var diag in diags) + { + throw new Exception(diag.GetCode().ToString()); + } + + script.Evaluate(); + + var parameter = new UserDataTestObject(); + script.CallFunction("test", parameter); + Assert.AreEqual(20000, parameter.Foo); + } + } + + [Test] + public void CanGetFromPropertyWithoutSetter() + { + UserDataHandler.RegisterType("testObject", typeof(UserDataTestObject)); + using (var script = Script.CreateScript(@" +function test(testObject v) + result = v['GetOnly'] +end +")) + { + var diags = script.Diagnostics.GetDiagnostics(); + foreach (var diag in diags) + { + throw new Exception(diag.GetCode().ToString()); + } + + script.Evaluate(); + + var parameter = new UserDataTestObject(); + script.CallFunction("test", parameter); + Assert.AreEqual(865, script.GetVariable("result").EvaluateInteger()); + } + } + + [Test] + public void CantSetToPropertyWithoutSetter() + { + UserDataHandler.RegisterType("testObject", typeof(UserDataTestObject)); + using (var script = Script.CreateScript(@" +function test(testObject v) + v['GetOnly'] = 10000 +end +")) + { + var diags = script.Diagnostics.GetDiagnostics().ToArray(); + Assert.IsNotEmpty(diags); + Assert.AreEqual(1, diags.Length); + } + } + + + [Test] + public void CanGetFromReadonlyField() + { + UserDataHandler.RegisterType("testObject", typeof(UserDataTestObject)); + using (var script = Script.CreateScript(@" +function test(testObject v) + result = v['ReadOnly'] +end +")) + { + var diags = script.Diagnostics.GetDiagnostics(); + foreach (var diag in diags) + { + throw new Exception(diag.GetCode().ToString()); + } + + script.Evaluate(); + + var parameter = new UserDataTestObject(); + script.CallFunction("test", parameter); + Assert.AreEqual(684, script.GetVariable("result").EvaluateInteger()); + } + } + + [Test] + public void CantSetToReadonlyField() + { + UserDataHandler.RegisterType("testObject", typeof(UserDataTestObject)); + using (var script = Script.CreateScript(@" +function test(testObject v) + v['ReadOnly'] = 10000 +end +")) + { + var diags = script.Diagnostics.GetDiagnostics().ToArray(); + Assert.IsNotEmpty(diags); + Assert.AreEqual(1, diags.Length); + } + } + + } +} \ No newline at end of file diff --git a/libPorygonLang.so b/libPorygonLang.so index 202e97d..fc7093d 100755 Binary files a/libPorygonLang.so and b/libPorygonLang.so differ