Upsilon/Upsilon/BaseTypes/UserData/UserDataType.cs

152 lines
6.2 KiB
C#

using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Upsilon.BaseTypes.ScriptFunction;
using Upsilon.BoundTypes;
using Upsilon.StandardLibraries;
namespace Upsilon.BaseTypes.UserData
{
internal class UserDataType
{
public UserDataType(System.Type type)
{
if (type.IsGenericType)
{
type = type.GetGenericTypeDefinition();
}
Type = type;
BoundTypeName = BoundTypeHandler.GetTypeName(type);
Variables = type.GetFields().ToDictionary(x => x.Name.ToLowerInvariant(), x => x);
Properties = type.GetProperties().ToDictionary(x => x.Name.ToLowerInvariant(), x => x);
Methods = new Dictionary<string, UserDataMethod>();
foreach (var methodInfo in type.GetMethods())
{
var hiddenAttribute = methodInfo.GetCustomAttribute(typeof(UpsilonHiddenAttribute));
if (hiddenAttribute != null)
continue;
var commonName = methodInfo.Name.ToLowerInvariant();
var attribute = methodInfo.GetCustomAttribute(typeof(ScriptFunctionAttribute));
if (attribute is ScriptFunctionAttribute sfa )
{
commonName = sfa.Name.ToLowerInvariant();
}
if (Methods.TryGetValue(commonName, out var methodData))
{
methodData.LoadMethodPart(methodInfo);
}
else
{
Methods.Add(commonName, new UserDataMethod(methodInfo));
}
}
OperatorHandler = new UserDataTypeOperators(type);
}
public string BoundTypeName { get; }
private System.Type Type { get; }
private Dictionary<string, FieldInfo> Variables { get; }
private Dictionary<string, PropertyInfo> Properties { get; }
private Dictionary<string, UserDataMethod> Methods { get; }
private UserDataTypeOperators OperatorHandler { get; }
public (ScriptType Type, bool Failed, string Error) Get(object value, string member)
{
var valueType = value.GetType();
if (valueType.IsGenericType)
valueType = valueType.GetGenericTypeDefinition();
if (valueType != Type)
return (null, true, "Invalid Type");
member = member.ToLowerInvariant();
if (Variables.TryGetValue(member, out var info))
{
var field = value.GetType().GetField(info.Name);
return (field.GetValue(value).ToScriptType(), false, null);
}
if (Properties.TryGetValue(member, out var prop))
{
var property = value.GetType().GetProperty(prop.Name);
if (property != null)
{
return (property.GetValue(value, BindingFlags.GetProperty, UpsilonBinder.Default, null,
CultureInfo.InvariantCulture).ToScriptType(), false, null);
}
}
if (Methods.TryGetValue(member, out var method))
{
var isCoroutine = typeof(IEnumerator).IsAssignableFrom(method.ReturnType);
return (new ScriptMethodInfoFunction(method, value, false, isCoroutine), false, null);
}
return (null, true, $"Can't find public member '{member}' on type '{Type}'.");
}
public (bool Failed, string Error) Set(object value, string member, ScriptType newValue)
{
member = member.ToLowerInvariant();
if (value.GetType() != Type)
return (true, "Invalid Type");
if (Variables.TryGetValue(member, out var info))
{
var field = value.GetType().GetField(info.Name);
field.SetValue(value, newValue, BindingFlags.SetField,
UpsilonBinder.Default, CultureInfo.InvariantCulture);
return (false, null);
}
if (Properties.TryGetValue(member, out var prop))
{
var property = value.GetType().GetProperty(prop.Name);
if (property != null)
{
if (property.SetMethod == null || property.SetMethod.IsPrivate)
{
return (true, $"Property '{member}' on type '{Type}' does not have a publicly available setter.");
}
property.SetValue(value, newValue, BindingFlags.SetProperty, UpsilonBinder.Default, null,
CultureInfo.InvariantCulture);
return (false, null);
}
}
return (true, $"Cannot find member '{member}' on type '{Type}'");
}
public (ScriptType Type, bool Failed) BinaryOperator(object value, ScriptType par1, OperatorType op, ScriptType par2)
{
var method = OperatorHandler.GetBinaryOperator(op, par1.GetCSharpType(), par2.GetCSharpType());
if (method == null)
{
return (new ScriptNull(), true);
}
// HACK: Ugly solution for generic methods
if (method.ContainsGenericParameters)
{
method = value.GetType().GetMethod(method.Name, new[] {par1.GetCSharpType(), par2.GetCSharpType()});
}
return (method.Invoke(value, new[] {par1.ToCSharpObject(), par2.ToCSharpObject()}).ToScriptType(), false);
}
public (ScriptType Type, bool Failed) UnaryOperator(object value, ScriptType par1, OperatorType op)
{
var method = OperatorHandler.GetUnaryOperator(op, par1.GetCSharpType());
if (method == null)
{
return (new ScriptNull(), true);
}
// HACK: Ugly solution for generic methods
if (method.ContainsGenericParameters)
{
method = value.GetType().GetMethod(method.Name, new[] {par1.GetCSharpType()});
}
return (method.Invoke(value, new[] {par1.ToCSharpObject()}).ToScriptType(), false);
}
}
}