using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Text; using System.Threading; using MoonSharp.Interpreter.Diagnostics; using MoonSharp.Interpreter.Interop.Converters; namespace MoonSharp.Interpreter.Interop { /// /// Class providing easier marshalling of CLR fields /// public class StandardUserDataFieldDescriptor { /// /// Gets the FieldInfo got by reflection /// public FieldInfo FieldInfo { get; private set; } /// /// Gets the /// public InteropAccessMode AccessMode { get; private set; } /// /// Gets a value indicating whether the described property is static. /// public bool IsStatic { get; private set; } /// /// Gets the name of the property /// public string Name { get; private set; } Func m_OptimizedGetter = null; /// /// Initializes a new instance of the class. /// /// The FieldInfo. /// The internal StandardUserDataFieldDescriptor(FieldInfo fi, InteropAccessMode accessMode) { if (Script.GlobalOptions.Platform.IsRunningOnAOT()) accessMode = InteropAccessMode.Reflection; this.FieldInfo = fi; this.AccessMode = accessMode; this.Name = fi.Name; this.IsStatic = this.FieldInfo.IsStatic; if (AccessMode == InteropAccessMode.Preoptimized) { this.OptimizeGetter(); } } /// /// Gets the value of the property /// /// The script. /// The object. /// public DynValue GetValue(Script script, object obj) { if (AccessMode == InteropAccessMode.LazyOptimized && m_OptimizedGetter == null) OptimizeGetter(); object result = null; if (m_OptimizedGetter != null) result = m_OptimizedGetter(obj); else result = FieldInfo.GetValue(obj); return ClrToScriptConversions.ObjectToDynValue(script, result); } internal void OptimizeGetter() { using (PerformanceStatistics.StartGlobalStopwatch(PerformanceCounter.AdaptersCompilation)) { if (IsStatic) { var paramExp = Expression.Parameter(typeof(object), "dummy"); var propAccess = Expression.Field(null, FieldInfo); var castPropAccess = Expression.Convert(propAccess, typeof(object)); var lambda = Expression.Lambda>(castPropAccess, paramExp); Interlocked.Exchange(ref m_OptimizedGetter, lambda.Compile()); } else { var paramExp = Expression.Parameter(typeof(object), "obj"); var castParamExp = Expression.Convert(paramExp, this.FieldInfo.DeclaringType); var propAccess = Expression.Field(castParamExp, FieldInfo); var castPropAccess = Expression.Convert(propAccess, typeof(object)); var lambda = Expression.Lambda>(castPropAccess, paramExp); Interlocked.Exchange(ref m_OptimizedGetter, lambda.Compile()); } } } /// /// Sets the value of the property /// /// The script. /// The object. /// The value to set. public void SetValue(Script script, object obj, DynValue v) { object value = ScriptToClrConversions.DynValueToObjectOfType(v, this.FieldInfo.FieldType, null, false); try { if (value is double) value = NumericConversions.DoubleToType(FieldInfo.FieldType, (double)value); FieldInfo.SetValue(IsStatic ? null : obj, value); } catch (ArgumentException) { // non-optimized setters fall here throw ScriptRuntimeException.UserDataArgumentTypeMismatch(v.Type, FieldInfo.FieldType); } catch (InvalidCastException) { // optimized setters fall here throw ScriptRuntimeException.UserDataArgumentTypeMismatch(v.Type, FieldInfo.FieldType); } } /// /// Gets the getter of the property as a DynValue containing a callback /// /// The script. /// The object. /// public DynValue GetGetterCallbackAsDynValue(Script script, object obj) { return DynValue.NewCallback((p1, p2) => GetValue(script, obj)); } } }