UserData.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Runtime.CompilerServices;
  6. using System.Text;
  7. using System.Threading;
  8. using MoonSharp.Interpreter.DataStructs;
  9. using MoonSharp.Interpreter.Interop;
  10. namespace MoonSharp.Interpreter
  11. {
  12. /// <summary>
  13. /// Class exposing C# objects as Lua userdata.
  14. /// For efficiency, a global registry of types is maintained, instead of a per-script one.
  15. /// </summary>
  16. public class UserData : RefIdObject
  17. {
  18. private UserData()
  19. {
  20. // This type can only be instantiated using one of the Create methods
  21. }
  22. /// <summary>
  23. /// Gets or sets the "uservalue". See debug.getuservalue and debug.setuservalue.
  24. /// http://www.lua.org/manual/5.2/manual.html#pdf-debug.setuservalue
  25. /// </summary>
  26. public DynValue UserValue { get; set; }
  27. /// <summary>
  28. /// Gets the object associated to this userdata (null for statics)
  29. /// </summary>
  30. public object Object { get; private set; }
  31. /// <summary>
  32. /// Gets the type descriptor of this userdata
  33. /// </summary>
  34. public IUserDataDescriptor Descriptor { get; private set; }
  35. private static object s_Lock = new object();
  36. private static Dictionary<Type, IUserDataDescriptor> s_Registry = new Dictionary<Type, IUserDataDescriptor>();
  37. private static InteropAccessMode s_DefaultAccessMode;
  38. private static MultiDictionary<string, StandardUserDataMethodDescriptor> s_ExtensionMethodRegistry = new MultiDictionary<string, StandardUserDataMethodDescriptor>();
  39. private static int s_ExtensionMethodChangeVersion = 0;
  40. static UserData()
  41. {
  42. RegisterType<AnonWrapper>(InteropAccessMode.HideMembers);
  43. RegisterType<EnumerableWrapper>(InteropAccessMode.HideMembers);
  44. s_DefaultAccessMode = InteropAccessMode.LazyOptimized;
  45. }
  46. /// <summary>
  47. /// Registers a type for userdata interop
  48. /// </summary>
  49. /// <typeparam name="T">The type to be registered</typeparam>
  50. /// <param name="accessMode">The access mode (optional).</param>
  51. /// <param name="friendlyName">Friendly name for the type (optional)</param>
  52. public static void RegisterType<T>(InteropAccessMode accessMode = InteropAccessMode.Default, string friendlyName = null)
  53. {
  54. RegisterType_Impl(typeof(T), accessMode, friendlyName, null);
  55. }
  56. /// <summary>
  57. /// Registers a type for userdata interop
  58. /// </summary>
  59. /// <param name="type">The type to be registered</param>
  60. /// <param name="accessMode">The access mode (optional).</param>
  61. /// <param name="friendlyName">Friendly name for the type (optional)</param>
  62. public static void RegisterType(Type type, InteropAccessMode accessMode = InteropAccessMode.Default, string friendlyName = null)
  63. {
  64. RegisterType_Impl(type, accessMode, friendlyName, null);
  65. }
  66. /// <summary>
  67. /// Registers a type with a custom userdata descriptor
  68. /// </summary>
  69. /// <typeparam name="T">The type to be registered</typeparam>
  70. /// <param name="customDescriptor">The custom descriptor.</param>
  71. public static void RegisterType<T>(IUserDataDescriptor customDescriptor)
  72. {
  73. RegisterType_Impl(typeof(T), InteropAccessMode.Default, null, customDescriptor);
  74. }
  75. /// <summary>
  76. /// Registers a type with a custom userdata descriptor
  77. /// </summary>
  78. /// <param name="type">The type to be registered</param>
  79. /// <param name="customDescriptor">The custom descriptor.</param>
  80. public static void RegisterType(Type type, IUserDataDescriptor customDescriptor)
  81. {
  82. RegisterType_Impl(type, InteropAccessMode.Default, null, customDescriptor);
  83. }
  84. /// <summary>
  85. /// Registers all types marked with a MoonSharpUserDataAttribute that ar contained in an assembly.
  86. /// </summary>
  87. /// <param name="asm">The assembly.</param>
  88. /// <param name="includeExtensionTypes">if set to <c>true</c> extension types are registered to the appropriate registry.</param>
  89. public static void RegisterAssembly(Assembly asm = null, bool includeExtensionTypes = false)
  90. {
  91. asm = asm ?? Assembly.GetCallingAssembly();
  92. if (includeExtensionTypes)
  93. {
  94. var extensionTypes = from t in asm.GetTypes()
  95. let attributes = t.GetCustomAttributes(typeof(ExtensionAttribute), true)
  96. where attributes != null && attributes.Length > 0
  97. select new { Attributes = attributes, DataType = t };
  98. foreach (var extType in extensionTypes)
  99. {
  100. UserData.RegisterExtensionType(extType.DataType);
  101. }
  102. }
  103. var userDataTypes = from t in asm.GetTypes()
  104. let attributes = t.GetCustomAttributes(typeof(MoonSharpUserDataAttribute), true)
  105. where attributes != null && attributes.Length > 0
  106. select new { Attributes = attributes, DataType = t };
  107. foreach (var userDataType in userDataTypes)
  108. {
  109. UserData.RegisterType(userDataType.DataType, userDataType.Attributes
  110. .OfType<MoonSharpUserDataAttribute>()
  111. .First()
  112. .AccessMode);
  113. }
  114. }
  115. /// <summary>
  116. /// Unregisters a type
  117. /// </summary>
  118. /// <typeparam name="T">The type to be unregistered</typeparam>
  119. public static void UnregisterType<T>()
  120. {
  121. UnregisterType(typeof(T));
  122. }
  123. /// <summary>
  124. /// Unregisters a type
  125. /// </summary>
  126. /// <param name="t">The The type to be unregistered</param>
  127. public static void UnregisterType(Type t)
  128. {
  129. lock (s_Lock)
  130. {
  131. if (s_Registry.ContainsKey(t))
  132. s_Registry.Remove(t);
  133. }
  134. }
  135. /// <summary>
  136. /// Creates a userdata DynValue from the specified object
  137. /// </summary>
  138. /// <param name="o">The object</param>
  139. /// <returns></returns>
  140. public static DynValue Create(object o)
  141. {
  142. var descr = GetDescriptorForObject(o);
  143. if (descr == null) return null;
  144. return DynValue.NewUserData(new UserData()
  145. {
  146. Descriptor = descr,
  147. Object = o
  148. });
  149. }
  150. /// <summary>
  151. /// Creates a static userdata DynValue from the specified Type
  152. /// </summary>
  153. /// <param name="t">The type</param>
  154. /// <returns></returns>
  155. public static DynValue CreateStatic(Type t)
  156. {
  157. var descr = GetDescriptorForType(t, false);
  158. if (descr == null) return null;
  159. return DynValue.NewUserData(new UserData()
  160. {
  161. Descriptor = descr,
  162. Object = null
  163. });
  164. }
  165. /// <summary>
  166. /// Creates a static userdata DynValue from the specified Type
  167. /// </summary>
  168. /// <typeparam name="T">The Type</typeparam>
  169. /// <returns></returns>
  170. public static DynValue CreateStatic<T>()
  171. {
  172. return CreateStatic(typeof(T));
  173. }
  174. /// <summary>
  175. /// Gets or sets the registration policy to be used in the whole application
  176. /// </summary>
  177. public static InteropRegistrationPolicy RegistrationPolicy { get; set; }
  178. /// <summary>
  179. /// Gets or sets the default access mode to be used in the whole application
  180. /// </summary>
  181. /// <value>
  182. /// The default access mode.
  183. /// </value>
  184. /// <exception cref="System.ArgumentException">InteropAccessMode is InteropAccessMode.Default</exception>
  185. public static InteropAccessMode DefaultAccessMode
  186. {
  187. get { return s_DefaultAccessMode; }
  188. set
  189. {
  190. if (value == InteropAccessMode.Default)
  191. throw new ArgumentException("InteropAccessMode is InteropAccessMode.Default");
  192. s_DefaultAccessMode = value;
  193. }
  194. }
  195. /// <summary>
  196. /// Registers an extension Type (that is a type containing extension methods)
  197. /// </summary>
  198. /// <param name="type">The type.</param>
  199. /// <param name="mode">The InteropAccessMode.</param>
  200. public static void RegisterExtensionType(Type type, InteropAccessMode mode = InteropAccessMode.Default)
  201. {
  202. lock (s_Lock)
  203. {
  204. foreach (MethodInfo mi in type.GetMethods().Where(_mi => _mi.IsStatic))
  205. {
  206. if (!StandardUserDataMethodDescriptor.CheckMethodIsCompatible(mi, false))
  207. continue;
  208. if (mi.GetCustomAttributes(typeof(ExtensionAttribute), false).Length == 0)
  209. continue;
  210. var desc = new StandardUserDataMethodDescriptor(mi, mode);
  211. s_ExtensionMethodRegistry.Add(mi.Name, desc);
  212. ++s_ExtensionMethodChangeVersion;
  213. }
  214. }
  215. }
  216. /// <summary>
  217. /// Gets all the extension methods which can match a given name
  218. /// </summary>
  219. /// <param name="name">The name.</param>
  220. /// <returns></returns>
  221. public static IEnumerable<StandardUserDataMethodDescriptor> GetExtensionMethodsByName(string name)
  222. {
  223. lock (s_Lock)
  224. return s_ExtensionMethodRegistry.Find(name);
  225. }
  226. /// <summary>
  227. /// Gets a number which gets incremented everytime the extension methods registry changes.
  228. /// Use this to invalidate caches based on extension methods
  229. /// </summary>
  230. /// <returns></returns>
  231. public static int GetExtensionMethodsChangeVersion()
  232. {
  233. return s_ExtensionMethodChangeVersion;
  234. }
  235. private static IUserDataDescriptor RegisterType_Impl(Type type, InteropAccessMode accessMode, string friendlyName, IUserDataDescriptor descriptor)
  236. {
  237. if (accessMode == InteropAccessMode.Default)
  238. {
  239. MoonSharpUserDataAttribute attr = type.GetCustomAttributes(true).OfType<MoonSharpUserDataAttribute>()
  240. .SingleOrDefault();
  241. if (attr != null)
  242. accessMode = attr.AccessMode;
  243. }
  244. if (accessMode == InteropAccessMode.Default)
  245. accessMode = s_DefaultAccessMode;
  246. lock (s_Lock)
  247. {
  248. if (!s_Registry.ContainsKey(type))
  249. {
  250. if (descriptor == null)
  251. {
  252. if (type.GetInterfaces().Any(ii => ii == typeof(IUserDataType)))
  253. {
  254. AutoDescribingUserDataDescriptor audd = new AutoDescribingUserDataDescriptor(type, friendlyName);
  255. s_Registry.Add(type, audd);
  256. return audd;
  257. }
  258. else
  259. {
  260. StandardUserDataDescriptor udd = new StandardUserDataDescriptor(type, accessMode, friendlyName);
  261. s_Registry.Add(type, udd);
  262. if (accessMode == InteropAccessMode.BackgroundOptimized)
  263. {
  264. ThreadPool.QueueUserWorkItem(o => udd.Optimize());
  265. }
  266. return udd;
  267. }
  268. }
  269. else
  270. {
  271. s_Registry.Add(type, descriptor);
  272. return descriptor;
  273. }
  274. }
  275. else return s_Registry[type];
  276. }
  277. }
  278. private static IUserDataDescriptor GetDescriptorForType<T>(bool searchInterfaces)
  279. {
  280. return GetDescriptorForType(typeof(T), searchInterfaces);
  281. }
  282. private static IUserDataDescriptor GetDescriptorForType(Type type, bool searchInterfaces)
  283. {
  284. lock (s_Lock)
  285. {
  286. IUserDataDescriptor typeDescriptor = null;
  287. // if the type has been explicitly registered, return its descriptor as it's complete
  288. if (s_Registry.ContainsKey(type))
  289. return s_Registry[type];
  290. if (RegistrationPolicy == InteropRegistrationPolicy.Automatic)
  291. {
  292. return RegisterType_Impl(type, DefaultAccessMode, type.FullName, null);
  293. }
  294. // search for the base object descriptors
  295. for (Type t = type; t != null; t = t.BaseType)
  296. {
  297. IUserDataDescriptor u;
  298. if (s_Registry.TryGetValue(t, out u))
  299. {
  300. typeDescriptor = u;
  301. break;
  302. }
  303. }
  304. // we should not search interfaces (for example, it's just for statics..), no need to look further
  305. if (!searchInterfaces)
  306. return typeDescriptor;
  307. List<IUserDataDescriptor> descriptors = new List<IUserDataDescriptor>();
  308. if (typeDescriptor != null)
  309. descriptors.Add(typeDescriptor);
  310. if (searchInterfaces)
  311. {
  312. foreach (Type t in type.GetInterfaces())
  313. {
  314. IUserDataDescriptor u;
  315. if (s_Registry.TryGetValue(t, out u))
  316. descriptors.Add(u);
  317. }
  318. }
  319. if (descriptors.Count == 1)
  320. return descriptors[0];
  321. else if (descriptors.Count == 0)
  322. return null;
  323. else
  324. return new CompositeUserDataDescriptor(descriptors, type);
  325. }
  326. }
  327. private static IUserDataDescriptor GetDescriptorForObject(object o)
  328. {
  329. return GetDescriptorForType(o.GetType(), true);
  330. }
  331. }
  332. }