using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Threading; using MoonSharp.Interpreter.DataStructs; using MoonSharp.Interpreter.Interop; namespace MoonSharp.Interpreter { /// /// Class exposing C# objects as Lua userdata. /// For efficiency, a global registry of types is maintained, instead of a per-script one. /// public class UserData : RefIdObject { private UserData() { // This type can only be instantiated using one of the Create methods } /// /// Gets or sets the "uservalue". See debug.getuservalue and debug.setuservalue. /// http://www.lua.org/manual/5.2/manual.html#pdf-debug.setuservalue /// public DynValue UserValue { get; set; } /// /// Gets the object associated to this userdata (null for statics) /// public object Object { get; private set; } /// /// Gets the type descriptor of this userdata /// public IUserDataDescriptor Descriptor { get; private set; } private static object s_Lock = new object(); private static Dictionary s_Registry = new Dictionary(); private static InteropAccessMode s_DefaultAccessMode; private static MultiDictionary s_ExtensionMethodRegistry = new MultiDictionary(); private static int s_ExtensionMethodChangeVersion = 0; static UserData() { RegisterType(InteropAccessMode.HideMembers); RegisterType(InteropAccessMode.HideMembers); s_DefaultAccessMode = InteropAccessMode.LazyOptimized; } /// /// Registers a type for userdata interop /// /// The type to be registered /// The access mode (optional). /// Friendly name for the type (optional) public static void RegisterType(InteropAccessMode accessMode = InteropAccessMode.Default, string friendlyName = null) { RegisterType_Impl(typeof(T), accessMode, friendlyName, null); } /// /// Registers a type for userdata interop /// /// The type to be registered /// The access mode (optional). /// Friendly name for the type (optional) public static void RegisterType(Type type, InteropAccessMode accessMode = InteropAccessMode.Default, string friendlyName = null) { RegisterType_Impl(type, accessMode, friendlyName, null); } /// /// Registers a type with a custom userdata descriptor /// /// The type to be registered /// The custom descriptor. public static void RegisterType(IUserDataDescriptor customDescriptor) { RegisterType_Impl(typeof(T), InteropAccessMode.Default, null, customDescriptor); } /// /// Registers a type with a custom userdata descriptor /// /// The type to be registered /// The custom descriptor. public static void RegisterType(Type type, IUserDataDescriptor customDescriptor) { RegisterType_Impl(type, InteropAccessMode.Default, null, customDescriptor); } /// /// Registers all types marked with a MoonSharpUserDataAttribute that ar contained in an assembly. /// /// The assembly. /// if set to true extension types are registered to the appropriate registry. public static void RegisterAssembly(Assembly asm = null, bool includeExtensionTypes = false) { asm = asm ?? Assembly.GetCallingAssembly(); if (includeExtensionTypes) { var extensionTypes = from t in asm.GetTypes() let attributes = t.GetCustomAttributes(typeof(ExtensionAttribute), true) where attributes != null && attributes.Length > 0 select new { Attributes = attributes, DataType = t }; foreach (var extType in extensionTypes) { UserData.RegisterExtensionType(extType.DataType); } } var userDataTypes = from t in asm.GetTypes() let attributes = t.GetCustomAttributes(typeof(MoonSharpUserDataAttribute), true) where attributes != null && attributes.Length > 0 select new { Attributes = attributes, DataType = t }; foreach (var userDataType in userDataTypes) { UserData.RegisterType(userDataType.DataType, userDataType.Attributes .OfType() .First() .AccessMode); } } /// /// Unregisters a type /// /// The type to be unregistered public static void UnregisterType() { UnregisterType(typeof(T)); } /// /// Unregisters a type /// /// The The type to be unregistered public static void UnregisterType(Type t) { lock (s_Lock) { if (s_Registry.ContainsKey(t)) s_Registry.Remove(t); } } /// /// Creates a userdata DynValue from the specified object /// /// The object /// public static DynValue Create(object o) { var descr = GetDescriptorForObject(o); if (descr == null) return null; return DynValue.NewUserData(new UserData() { Descriptor = descr, Object = o }); } /// /// Creates a static userdata DynValue from the specified Type /// /// The type /// public static DynValue CreateStatic(Type t) { var descr = GetDescriptorForType(t, false); if (descr == null) return null; return DynValue.NewUserData(new UserData() { Descriptor = descr, Object = null }); } /// /// Creates a static userdata DynValue from the specified Type /// /// The Type /// public static DynValue CreateStatic() { return CreateStatic(typeof(T)); } /// /// Gets or sets the registration policy to be used in the whole application /// public static InteropRegistrationPolicy RegistrationPolicy { get; set; } /// /// Gets or sets the default access mode to be used in the whole application /// /// /// The default access mode. /// /// InteropAccessMode is InteropAccessMode.Default public static InteropAccessMode DefaultAccessMode { get { return s_DefaultAccessMode; } set { if (value == InteropAccessMode.Default) throw new ArgumentException("InteropAccessMode is InteropAccessMode.Default"); s_DefaultAccessMode = value; } } /// /// Registers an extension Type (that is a type containing extension methods) /// /// The type. /// The InteropAccessMode. public static void RegisterExtensionType(Type type, InteropAccessMode mode = InteropAccessMode.Default) { lock (s_Lock) { foreach (MethodInfo mi in type.GetMethods().Where(_mi => _mi.IsStatic)) { if (!StandardUserDataMethodDescriptor.CheckMethodIsCompatible(mi, false)) continue; if (mi.GetCustomAttributes(typeof(ExtensionAttribute), false).Length == 0) continue; var desc = new StandardUserDataMethodDescriptor(mi, mode); s_ExtensionMethodRegistry.Add(mi.Name, desc); ++s_ExtensionMethodChangeVersion; } } } /// /// Gets all the extension methods which can match a given name /// /// The name. /// public static IEnumerable GetExtensionMethodsByName(string name) { lock (s_Lock) return s_ExtensionMethodRegistry.Find(name); } /// /// Gets a number which gets incremented everytime the extension methods registry changes. /// Use this to invalidate caches based on extension methods /// /// public static int GetExtensionMethodsChangeVersion() { return s_ExtensionMethodChangeVersion; } private static IUserDataDescriptor RegisterType_Impl(Type type, InteropAccessMode accessMode, string friendlyName, IUserDataDescriptor descriptor) { if (accessMode == InteropAccessMode.Default) { MoonSharpUserDataAttribute attr = type.GetCustomAttributes(true).OfType() .SingleOrDefault(); if (attr != null) accessMode = attr.AccessMode; } if (accessMode == InteropAccessMode.Default) accessMode = s_DefaultAccessMode; lock (s_Lock) { if (!s_Registry.ContainsKey(type)) { if (descriptor == null) { if (type.GetInterfaces().Any(ii => ii == typeof(IUserDataType))) { AutoDescribingUserDataDescriptor audd = new AutoDescribingUserDataDescriptor(type, friendlyName); s_Registry.Add(type, audd); return audd; } else { StandardUserDataDescriptor udd = new StandardUserDataDescriptor(type, accessMode, friendlyName); s_Registry.Add(type, udd); if (accessMode == InteropAccessMode.BackgroundOptimized) { ThreadPool.QueueUserWorkItem(o => udd.Optimize()); } return udd; } } else { s_Registry.Add(type, descriptor); return descriptor; } } else return s_Registry[type]; } } private static IUserDataDescriptor GetDescriptorForType(bool searchInterfaces) { return GetDescriptorForType(typeof(T), searchInterfaces); } private static IUserDataDescriptor GetDescriptorForType(Type type, bool searchInterfaces) { lock (s_Lock) { IUserDataDescriptor typeDescriptor = null; // if the type has been explicitly registered, return its descriptor as it's complete if (s_Registry.ContainsKey(type)) return s_Registry[type]; if (RegistrationPolicy == InteropRegistrationPolicy.Automatic) { return RegisterType_Impl(type, DefaultAccessMode, type.FullName, null); } // search for the base object descriptors for (Type t = type; t != null; t = t.BaseType) { IUserDataDescriptor u; if (s_Registry.TryGetValue(t, out u)) { typeDescriptor = u; break; } } // we should not search interfaces (for example, it's just for statics..), no need to look further if (!searchInterfaces) return typeDescriptor; List descriptors = new List(); if (typeDescriptor != null) descriptors.Add(typeDescriptor); if (searchInterfaces) { foreach (Type t in type.GetInterfaces()) { IUserDataDescriptor u; if (s_Registry.TryGetValue(t, out u)) descriptors.Add(u); } } if (descriptors.Count == 1) return descriptors[0]; else if (descriptors.Count == 0) return null; else return new CompositeUserDataDescriptor(descriptors, type); } } private static IUserDataDescriptor GetDescriptorForObject(object o) { return GetDescriptorForType(o.GetType(), true); } } }