diff --git a/PorygonSharp/UserData/UserData.cs b/PorygonSharp/UserData/UserData.cs new file mode 100644 index 0000000..e7415cc --- /dev/null +++ b/PorygonSharp/UserData/UserData.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace PorygonSharp.UserData +{ + internal class UserData + { + public readonly Dictionary Fields = new Dictionary(); + public uint Id { get; } + + public UserData(uint id) + { + Id = id; + } + } +} \ No newline at end of file diff --git a/PorygonSharp/UserData/UserDataField.cs b/PorygonSharp/UserData/UserDataField.cs new file mode 100644 index 0000000..cbb7716 --- /dev/null +++ b/PorygonSharp/UserData/UserDataField.cs @@ -0,0 +1,103 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using PorygonSharp.EvalValues; + +namespace PorygonSharp.UserData +{ + internal class UserDataField + { + internal delegate IntPtr GetterDelegate(IntPtr ptr); + internal delegate void SetterDelegate(IntPtr ptr, IntPtr val); + private delegate IntPtr CallerDelegate(IntPtr parent, IntPtr scriptOption, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)]IntPtr[] parameters, int size); + + private readonly GetterDelegate _getter; + private readonly SetterDelegate _setter; + + public UserDataField(FieldInfo field) + { + _getter = ptr => + { + var obj = GCHandle.FromIntPtr(ptr).Target; + var value = field.GetValue(obj); + return EvalValueCreator.CreateValue(value).GetPointer(); + }; + + if (!field.IsInitOnly) + { + _setter = (ptr, val) => + { + var obj = GCHandle.FromIntPtr(ptr).Target; + var evalValue = Convert.ChangeType(new EvalValue(val).GetObjectValue(), field.FieldType); + field.SetValue(obj, evalValue); + }; + } + } + + public UserDataField(PropertyInfo property) + { + if (property.GetGetMethod(false) != null) + { + _getter = ptr => + { + var obj = GCHandle.FromIntPtr(ptr).Target; + var value = property.GetValue(obj); + return EvalValueCreator.CreateValue(value).GetPointer(); + }; + } + if (property.GetSetMethod(false) != null) + { + _setter = (ptr, val) => + { + var obj = GCHandle.FromIntPtr(ptr).Target; + var evalValue = Convert.ChangeType(new EvalValue(val).GetObjectValue(), property.PropertyType); + property.SetValue(obj, evalValue); + }; + } + } + + public UserDataField(MethodInfo method) + { + var parameterTypes = method.GetParameters().Select(x => x.ParameterType).ToArray(); + _getter = ptr => + { + var func = new CallerDelegate((parent, scriptOptions, parameters, size) => + { + + var evaluatedParameters = new object[size]; + for (var i = 0; i < size; i++) + { + var eval = new EvalValue(parameters[i]); + var val = eval.GetObjectValue(); + var convertedType = Convert.ChangeType(val, parameterTypes[i]); + evaluatedParameters[i] = convertedType; + } + + var parentObj = GCHandle.FromIntPtr(parent).Target; + var result = method.Invoke(parentObj, evaluatedParameters); + return EvalValueCreator.CreateValue(result).GetPointer(); + }); + var funcPtr = Marshal.GetFunctionPointerForDelegate(func); + return EvalValueCreator.FunctionEvalValue(funcPtr, ptr).GetPointer(); + }; + } + + public UserDataField(GetterDelegate getter, SetterDelegate setter) + { + _getter = getter; + _setter = setter; + } + + internal IntPtr GetGetterPointer() + { + return _getter == null ? IntPtr.Zero : Marshal.GetFunctionPointerForDelegate(_getter); + } + + internal IntPtr GetSetterPointer() + { + return _setter == null ? IntPtr.Zero : Marshal.GetFunctionPointerForDelegate(_setter); + } + } +} \ No newline at end of file diff --git a/PorygonSharp/UserData/UserDataHandler.cs b/PorygonSharp/UserData/UserDataHandler.cs index 9e0e2c8..6c87462 100644 --- a/PorygonSharp/UserData/UserDataHandler.cs +++ b/PorygonSharp/UserData/UserDataHandler.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -11,12 +12,9 @@ namespace PorygonSharp.UserData { public static class UserDataHandler { - private static readonly Dictionary UserDataLookup = new Dictionary(); + private static readonly ConcurrentDictionary UserDataLookup = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary ReverseLookup = new ConcurrentDictionary(); - private delegate IntPtr GetterDelegate(IntPtr ptr); - private delegate void SetterDelegate(IntPtr ptr, IntPtr val); - private delegate IntPtr CallerDelegate(IntPtr parent, IntPtr scriptOption, - [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)]IntPtr[] parameters, int size); public static void RegisterAssembly(Assembly assembly) { @@ -45,7 +43,9 @@ namespace PorygonSharp.UserData if (UserDataLookup.ContainsKey(type)) return; RegisterUserDataType(hash); - UserDataLookup.Add(type, hash); + var ud = new UserData(hash); + UserDataLookup.TryAdd(type, ud); + ReverseLookup.TryAdd(hash, ud); } const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | @@ -72,129 +72,93 @@ namespace PorygonSharp.UserData public static void RegisterEnumType(string name, Type type) { var hash = name.ScriptHash(); - if (UserDataLookup.ContainsKey(type)) - return; - RegisterUserDataType(hash); - UserDataLookup.Add(type, hash); + lock (UserDataLookup) + { + if (UserDataLookup.ContainsKey(type)) + return; + RegisterUserDataType(hash); + var ud = new UserData(hash); + UserDataLookup.TryAdd(type, ud); + ReverseLookup.TryAdd(hash, ud); + } var values = Enum.GetValues(type); foreach (var value in values) { - var getter = new GetterDelegate(ptr => EvalValueCreator.CreateIntegerEvalValue(Convert.ToInt64(value))); + var getter = new UserDataField.GetterDelegate(ptr => EvalValueCreator.CreateIntegerEvalValue(Convert.ToInt64(value))); + var fieldData = new UserDataField(getter, null); var valueName = Enum.GetName(type, value); var fieldName = valueName.ScriptHash(); var t = ScriptType.ScriptType.CreateNumericScriptType(true, false); RegisterUserDataField(hash, fieldName, CreateUserDataField(t, Marshal.GetFunctionPointerForDelegate(getter), IntPtr.Zero)); + ReverseLookup[hash].Fields[fieldName] = fieldData; } } 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 EvalValueCreator.CreateValue(value).GetPointer(); - }); - - var setterPtr = IntPtr.Zero; - if (!field.IsInitOnly) - { - var setter = new SetterDelegate((ptr, val) => - { - var obj = GCHandle.FromIntPtr(ptr).Target; - var evalValue = Convert.ChangeType(new EvalValue(val).GetObjectValue(), field.FieldType); - field.SetValue(obj, evalValue); - }); - setterPtr = Marshal.GetFunctionPointerForDelegate(setter); - } var scriptType = ScriptType.ScriptType.GetScriptType(field.FieldType); if (!scriptType.HasValue) return; - var userDataField = CreateUserDataField(scriptType.Value, Marshal.GetFunctionPointerForDelegate(getter), - setterPtr); + var fieldData = new UserDataField(field); + var userDataField = CreateUserDataField(scriptType.Value, fieldData.GetGetterPointer(), + fieldData.GetSetterPointer()); var fieldName = field.Name.ScriptHash(); RegisterUserDataField(typeHash, fieldName, userDataField); + ReverseLookup[typeHash].Fields[fieldName] = fieldData; } 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 EvalValueCreator.CreateValue(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); if (!scriptType.HasValue) return; - var userDataField = CreateUserDataField(scriptType.Value, getterPtr, setterPtr); + var fieldData = new UserDataField(property); + var userDataField = CreateUserDataField(scriptType.Value, fieldData.GetGetterPointer(), + fieldData.GetSetterPointer()); var fieldName = property.Name.ScriptHash(); RegisterUserDataField(typeHash, fieldName, userDataField); + ReverseLookup[typeHash].Fields[fieldName] = fieldData; } private static void RegisterFunction(MethodInfo method, uint typeHash) { - var parameterTypes = method.GetParameters().Select(x => x.ParameterType).ToArray(); - var getter = new GetterDelegate(ptr => - { - var func = new CallerDelegate((parent, scriptOptions, parameters, size) => - { - - var evaluatedParameters = new object[size]; - for (var i = 0; i < size; i++) - { - var eval = new EvalValue(parameters[i]); - var val = eval.GetObjectValue(); - var convertedType = Convert.ChangeType(val, parameterTypes[i]); - evaluatedParameters[i] = convertedType; - } - - var parentObj = GCHandle.FromIntPtr(parent).Target; - var result = method.Invoke(parentObj, evaluatedParameters); - return EvalValueCreator.CreateValue(result).GetPointer(); - }); - var funcPtr = Marshal.GetFunctionPointerForDelegate(func); - return EvalValueCreator.FunctionEvalValue(funcPtr, ptr).GetPointer(); - }); - var getterPtr = Marshal.GetFunctionPointerForDelegate(getter); var type = ScriptType.ScriptType.GetFunctionScriptType(method); if (type == IntPtr.Zero || !type.HasValue) return; - var userDataField = CreateUserDataField(type.Value, getterPtr, IntPtr.Zero); + var fieldData = new UserDataField(method); + var userDataField = CreateUserDataField(type.Value, fieldData.GetGetterPointer(), IntPtr.Zero); var fieldName = method.Name.ScriptHash(); RegisterUserDataField(typeHash, fieldName, userDataField); + ReverseLookup[typeHash].Fields[fieldName] = fieldData; } public static uint GetTypeId(Type t) { - return UserDataLookup[t]; + if (UserDataLookup.TryGetValue(t, out var i)) + return i.Id; + var attr = t.GetCustomAttribute(); + if (attr == null) + return 0; + var id = attr.Identifier.ScriptHash(); + if (ReverseLookup.ContainsKey(id)) + return id; + return 0; } public static bool IsTypeRegistered(Type t) { - return UserDataLookup.ContainsKey(t); + if (UserDataLookup.ContainsKey(t)) return true; + var attr = t.GetCustomAttribute(); + if (attr == null) + return false; + var id = attr.Identifier.ScriptHash(); + return ReverseLookup.ContainsKey(id); } public static int GetUserDataFieldCount(Type t) diff --git a/PorygonSharp/libPorygonLang.so b/PorygonSharp/libPorygonLang.so index 5dde80c..4100788 100755 Binary files a/PorygonSharp/libPorygonLang.so and b/PorygonSharp/libPorygonLang.so differ