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);
}
}
}