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