152 lines
6.2 KiB
C#
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);
|
|
}
|
|
}
|
|
} |