This commit is contained in:
2021-06-13 10:28:03 +02:00
parent eb70603c85
commit df2d24cbd3
7487 changed files with 943244 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
namespace Unity.VisualScripting
{
public enum ActionDirection
{
Any = 0,
Get = 1,
Set = 2
}
}

View File

@@ -0,0 +1,479 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace Unity.VisualScripting
{
public static class AttributeUtility
{
private static readonly Dictionary<object, AttributeCache> optimizedCaches = new Dictionary<object, AttributeCache>();
private class AttributeCache
{
// Using lists instead of hashsets because:
// - Insertion will be faster
// - Iteration will be just as fast
// - We don't need contains lookups
public List<Attribute> inheritedAttributes { get; } = new List<Attribute>();
public List<Attribute> definedAttributes { get; } = new List<Attribute>();
// Important to use Attribute.GetCustomAttributes, because MemberInfo.GetCustomAttributes
// ignores the inherited parameter on properties and events
// However, Attribute.GetCustomAttributes seems to have at least two obscure Mono 2.0 bugs.
// 1. Basically, when a parameter is optional and is marked as [OptionalAttribute],
// the custom attributes array is typed object[] instead of Attribute[], which
// makes Mono throw an exception in Attribute.GetCustomAttributes when trying
// to cast the array. After some testing, it appears this only happens for
// non-inherited calls, and only for parameter infos (although I'm not sure why).
// I *believe* the offending line in the Mono source is this one:
// https://github.com/mono/mono/blob/mono-2-0/mcs/class/corlib/System/MonoCustomAttrs.cs#L143
// 2. For some other implementation reason, on iOS, GetCustomAttributes on MemberInfo fails.
// https://support.ludiq.io/forums/5-bolt/topics/729-systeminvalidcastexception-in-attributecache-on-ios/
// As a fallback, we will use the GetCustomAttributes from the type itself,
// which doesn't seem to be bugged (ugh). But because this method ignores the
// inherited parameter on some occasions, we will warn if the inherited fetch fails.
// Additionally, some Unity built-in attributes use threaded API methods in their
// constructors and will therefore throw an error if GetCustomAttributes is called
// from the serialization thread or from a secondary thread. We'll generally fallback
// and warn on any exception to make sure not to block anything more than needed.
// https://support.ludiq.io/communities/5/topics/2024-/
public AttributeCache(MemberInfo element)
{
Ensure.That(nameof(element)).IsNotNull(element);
try
{
try
{
Cache(Attribute.GetCustomAttributes(element, true), inheritedAttributes);
}
catch (InvalidCastException ex)
{
Cache(element.GetCustomAttributes(true).Cast<Attribute>().ToArray(), inheritedAttributes);
Debug.LogWarning($"Failed to fetch inherited attributes on {element}.\n{ex}");
}
}
catch (Exception ex)
{
Debug.LogWarning($"Failed to fetch inherited attributes on {element}.\n{ex}");
}
try
{
try
{
Cache(Attribute.GetCustomAttributes(element, false), definedAttributes);
}
catch (InvalidCastException)
{
Cache(element.GetCustomAttributes(false).Cast<Attribute>().ToArray(), definedAttributes);
}
}
catch (Exception ex)
{
Debug.LogWarning($"Failed to fetch defined attributes on {element}.\n{ex}");
}
}
public AttributeCache(ParameterInfo element)
{
Ensure.That(nameof(element)).IsNotNull(element);
try
{
try
{
Cache(Attribute.GetCustomAttributes(element, true), inheritedAttributes);
}
catch (InvalidCastException ex)
{
Cache(element.GetCustomAttributes(true).Cast<Attribute>().ToArray(), inheritedAttributes);
Debug.LogWarning($"Failed to fetch inherited attributes on {element}.\n{ex}");
}
}
catch (Exception ex)
{
Debug.LogWarning($"Failed to fetch inherited attributes on {element}.\n{ex}");
}
try
{
try
{
Cache(Attribute.GetCustomAttributes(element, false), definedAttributes);
}
catch (InvalidCastException)
{
Cache(element.GetCustomAttributes(false).Cast<Attribute>().ToArray(), definedAttributes);
}
}
catch (Exception ex)
{
Debug.LogWarning($"Failed to fetch defined attributes on {element}.\n{ex}");
}
}
public AttributeCache(IAttributeProvider element)
{
Ensure.That(nameof(element)).IsNotNull(element);
try
{
Cache(element.GetCustomAttributes(true), inheritedAttributes);
}
catch (Exception ex)
{
Debug.LogWarning($"Failed to fetch inherited attributes on {element}.\n{ex}");
}
try
{
Cache(element.GetCustomAttributes(false), definedAttributes);
}
catch (Exception ex)
{
Debug.LogWarning($"Failed to fetch defined attributes on {element}.\n{ex}");
}
}
private void Cache(Attribute[] attributeObjects, List<Attribute> cache)
{
foreach (var attributeObject in attributeObjects)
{
cache.Add(attributeObject);
}
}
private bool HasAttribute(Type attributeType, List<Attribute> cache)
{
for (int i = 0; i < cache.Count; i++)
{
var attribute = cache[i];
if (attributeType.IsInstanceOfType(attribute))
{
return true;
}
}
return false;
}
private Attribute GetAttribute(Type attributeType, List<Attribute> cache)
{
for (int i = 0; i < cache.Count; i++)
{
var attribute = cache[i];
if (attributeType.IsInstanceOfType(attribute))
{
return attribute;
}
}
return null;
}
private IEnumerable<Attribute> GetAttributes(Type attributeType, List<Attribute> cache)
{
for (int i = 0; i < cache.Count; i++)
{
var attribute = cache[i];
if (attributeType.IsInstanceOfType(attribute))
{
yield return attribute;
}
}
}
public bool HasAttribute(Type attributeType, bool inherit = true)
{
if (inherit)
{
return HasAttribute(attributeType, inheritedAttributes);
}
else
{
return HasAttribute(attributeType, definedAttributes);
}
}
public Attribute GetAttribute(Type attributeType, bool inherit = true)
{
if (inherit)
{
return GetAttribute(attributeType, inheritedAttributes);
}
else
{
return GetAttribute(attributeType, definedAttributes);
}
}
public IEnumerable<Attribute> GetAttributes(Type attributeType, bool inherit = true)
{
if (inherit)
{
return GetAttributes(attributeType, inheritedAttributes);
}
else
{
return GetAttributes(attributeType, definedAttributes);
}
}
public bool HasAttribute<TAttribute>(bool inherit = true)
where TAttribute : Attribute
{
return HasAttribute(typeof(TAttribute), inherit);
}
public TAttribute GetAttribute<TAttribute>(bool inherit = true)
where TAttribute : Attribute
{
return (TAttribute)GetAttribute(typeof(TAttribute), inherit);
}
public IEnumerable<TAttribute> GetAttributes<TAttribute>(bool inherit = true)
where TAttribute : Attribute
{
return GetAttributes(typeof(TAttribute), inherit).Cast<TAttribute>();
}
}
private static AttributeCache GetAttributeCache(MemberInfo element)
{
Ensure.That(nameof(element)).IsNotNull(element);
// For MemberInfo (and therefore Type), we use the MetadataToken
// as a key instead of the object itself, because member infos
// are not singletons but their tokens are, optimizing the cache.
var key = element;
lock (optimizedCaches)
{
if (!optimizedCaches.TryGetValue(key, out var cache))
{
cache = new AttributeCache(element);
optimizedCaches.Add(key, cache);
}
return cache;
}
}
private static AttributeCache GetAttributeCache(ParameterInfo element)
{
Ensure.That(nameof(element)).IsNotNull(element);
// For ParameterInfo, we maybe also should use the MetadataToken,
// but I'm not sure they're globally unique or just locally unique. TODO: Check
var key = element;
lock (optimizedCaches)
{
if (!optimizedCaches.TryGetValue(key, out var cache))
{
cache = new AttributeCache(element);
optimizedCaches.Add(key, cache);
}
return cache;
}
}
private static AttributeCache GetAttributeCache(IAttributeProvider element)
{
Ensure.That(nameof(element)).IsNotNull(element);
var key = element;
lock (optimizedCaches)
{
if (!optimizedCaches.TryGetValue(key, out var cache))
{
cache = new AttributeCache(element);
optimizedCaches.Add(key, cache);
}
return cache;
}
}
#region Members (& Types)
public static void CacheAttributes(MemberInfo element)
{
GetAttributeCache(element);
}
public static bool HasAttribute(this MemberInfo element, Type attributeType, bool inherit = true)
{
return GetAttributeCache(element).HasAttribute(attributeType, inherit);
}
public static Attribute GetAttribute(this MemberInfo element, Type attributeType, bool inherit = true)
{
return GetAttributeCache(element).GetAttribute(attributeType, inherit);
}
public static IEnumerable<Attribute> GetAttributes(this MemberInfo element, Type attributeType, bool inherit = true)
{
return GetAttributeCache(element).GetAttributes(attributeType, inherit);
}
public static bool HasAttribute<TAttribute>(this MemberInfo element, bool inherit = true)
where TAttribute : Attribute
{
return GetAttributeCache(element).HasAttribute<TAttribute>(inherit);
}
public static TAttribute GetAttribute<TAttribute>(this MemberInfo element, bool inherit = true)
where TAttribute : Attribute
{
return GetAttributeCache(element).GetAttribute<TAttribute>(inherit);
}
public static IEnumerable<TAttribute> GetAttributes<TAttribute>(this MemberInfo element, bool inherit = true)
where TAttribute : Attribute
{
return GetAttributeCache(element).GetAttributes<TAttribute>(inherit);
}
#endregion
#region Parameters
public static void CacheAttributes(ParameterInfo element)
{
GetAttributeCache(element);
}
public static bool HasAttribute(this ParameterInfo element, Type attributeType, bool inherit = true)
{
return GetAttributeCache(element).HasAttribute(attributeType, inherit);
}
public static Attribute GetAttribute(this ParameterInfo element, Type attributeType, bool inherit = true)
{
return GetAttributeCache(element).GetAttribute(attributeType, inherit);
}
public static IEnumerable<Attribute> GetAttributes(this ParameterInfo element, Type attributeType, bool inherit = true)
{
return GetAttributeCache(element).GetAttributes(attributeType, inherit);
}
public static bool HasAttribute<TAttribute>(this ParameterInfo element, bool inherit = true)
where TAttribute : Attribute
{
return GetAttributeCache(element).HasAttribute<TAttribute>(inherit);
}
public static TAttribute GetAttribute<TAttribute>(this ParameterInfo element, bool inherit = true)
where TAttribute : Attribute
{
return GetAttributeCache(element).GetAttribute<TAttribute>(inherit);
}
public static IEnumerable<TAttribute> GetAttributes<TAttribute>(this ParameterInfo element, bool inherit = true)
where TAttribute : Attribute
{
return GetAttributeCache(element).GetAttributes<TAttribute>(inherit);
}
#endregion
#region Providers
public static void CacheAttributes(IAttributeProvider element)
{
GetAttributeCache(element);
}
public static bool HasAttribute(this IAttributeProvider element, Type attributeType, bool inherit = true)
{
return GetAttributeCache(element).HasAttribute(attributeType, inherit);
}
public static Attribute GetAttribute(this IAttributeProvider element, Type attributeType, bool inherit = true)
{
return GetAttributeCache(element).GetAttribute(attributeType, inherit);
}
public static IEnumerable<Attribute> GetAttributes(this IAttributeProvider element, Type attributeType, bool inherit = true)
{
return GetAttributeCache(element).GetAttributes(attributeType, inherit);
}
public static bool HasAttribute<TAttribute>(this IAttributeProvider element, bool inherit = true)
where TAttribute : Attribute
{
return GetAttributeCache(element).HasAttribute<TAttribute>(inherit);
}
public static TAttribute GetAttribute<TAttribute>(this IAttributeProvider element, bool inherit = true)
where TAttribute : Attribute
{
return GetAttributeCache(element).GetAttribute<TAttribute>(inherit);
}
public static IEnumerable<TAttribute> GetAttributes<TAttribute>(this IAttributeProvider element, bool inherit = true)
where TAttribute : Attribute
{
return GetAttributeCache(element).GetAttributes<TAttribute>(inherit);
}
#endregion
#region Conditions
public static bool CheckCondition(Type type, object target, string conditionMemberName, bool fallback)
{
Ensure.That(nameof(type)).IsNotNull(type);
try
{
if (target != null && !type.IsInstanceOfType(target))
{
throw new ArgumentException("Target is not an instance of type.", nameof(target));
}
if (conditionMemberName == null)
{
return fallback;
}
var manipulator = type.GetMember(conditionMemberName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault()?.ToManipulator();
if (manipulator == null)
{
throw new MissingMemberException(type.ToString(), conditionMemberName);
}
return manipulator.Get<bool>(target);
}
catch (Exception ex)
{
Debug.LogWarning("Failed to check attribute condition: \n" + ex);
return fallback;
}
}
public static bool CheckCondition<T>(T target, string conditionMemberName, bool fallback)
{
return CheckCondition(target?.GetType() ?? typeof(T), target, conditionMemberName, fallback);
}
#endregion
}
}

View File

@@ -0,0 +1,842 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace Unity.VisualScripting
{
public static class ConversionUtility
{
public enum ConversionType
{
Impossible,
Identity,
Upcast,
Downcast,
NumericImplicit,
NumericExplicit,
UserDefinedImplicit,
UserDefinedExplicit,
UserDefinedThenNumericImplicit,
UserDefinedThenNumericExplicit,
UnityHierarchy,
EnumerableToArray,
EnumerableToList,
ToString
}
private const BindingFlags UserDefinedBindingFlags = BindingFlags.Static | BindingFlags.Public;
private static readonly Dictionary<ConversionQuery, ConversionType> conversionTypesCache = new Dictionary<ConversionQuery, ConversionType>(new ConversionQueryComparer());
private static readonly Dictionary<ConversionQuery, MethodInfo[]> userConversionMethodsCache = new Dictionary<ConversionQuery, MethodInfo[]>(new ConversionQueryComparer());
private static bool RespectsIdentity(Type source, Type destination)
{
return source == destination;
}
private static bool IsUpcast(Type source, Type destination)
{
return destination.IsAssignableFrom(source);
}
private static bool IsDowncast(Type source, Type destination)
{
return source.IsAssignableFrom(destination);
}
private static bool ExpectsString(Type source, Type destination)
{
return destination == typeof(string);
}
public static bool HasImplicitNumericConversion(Type source, Type destination)
{
return implicitNumericConversions.ContainsKey(source) && implicitNumericConversions[source].Contains(destination);
}
public static bool HasExplicitNumericConversion(Type source, Type destination)
{
return explicitNumericConversions.ContainsKey(source) && explicitNumericConversions[source].Contains(destination);
}
public static bool HasNumericConversion(Type source, Type destination)
{
return HasImplicitNumericConversion(source, destination) || HasExplicitNumericConversion(source, destination);
}
private static IEnumerable<MethodInfo> FindUserDefinedConversionMethods(ConversionQuery query)
{
var source = query.source;
var destination = query.destination;
var sourceMethods = source.GetMethods(UserDefinedBindingFlags)
.Where(m => m.IsUserDefinedConversion());
var destinationMethods = destination.GetMethods(UserDefinedBindingFlags)
.Where(m => m.IsUserDefinedConversion());
return sourceMethods.Concat(destinationMethods).Where
(
m => m.GetParameters()[0].ParameterType.IsAssignableFrom(source) ||
source.IsAssignableFrom(m.GetParameters()[0].ParameterType)
);
}
// Returning an array directly so that the enumeration in
// UserDefinedConversion does not allocate memory
private static MethodInfo[] GetUserDefinedConversionMethods(Type source, Type destination)
{
var query = new ConversionQuery(source, destination);
if (!userConversionMethodsCache.ContainsKey(query))
{
userConversionMethodsCache.Add(query, FindUserDefinedConversionMethods(query).ToArray());
}
return userConversionMethodsCache[query];
}
private static ConversionType GetUserDefinedConversionType(Type source, Type destination)
{
var conversionMethods = GetUserDefinedConversionMethods(source, destination);
// Duplicate user defined conversions are not allowed, so FirstOrDefault is safe.
// Look for direct conversions.
var conversionMethod = conversionMethods.FirstOrDefault(m => m.ReturnType == destination);
if (conversionMethod != null)
{
if (conversionMethod.Name == "op_Implicit")
{
return ConversionType.UserDefinedImplicit;
}
else if (conversionMethod.Name == "op_Explicit")
{
return ConversionType.UserDefinedExplicit;
}
}
// Primitive types can skip the middleman cast, even if it is explicit.
else if (destination.IsPrimitive && destination != typeof(IntPtr) && destination != typeof(UIntPtr))
{
// Look for implicit conversions.
conversionMethod = conversionMethods.FirstOrDefault(m => HasImplicitNumericConversion(m.ReturnType, destination));
if (conversionMethod != null)
{
if (conversionMethod.Name == "op_Implicit")
{
return ConversionType.UserDefinedThenNumericImplicit;
}
else if (conversionMethod.Name == "op_Explicit")
{
return ConversionType.UserDefinedThenNumericExplicit;
}
}
// Look for explicit conversions.
else
{
conversionMethod = conversionMethods.FirstOrDefault(m => HasExplicitNumericConversion(m.ReturnType, destination));
if (conversionMethod != null)
{
return ConversionType.UserDefinedThenNumericExplicit;
}
}
}
return ConversionType.Impossible;
}
private static bool HasEnumerableToArrayConversion(Type source, Type destination)
{
return source != typeof(string) &&
typeof(IEnumerable).IsAssignableFrom(source) &&
destination.IsArray &&
destination.GetArrayRank() == 1;
}
private static bool HasEnumerableToListConversion(Type source, Type destination)
{
return source != typeof(string) &&
typeof(IEnumerable).IsAssignableFrom(source) &&
destination.IsGenericType &&
destination.GetGenericTypeDefinition() == typeof(List<>);
}
private static bool HasUnityHierarchyConversion(Type source, Type destination)
{
if (destination == typeof(GameObject))
{
return typeof(Component).IsAssignableFrom(source);
}
else if (typeof(Component).IsAssignableFrom(destination) || destination.IsInterface)
{
return source == typeof(GameObject) || typeof(Component).IsAssignableFrom(source);
}
return false;
}
private static bool IsValidConversion(ConversionType conversionType, bool guaranteed)
{
if (conversionType == ConversionType.Impossible)
{
return false;
}
if (guaranteed)
{
// Downcasts are not guaranteed to succeed.
if (conversionType == ConversionType.Downcast)
{
return false;
}
}
return true;
}
public static bool CanConvert(object value, Type type, bool guaranteed)
{
return IsValidConversion(GetRequiredConversion(value, type), guaranteed);
}
public static bool CanConvert(Type source, Type destination, bool guaranteed)
{
return IsValidConversion(GetRequiredConversion(source, destination), guaranteed);
}
public static object Convert(object value, Type type)
{
return Convert(value, type, GetRequiredConversion(value, type));
}
public static T Convert<T>(object value)
{
return (T)Convert(value, typeof(T));
}
public static bool TryConvert(object value, Type type, out object result, bool guaranteed)
{
var conversionType = GetRequiredConversion(value, type);
if (IsValidConversion(conversionType, guaranteed))
{
result = Convert(value, type, conversionType);
return true;
}
else
{
result = value;
return false;
}
}
public static bool TryConvert<T>(object value, out T result, bool guaranteed)
{
object _result;
if (TryConvert(value, typeof(T), out _result, guaranteed))
{
result = (T)_result;
return false;
}
else
{
result = default(T);
return false;
}
}
public static bool IsConvertibleTo(this Type source, Type destination, bool guaranteed)
{
return CanConvert(source, destination, guaranteed);
}
public static bool IsConvertibleTo(this object source, Type type, bool guaranteed)
{
return CanConvert(source, type, guaranteed);
}
public static bool IsConvertibleTo<T>(this object source, bool guaranteed)
{
return CanConvert(source, typeof(T), guaranteed);
}
public static object ConvertTo(this object source, Type type)
{
return Convert(source, type);
}
public static T ConvertTo<T>(this object source)
{
return (T)Convert(source, typeof(T));
}
public static ConversionType GetRequiredConversion(Type source, Type destination)
{
var query = new ConversionQuery(source, destination);
if (!conversionTypesCache.TryGetValue(query, out var conversionType))
{
conversionType = DetermineConversionType(query);
conversionTypesCache.Add(query, conversionType);
}
return conversionType;
}
private static ConversionType DetermineConversionType(ConversionQuery query)
{
var source = query.source;
var destination = query.destination;
if (source == null)
{
if (destination.IsNullable())
{
return ConversionType.Identity;
}
else
{
return ConversionType.Impossible;
}
}
Ensure.That(nameof(destination)).IsNotNull(destination);
if (RespectsIdentity(source, destination))
{
return ConversionType.Identity;
}
else if (IsUpcast(source, destination))
{
return ConversionType.Upcast;
}
else if (IsDowncast(source, destination))
{
return ConversionType.Downcast;
}
// Disabling *.ToString conversion, because it's more often than otherwise very confusing
/*else if (ExpectsString(source, destination))
{
return ConversionType.ToString;
}*/
else if (HasImplicitNumericConversion(source, destination))
{
return ConversionType.NumericImplicit;
}
else if (HasExplicitNumericConversion(source, destination))
{
return ConversionType.NumericExplicit;
}
else if (HasUnityHierarchyConversion(source, destination))
{
return ConversionType.UnityHierarchy;
}
else if (HasEnumerableToArrayConversion(source, destination))
{
return ConversionType.EnumerableToArray;
}
else if (HasEnumerableToListConversion(source, destination))
{
return ConversionType.EnumerableToList;
}
else
{
var userDefinedConversionType = GetUserDefinedConversionType(source, destination);
if (userDefinedConversionType != ConversionType.Impossible)
{
return userDefinedConversionType;
}
}
return ConversionType.Impossible;
}
public static ConversionType GetRequiredConversion(object value, Type type)
{
Ensure.That(nameof(type)).IsNotNull(type);
return GetRequiredConversion(value?.GetType(), type);
}
private static object NumericConversion(object value, Type type)
{
return System.Convert.ChangeType(value, type);
}
private static object UserDefinedConversion(ConversionType conversion, object value, Type type)
{
var valueType = value.GetType();
var conversionMethods = GetUserDefinedConversionMethods(valueType, type);
var numeric = conversion == ConversionType.UserDefinedThenNumericImplicit ||
conversion == ConversionType.UserDefinedThenNumericExplicit;
MethodInfo conversionMethod = null;
if (numeric)
{
foreach (var m in conversionMethods)
{
if (HasNumericConversion(m.ReturnType, type))
{
conversionMethod = m;
break;
}
}
}
else
{
foreach (var m in conversionMethods)
{
if (m.ReturnType == type)
{
conversionMethod = m;
break;
}
}
}
var result = conversionMethod.InvokeOptimized(null, value);
if (numeric)
{
result = NumericConversion(result, type);
}
return result;
}
private static object EnumerableToArrayConversion(object value, Type arrayType)
{
var elementType = arrayType.GetElementType();
var objectArray = ((IEnumerable)value).Cast<object>().Where(elementType.IsAssignableFrom).ToArray(); // Non-generic OfType
var typedArray = Array.CreateInstance(elementType, objectArray.Length);
objectArray.CopyTo(typedArray, 0);
return typedArray;
}
private static object EnumerableToListConversion(object value, Type listType)
{
var elementType = listType.GetGenericArguments()[0];
var objectArray = ((IEnumerable)value).Cast<object>().Where(elementType.IsAssignableFrom).ToArray(); // Non-generic OfType
var typedList = (IList)Activator.CreateInstance(listType);
for (var i = 0; i < objectArray.Length; i++)
{
typedList.Add(objectArray[i]);
}
return typedList;
}
private static object UnityHierarchyConversion(object value, Type type)
{
if (value.IsUnityNull())
{
return null;
}
if (type == typeof(GameObject) && value is Component)
{
return ((Component)value).gameObject;
}
else if (typeof(Component).IsAssignableFrom(type) || type.IsInterface)
{
if (value is Component)
{
return ((Component)value).GetComponent(type);
}
else if (value is GameObject)
{
return ((GameObject)value).GetComponent(type);
}
}
throw new InvalidConversionException();
}
private static object Convert(object value, Type type, ConversionType conversionType)
{
Ensure.That(nameof(type)).IsNotNull(type);
if (conversionType == ConversionType.Impossible)
{
throw new InvalidConversionException($"Cannot convert from '{value?.GetType().ToString() ?? "null"}' to '{type}'.");
}
try
{
switch (conversionType)
{
case ConversionType.Identity:
case ConversionType.Upcast:
case ConversionType.Downcast:
return value;
case ConversionType.ToString:
return value.ToString();
case ConversionType.NumericImplicit:
case ConversionType.NumericExplicit:
return NumericConversion(value, type);
case ConversionType.UserDefinedImplicit:
case ConversionType.UserDefinedExplicit:
case ConversionType.UserDefinedThenNumericImplicit:
case ConversionType.UserDefinedThenNumericExplicit:
return UserDefinedConversion(conversionType, value, type);
case ConversionType.EnumerableToArray:
return EnumerableToArrayConversion(value, type);
case ConversionType.EnumerableToList:
return EnumerableToListConversion(value, type);
case ConversionType.UnityHierarchy:
return UnityHierarchyConversion(value, type);
default:
throw new UnexpectedEnumValueException<ConversionType>(conversionType);
}
}
catch (Exception ex)
{
throw new InvalidConversionException($"Failed to convert from '{value?.GetType().ToString() ?? "null"}' to '{type}' via {conversionType}.", ex);
}
}
private struct ConversionQuery : IEquatable<ConversionQuery>
{
public readonly Type source;
public readonly Type destination;
public ConversionQuery(Type source, Type destination)
{
this.source = source;
this.destination = destination;
}
public bool Equals(ConversionQuery other)
{
return
source == other.source &&
destination == other.destination;
}
public override bool Equals(object obj)
{
if (!(obj is ConversionQuery))
{
return false;
}
return Equals((ConversionQuery)obj);
}
public override int GetHashCode()
{
return HashUtility.GetHashCode(source, destination);
}
}
// Make sure the equality comparer doesn't use boxing
private struct ConversionQueryComparer : IEqualityComparer<ConversionQuery>
{
public bool Equals(ConversionQuery x, ConversionQuery y)
{
return x.Equals(y);
}
public int GetHashCode(ConversionQuery obj)
{
return obj.GetHashCode();
}
}
#region Numeric Conversions
// https://msdn.microsoft.com/en-us/library/y5b434w4.aspx
private static readonly Dictionary<Type, HashSet<Type>> implicitNumericConversions = new Dictionary<Type, HashSet<Type>>()
{
{
typeof(sbyte),
new HashSet<Type>()
{
typeof(byte),
typeof(int),
typeof(long),
typeof(float),
typeof(double),
typeof(decimal)
}
},
{
typeof(byte),
new HashSet<Type>()
{
typeof(short),
typeof(ushort),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(float),
typeof(double),
typeof(decimal)
}
},
{
typeof(short),
new HashSet<Type>()
{
typeof(int),
typeof(long),
typeof(float),
typeof(double),
typeof(decimal)
}
},
{
typeof(ushort),
new HashSet<Type>()
{
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(float),
typeof(double),
typeof(decimal),
}
},
{
typeof(int),
new HashSet<Type>()
{
typeof(long),
typeof(float),
typeof(double),
typeof(decimal)
}
},
{
typeof(uint),
new HashSet<Type>()
{
typeof(long),
typeof(ulong),
typeof(float),
typeof(double),
typeof(decimal)
}
},
{
typeof(long),
new HashSet<Type>()
{
typeof(float),
typeof(double),
typeof(decimal)
}
},
{
typeof(char),
new HashSet<Type>()
{
typeof(ushort),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(float),
typeof(double),
typeof(decimal)
}
},
{
typeof(float),
new HashSet<Type>()
{
typeof(double)
}
},
{
typeof(ulong),
new HashSet<Type>()
{
typeof(float),
typeof(double),
typeof(decimal)
}
},
};
// https://msdn.microsoft.com/en-us/library/yht2cx7b.aspx
private static readonly Dictionary<Type, HashSet<Type>> explicitNumericConversions = new Dictionary<Type, HashSet<Type>>()
{
{
typeof(sbyte),
new HashSet<Type>()
{
typeof(byte),
typeof(ushort),
typeof(uint),
typeof(ulong),
typeof(char)
}
},
{
typeof(byte),
new HashSet<Type>()
{
typeof(sbyte),
typeof(char)
}
},
{
typeof(short),
new HashSet<Type>()
{
typeof(sbyte),
typeof(byte),
typeof(ushort),
typeof(uint),
typeof(ulong),
typeof(char)
}
},
{
typeof(ushort),
new HashSet<Type>()
{
typeof(sbyte),
typeof(byte),
typeof(short),
typeof(char)
}
},
{
typeof(int),
new HashSet<Type>()
{
typeof(sbyte),
typeof(byte),
typeof(short),
typeof(ushort),
typeof(uint),
typeof(ulong),
typeof(char)
}
},
{
typeof(uint),
new HashSet<Type>()
{
typeof(sbyte),
typeof(byte),
typeof(short),
typeof(ushort),
typeof(int),
typeof(char)
}
},
{
typeof(long),
new HashSet<Type>()
{
typeof(sbyte),
typeof(byte),
typeof(short),
typeof(ushort),
typeof(int),
typeof(uint),
typeof(ulong),
typeof(char)
}
},
{
typeof(ulong),
new HashSet<Type>()
{
typeof(sbyte),
typeof(byte),
typeof(short),
typeof(ushort),
typeof(int),
typeof(uint),
typeof(long),
typeof(char)
}
},
{
typeof(char),
new HashSet<Type>()
{
typeof(sbyte),
typeof(byte),
typeof(short)
}
},
{
typeof(float),
new HashSet<Type>()
{
typeof(sbyte),
typeof(byte),
typeof(short),
typeof(ushort),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(char),
typeof(decimal)
}
},
{
typeof(double),
new HashSet<Type>()
{
typeof(sbyte),
typeof(byte),
typeof(short),
typeof(ushort),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(char),
typeof(float),
typeof(decimal),
}
},
{
typeof(decimal),
new HashSet<Type>()
{
typeof(sbyte),
typeof(byte),
typeof(short),
typeof(ushort),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(char),
typeof(float),
typeof(double)
}
}
};
#endregion
}
}

View File

@@ -0,0 +1,10 @@
using System;
namespace Unity.VisualScripting
{
public sealed class GenericClosingException : Exception
{
public GenericClosingException(string message) : base(message) { }
public GenericClosingException(Type open, Type closed) : base($"Open-constructed type '{open}' is not assignable from closed-constructed type '{closed}'.") { }
}
}

View File

@@ -0,0 +1,9 @@
using System;
namespace Unity.VisualScripting
{
public interface IAttributeProvider
{
Attribute[] GetCustomAttributes(bool inherit);
}
}

View File

@@ -0,0 +1,7 @@
namespace Unity.VisualScripting
{
public interface IPrewarmable
{
void Prewarm();
}
}

View File

@@ -0,0 +1,65 @@
using System;
using System.Reflection;
namespace Unity.VisualScripting
{
public struct LooseAssemblyName
{
public readonly string name;
public LooseAssemblyName(string name)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
this.name = name;
}
public override bool Equals(object obj)
{
if (!(obj is LooseAssemblyName))
{
return false;
}
return ((LooseAssemblyName)obj).name == name;
}
public override int GetHashCode()
{
return HashUtility.GetHashCode(name);
}
public static bool operator ==(LooseAssemblyName a, LooseAssemblyName b)
{
return a.Equals(b);
}
public static bool operator !=(LooseAssemblyName a, LooseAssemblyName b)
{
return !(a == b);
}
public static implicit operator LooseAssemblyName(string name)
{
return new LooseAssemblyName(name);
}
public static implicit operator string(LooseAssemblyName name)
{
return name.name;
}
public static explicit operator LooseAssemblyName(AssemblyName strongAssemblyName)
{
return new LooseAssemblyName(strongAssemblyName.Name);
}
public override string ToString()
{
return name;
}
}
}

View File

@@ -0,0 +1,510 @@
using System;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace Unity.VisualScripting
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public sealed class MemberFilter : Attribute, ICloneable
{
public MemberFilter()
{
// Whitelist
Fields = false;
Properties = false;
Methods = false;
Constructors = false;
Gettable = false;
Settable = false;
// Blacklist
Inherited = true;
Targeted = true;
NonTargeted = true;
Public = true;
NonPublic = false;
ReadOnly = true;
WriteOnly = true;
Extensions = true;
Operators = true;
Conversions = true;
Parameters = true;
Obsolete = false;
OpenConstructedGeneric = false;
TypeInitializers = true;
ClsNonCompliant = true;
}
public bool Fields { get; set; }
public bool Properties { get; set; }
public bool Methods { get; set; }
public bool Constructors { get; set; }
public bool Gettable { get; set; }
public bool Settable { get; set; }
public bool Inherited { get; set; }
public bool Targeted { get; set; }
public bool NonTargeted { get; set; }
public bool Public { get; set; }
public bool NonPublic { get; set; }
public bool ReadOnly { get; set; }
public bool WriteOnly { get; set; }
public bool Extensions { get; set; }
public bool Operators { get; set; }
public bool Conversions { get; set; }
public bool Setters { get; set; }
public bool Parameters { get; set; }
public bool Obsolete { get; set; }
public bool OpenConstructedGeneric { get; set; }
public bool TypeInitializers { get; set; }
public bool ClsNonCompliant { get; set; }
public BindingFlags validBindingFlags
{
get
{
BindingFlags flags = 0;
if (Public)
{
flags |= BindingFlags.Public;
}
if (NonPublic)
{
flags |= BindingFlags.NonPublic;
}
if (Targeted || Constructors)
{
flags |= BindingFlags.Instance;
}
if (NonTargeted)
{
flags |= BindingFlags.Static;
}
if (!Inherited)
{
flags |= BindingFlags.DeclaredOnly;
}
if (NonTargeted && Inherited)
{
flags |= BindingFlags.FlattenHierarchy;
}
return flags;
}
}
public MemberTypes validMemberTypes
{
get
{
MemberTypes types = 0;
if (Fields || Gettable || Settable)
{
types |= MemberTypes.Field;
}
if (Properties || Gettable || Settable)
{
types |= MemberTypes.Property;
}
if (Methods || Gettable)
{
types |= MemberTypes.Method;
}
if (Constructors || Gettable)
{
types |= MemberTypes.Constructor;
}
return types;
}
}
object ICloneable.Clone()
{
return Clone();
}
public MemberFilter Clone()
{
return new MemberFilter()
{
Fields = Fields,
Properties = Properties,
Methods = Methods,
Constructors = Constructors,
Gettable = Gettable,
Settable = Settable,
Inherited = Inherited,
Targeted = Targeted,
NonTargeted = NonTargeted,
Public = Public,
NonPublic = NonPublic,
ReadOnly = ReadOnly,
WriteOnly = WriteOnly,
Extensions = Extensions,
Operators = Operators,
Conversions = Conversions,
Parameters = Parameters,
Obsolete = Obsolete,
OpenConstructedGeneric = OpenConstructedGeneric,
TypeInitializers = TypeInitializers,
ClsNonCompliant = ClsNonCompliant
};
}
public override bool Equals(object obj)
{
var other = obj as MemberFilter;
if (other == null)
{
return false;
}
return
Fields == other.Fields &&
Properties == other.Properties &&
Methods == other.Methods &&
Constructors == other.Constructors &&
Gettable == other.Gettable &&
Settable == other.Settable &&
Inherited == other.Inherited &&
Targeted == other.Targeted &&
NonTargeted == other.NonTargeted &&
Public == other.Public &&
NonPublic == other.NonPublic &&
ReadOnly == other.ReadOnly &&
WriteOnly == other.WriteOnly &&
Extensions == other.Extensions &&
Operators == other.Operators &&
Conversions == other.Conversions &&
Parameters == other.Parameters &&
Obsolete == other.Obsolete &&
OpenConstructedGeneric == other.OpenConstructedGeneric &&
TypeInitializers == other.TypeInitializers &&
ClsNonCompliant == other.ClsNonCompliant;
}
public override int GetHashCode()
{
unchecked
{
var hash = 17;
hash = hash * 23 + Fields.GetHashCode();
hash = hash * 23 + Properties.GetHashCode();
hash = hash * 23 + Methods.GetHashCode();
hash = hash * 23 + Constructors.GetHashCode();
hash = hash * 23 + Gettable.GetHashCode();
hash = hash * 23 + Settable.GetHashCode();
hash = hash * 23 + Inherited.GetHashCode();
hash = hash * 23 + Targeted.GetHashCode();
hash = hash * 23 + NonTargeted.GetHashCode();
hash = hash * 23 + Public.GetHashCode();
hash = hash * 23 + NonPublic.GetHashCode();
hash = hash * 23 + ReadOnly.GetHashCode();
hash = hash * 23 + WriteOnly.GetHashCode();
hash = hash * 23 + Extensions.GetHashCode();
hash = hash * 23 + Operators.GetHashCode();
hash = hash * 23 + Conversions.GetHashCode();
hash = hash * 23 + Parameters.GetHashCode();
hash = hash * 23 + Obsolete.GetHashCode();
hash = hash * 23 + OpenConstructedGeneric.GetHashCode();
hash = hash * 23 + TypeInitializers.GetHashCode();
hash = hash * 23 + ClsNonCompliant.GetHashCode();
return hash;
}
}
public bool ValidateMember(MemberInfo member, TypeFilter typeFilter = null)
{
if (member is FieldInfo)
{
var field = (FieldInfo)member;
// Whitelist
var isGettable = true;
var isSettable = !field.IsLiteral && !field.IsInitOnly;
var whitelisted = Fields || (Gettable && isGettable) || (Settable && isSettable);
if (!whitelisted)
{
return false;
}
// Targetting
var isTargeted = !field.IsStatic;
if (!Targeted && isTargeted)
{
return false;
}
if (!NonTargeted && !isTargeted)
{
return false;
}
// Accessibility
if (!WriteOnly && !isGettable)
{
return false;
}
if (!ReadOnly && !isSettable)
{
return false;
}
// Visibility
if (!Public && field.IsPublic)
{
return false;
}
if (!NonPublic && !field.IsPublic)
{
return false;
}
// Type
if (typeFilter != null && !typeFilter.ValidateType(field.FieldType))
{
return false;
}
// Other
if (field.IsSpecialName)
{
return false;
}
}
else if (member is PropertyInfo)
{
var property = (PropertyInfo)member;
var getter = property.GetGetMethod(true);
var setter = property.GetSetMethod(true);
// Whitelist
var isGettable = property.CanRead;
var isSettable = property.CanWrite;
var whitelisted = Properties || (Gettable && isGettable) || (Settable && isSettable);
if (!whitelisted)
{
return false;
}
// Visibility & Accessibility
// TODO: Refactor + Take into account when Public = false
var requiresRead = (!WriteOnly || (!Properties && Gettable));
var requiresWrite = (!ReadOnly || (!Properties && Settable));
var canRead = property.CanRead && (NonPublic || getter.IsPublic);
var canWrite = property.CanWrite && (NonPublic || setter.IsPublic);
if (requiresRead && !canRead)
{
return false;
}
if (requiresWrite && !canWrite)
{
return false;
}
// Targetting
var isTargeted = !(getter ?? setter).IsStatic;
if (!Targeted && isTargeted)
{
return false;
}
if (!NonTargeted && !isTargeted)
{
return false;
}
// Type
if (typeFilter != null && !typeFilter.ValidateType(property.PropertyType))
{
return false;
}
// Other
if (property.IsSpecialName)
{
return false;
}
if (property.GetIndexParameters().Any())
{
return false;
}
}
else if (member is MethodBase)
{
var methodOrConstructor = (MethodBase)member;
var isExtension = methodOrConstructor.IsExtensionMethod();
var isTargeted = !methodOrConstructor.IsStatic || isExtension;
// Visibility
if (!Public && methodOrConstructor.IsPublic)
{
return false;
}
if (!NonPublic && !methodOrConstructor.IsPublic)
{
return false;
}
// Other
if (!Parameters && (methodOrConstructor.GetParameters().Length > (isExtension ? 1 : 0)))
{
return false;
}
if (!OpenConstructedGeneric && methodOrConstructor.ContainsGenericParameters)
{
return false;
}
if (member is MethodInfo)
{
var method = (MethodInfo)member;
var isOperator = method.IsOperator();
var isConversion = method.IsUserDefinedConversion();
// Whitelist
var isGettable = method.ReturnType != typeof(void);
var isSettable = false;
var whitelisted = Methods || (Gettable && isGettable) || (Settable && isSettable);
if (!whitelisted)
{
return false;
}
// Targetting
if (!Targeted && isTargeted)
{
return false;
}
if (!NonTargeted && !isTargeted)
{
return false;
}
// Operators
if (!Operators && isOperator)
{
return false;
}
// Extensions
if (!Extensions && isExtension)
{
return false;
}
// Type
if (typeFilter != null && !typeFilter.ValidateType(method.ReturnType))
{
return false;
}
// Other
if (method.IsSpecialName && !(isOperator || isConversion))
{
return false;
}
}
else if (member is ConstructorInfo)
{
var constructor = (ConstructorInfo)member;
// Whitelist
var isGettable = true;
var isSettable = false;
var whitelisted = Constructors || (Gettable && isGettable) || (Settable && isSettable);
if (!whitelisted)
{
return false;
}
// Type
if (typeFilter != null && !typeFilter.ValidateType(constructor.DeclaringType))
{
return false;
}
// Type Initializers
if (constructor.IsStatic && !TypeInitializers)
{
return false;
}
// Other
if (typeof(Component).IsAssignableFrom(member.DeclaringType) || typeof(ScriptableObject).IsAssignableFrom(member.DeclaringType))
{
return false;
}
}
}
// Obsolete
if (!Obsolete && member.HasAttribute<ObsoleteAttribute>(false))
{
return false;
}
// CLS Compliance
if (!ClsNonCompliant)
{
var clsCompliantAttribute = member.GetAttribute<CLSCompliantAttribute>();
if (clsCompliantAttribute != null && !clsCompliantAttribute.IsCompliant)
{
return false;
}
}
return true;
}
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine($"Fields: {Fields}");
sb.AppendLine($"Properties: {Properties}");
sb.AppendLine($"Methods: {Methods}");
sb.AppendLine($"Constructors: {Constructors}");
sb.AppendLine($"Gettable: {Gettable}");
sb.AppendLine($"Settable: {Settable}");
sb.AppendLine();
sb.AppendLine($"Inherited: {Inherited}");
sb.AppendLine($"Instance: {Targeted}");
sb.AppendLine($"Static: {NonTargeted}");
sb.AppendLine($"Public: {Public}");
sb.AppendLine($"NonPublic: {NonPublic}");
sb.AppendLine($"ReadOnly: {ReadOnly}");
sb.AppendLine($"WriteOnly: {WriteOnly}");
sb.AppendLine($"Extensions: {Extensions}");
sb.AppendLine($"Operators: {Operators}");
sb.AppendLine($"Conversions: {Conversions}");
sb.AppendLine($"Parameters: {Parameters}");
sb.AppendLine($"Obsolete: {Obsolete}");
sb.AppendLine($"OpenConstructedGeneric: {OpenConstructedGeneric}");
sb.AppendLine($"TypeInitializers: {TypeInitializers}");
sb.AppendLine($"ClsNonCompliant: {ClsNonCompliant}");
return sb.ToString();
}
public static MemberFilter Any => new MemberFilter()
{
Fields = true,
Properties = true,
Methods = true,
Constructors = true
};
}
}

View File

@@ -0,0 +1,22 @@
using System.Collections.Generic;
using System.Reflection;
namespace Unity.VisualScripting
{
/// <summary>
/// A member info comparer that will ignore the ReflectedType
/// property by relying on the metadata token for comparison.
/// </summary>
public class MemberInfoComparer : EqualityComparer<MemberInfo>
{
public override bool Equals(MemberInfo x, MemberInfo y)
{
return x?.MetadataToken == y?.MetadataToken;
}
public override int GetHashCode(MemberInfo obj)
{
return obj.MetadataToken;
}
}
}

View File

@@ -0,0 +1,693 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using UnityObject = UnityEngine.Object;
namespace Unity.VisualScripting
{
public static class MemberUtility
{
static MemberUtility()
{
// Cache a list of all extension methods in assemblies
// http://stackoverflow.com/a/299526
extensionMethodsCache = RuntimeCodebase.types
.Where(type => type.IsStatic() && !type.IsGenericType && !type.IsNested)
.SelectMany(type => type.GetMethods())
.Where(method => method.IsExtension())
.ToArray();
// The process of resolving generic methods is very expensive.
// Cache the results for each this parameter type.
inheritedExtensionMethodsCache = new Dictionary<Type, MethodInfo[]>();
genericExtensionMethods = new HashSet<MethodInfo>();
}
private static readonly MethodInfo[] extensionMethodsCache;
private static readonly Dictionary<Type, MethodInfo[]> inheritedExtensionMethodsCache;
private static readonly HashSet<MethodInfo> genericExtensionMethods;
public static bool IsOperator(this MethodInfo method)
{
return method.IsSpecialName && OperatorUtility.operatorNames.ContainsKey(method.Name);
}
public static bool IsUserDefinedConversion(this MethodInfo method)
{
return method.IsSpecialName && (method.Name == "op_Implicit" || method.Name == "op_Explicit");
}
/// <remarks>This may return an open-constructed method as well.</remarks>
public static MethodInfo MakeGenericMethodVia(this MethodInfo openConstructedMethod, params Type[] closedConstructedParameterTypes)
{
Ensure.That(nameof(openConstructedMethod)).IsNotNull(openConstructedMethod);
Ensure.That(nameof(closedConstructedParameterTypes)).IsNotNull(closedConstructedParameterTypes);
if (!openConstructedMethod.ContainsGenericParameters)
{
// The method contains no generic parameters,
// it is by definition already resolved.
return openConstructedMethod;
}
var openConstructedParameterTypes = openConstructedMethod.GetParameters().Select(p => p.ParameterType).ToArray();
if (openConstructedParameterTypes.Length != closedConstructedParameterTypes.Length)
{
throw new ArgumentOutOfRangeException(nameof(closedConstructedParameterTypes));
}
var resolvedGenericParameters = new Dictionary<Type, Type>();
for (var i = 0; i < openConstructedParameterTypes.Length; i++)
{
// Resolve each open-constructed parameter type via the equivalent
// closed-constructed parameter type.
var openConstructedParameterType = openConstructedParameterTypes[i];
var closedConstructedParameterType = closedConstructedParameterTypes[i];
openConstructedParameterType.MakeGenericTypeVia(closedConstructedParameterType, resolvedGenericParameters);
}
// Construct the final closed-constructed method from the resolved arguments
var openConstructedGenericArguments = openConstructedMethod.GetGenericArguments();
var closedConstructedGenericArguments = openConstructedGenericArguments.Select(openConstructedGenericArgument =>
{
// If the generic argument has been successfully resolved, use it;
// otherwise, leave the open-constructed argument in place.
if (resolvedGenericParameters.ContainsKey(openConstructedGenericArgument))
{
return resolvedGenericParameters[openConstructedGenericArgument];
}
else
{
return openConstructedGenericArgument;
}
}).ToArray();
return openConstructedMethod.MakeGenericMethod(closedConstructedGenericArguments);
}
public static bool IsGenericExtension(this MethodInfo methodInfo)
{
return genericExtensionMethods.Contains(methodInfo);
}
private static IEnumerable<MethodInfo> GetInheritedExtensionMethods(Type thisArgumentType)
{
foreach (var extensionMethod in extensionMethodsCache)
{
var compatibleThis = extensionMethod.GetParameters()[0].ParameterType.CanMakeGenericTypeVia(thisArgumentType);
if (compatibleThis)
{
if (extensionMethod.ContainsGenericParameters)
{
var closedConstructedParameterTypes = thisArgumentType.Yield().Concat(extensionMethod.GetParametersWithoutThis().Select(p => p.ParameterType));
var closedConstructedMethod = extensionMethod.MakeGenericMethodVia(closedConstructedParameterTypes.ToArray());
genericExtensionMethods.Add(closedConstructedMethod);
yield return closedConstructedMethod;
}
else
{
yield return extensionMethod;
}
}
}
}
public static IEnumerable<MethodInfo> GetExtensionMethods(this Type thisArgumentType, bool inherited = true)
{
if (inherited)
{
lock (inheritedExtensionMethodsCache)
{
if (!inheritedExtensionMethodsCache.TryGetValue(thisArgumentType, out var inheritedExtensionMethods))
{
inheritedExtensionMethods = GetInheritedExtensionMethods(thisArgumentType).ToArray();
inheritedExtensionMethodsCache.Add(thisArgumentType, inheritedExtensionMethods);
}
return inheritedExtensionMethods;
}
}
else
{
return extensionMethodsCache.Where(method => method.GetParameters()[0].ParameterType == thisArgumentType);
}
}
public static bool IsExtension(this MethodInfo methodInfo)
{
return methodInfo.HasAttribute<ExtensionAttribute>(false);
}
public static bool IsExtensionMethod(this MemberInfo memberInfo)
{
return memberInfo is MethodInfo methodInfo && methodInfo.IsExtension();
}
public static Delegate CreateDelegate(this MethodInfo methodInfo, Type delegateType)
{
return Delegate.CreateDelegate(delegateType, methodInfo);
}
public static bool IsAccessor(this MemberInfo memberInfo)
{
return memberInfo is FieldInfo || memberInfo is PropertyInfo;
}
public static Type GetAccessorType(this MemberInfo memberInfo)
{
if (memberInfo is FieldInfo)
{
return ((FieldInfo)memberInfo).FieldType;
}
else if (memberInfo is PropertyInfo)
{
return ((PropertyInfo)memberInfo).PropertyType;
}
else
{
return null;
}
}
public static bool IsPubliclyGettable(this MemberInfo memberInfo)
{
if (memberInfo is FieldInfo)
{
return ((FieldInfo)memberInfo).IsPublic;
}
else if (memberInfo is PropertyInfo)
{
var propertyInfo = (PropertyInfo)memberInfo;
return propertyInfo.CanRead && propertyInfo.GetGetMethod(false) != null;
}
else if (memberInfo is MethodInfo)
{
return ((MethodInfo)memberInfo).IsPublic;
}
else if (memberInfo is ConstructorInfo)
{
return ((ConstructorInfo)memberInfo).IsPublic;
}
else
{
throw new NotSupportedException();
}
}
private static Type ExtendedDeclaringType(this MemberInfo memberInfo)
{
if (memberInfo is MethodInfo methodInfo && methodInfo.IsExtension())
{
return methodInfo.GetParameters()[0].ParameterType;
}
else
{
return memberInfo.DeclaringType;
}
}
public static Type ExtendedDeclaringType(this MemberInfo memberInfo, bool invokeAsExtension)
{
if (invokeAsExtension)
{
return memberInfo.ExtendedDeclaringType();
}
else
{
return memberInfo.DeclaringType;
}
}
public static bool IsStatic(this PropertyInfo propertyInfo)
{
return (propertyInfo.GetGetMethod(true)?.IsStatic ?? false) ||
(propertyInfo.GetSetMethod(true)?.IsStatic ?? false);
}
public static bool IsStatic(this MemberInfo memberInfo)
{
if (memberInfo is FieldInfo)
{
return ((FieldInfo)memberInfo).IsStatic;
}
else if (memberInfo is PropertyInfo)
{
return ((PropertyInfo)memberInfo).IsStatic();
}
else if (memberInfo is MethodBase)
{
return ((MethodBase)memberInfo).IsStatic;
}
else
{
throw new NotSupportedException();
}
}
private static IEnumerable<ParameterInfo> GetParametersWithoutThis(this MethodBase methodBase)
{
return methodBase.GetParameters().Skip(methodBase.IsExtensionMethod() ? 1 : 0);
}
public static bool IsInvokedAsExtension(this MethodBase methodBase, Type targetType)
{
return methodBase.IsExtensionMethod() && methodBase.DeclaringType != targetType;
}
public static IEnumerable<ParameterInfo> GetInvocationParameters(this MethodBase methodBase, bool invokeAsExtension)
{
if (invokeAsExtension)
{
return methodBase.GetParametersWithoutThis();
}
else
{
return methodBase.GetParameters();
}
}
public static IEnumerable<ParameterInfo> GetInvocationParameters(this MethodBase methodBase, Type targetType)
{
return methodBase.GetInvocationParameters(methodBase.IsInvokedAsExtension(targetType));
}
public static Type UnderlyingParameterType(this ParameterInfo parameterInfo)
{
if (parameterInfo.ParameterType.IsByRef)
{
return parameterInfo.ParameterType.GetElementType();
}
else
{
return parameterInfo.ParameterType;
}
}
// https://stackoverflow.com/questions/9977530/
// https://stackoverflow.com/questions/16186694
public static bool HasDefaultValue(this ParameterInfo parameterInfo)
{
return (parameterInfo.Attributes & ParameterAttributes.HasDefault) == ParameterAttributes.HasDefault;
}
public static object DefaultValue(this ParameterInfo parameterInfo)
{
if (parameterInfo.HasDefaultValue())
{
var defaultValue = parameterInfo.DefaultValue;
// https://stackoverflow.com/questions/45393580
if (defaultValue == null && parameterInfo.ParameterType.IsValueType)
{
defaultValue = parameterInfo.ParameterType.Default();
}
return defaultValue;
}
else
{
return parameterInfo.UnderlyingParameterType().Default();
}
}
public static object PseudoDefaultValue(this ParameterInfo parameterInfo)
{
if (parameterInfo.HasDefaultValue())
{
var defaultValue = parameterInfo.DefaultValue;
// https://stackoverflow.com/questions/45393580
if (defaultValue == null && parameterInfo.ParameterType.IsValueType)
{
defaultValue = parameterInfo.ParameterType.PseudoDefault();
}
return defaultValue;
}
else
{
return parameterInfo.UnderlyingParameterType().PseudoDefault();
}
}
public static bool AllowsNull(this ParameterInfo parameterInfo)
{
var type = parameterInfo.ParameterType;
return (type.IsReferenceType() && parameterInfo.HasAttribute<AllowsNullAttribute>()) || Nullable.GetUnderlyingType(type) != null;
}
// https://stackoverflow.com/questions/30102174/
public static bool HasOutModifier(this ParameterInfo parameterInfo)
{
Ensure.That(nameof(parameterInfo)).IsNotNull(parameterInfo);
// Checking for IsOut is not enough, because parameters marked with the [Out] attribute
// also return true, while not necessarily having the "out" modifier. This is common for P/Invoke,
// for example in Unity's ParticleSystem.GetParticles.
return parameterInfo.IsOut && parameterInfo.ParameterType.IsByRef;
}
public static bool CanWrite(this FieldInfo fieldInfo)
{
return !(fieldInfo.IsInitOnly || fieldInfo.IsLiteral);
}
public static Member ToManipulator(this MemberInfo memberInfo)
{
return ToManipulator(memberInfo, memberInfo.DeclaringType);
}
public static Member ToManipulator(this MemberInfo memberInfo, Type targetType)
{
if (memberInfo is FieldInfo fieldInfo)
{
return fieldInfo.ToManipulator(targetType);
}
if (memberInfo is PropertyInfo propertyInfo)
{
return propertyInfo.ToManipulator(targetType);
}
if (memberInfo is MethodInfo methodInfo)
{
return methodInfo.ToManipulator(targetType);
}
if (memberInfo is ConstructorInfo constructorInfo)
{
return constructorInfo.ToManipulator(targetType);
}
throw new InvalidOperationException();
}
public static Member ToManipulator(this FieldInfo fieldInfo, Type targetType)
{
return new Member(targetType, fieldInfo);
}
public static Member ToManipulator(this PropertyInfo propertyInfo, Type targetType)
{
return new Member(targetType, propertyInfo);
}
public static Member ToManipulator(this MethodInfo methodInfo, Type targetType)
{
return new Member(targetType, methodInfo);
}
public static Member ToManipulator(this ConstructorInfo constructorInfo, Type targetType)
{
return new Member(targetType, constructorInfo);
}
public static ConstructorInfo GetConstructorAccepting(this Type type, Type[] paramTypes, bool nonPublic)
{
var bindingFlags = BindingFlags.Instance | BindingFlags.Public;
if (nonPublic)
{
bindingFlags |= BindingFlags.NonPublic;
}
return type
.GetConstructors(bindingFlags)
.FirstOrDefault(constructor =>
{
var parameters = constructor.GetParameters();
if (parameters.Length != paramTypes.Length)
{
return false;
}
for (var i = 0; i < parameters.Length; i++)
{
if (paramTypes[i] == null)
{
if (!parameters[i].ParameterType.IsNullable())
{
return false;
}
}
else
{
if (!parameters[i].ParameterType.IsAssignableFrom(paramTypes[i]))
{
return false;
}
}
}
return true;
});
}
public static ConstructorInfo GetConstructorAccepting(this Type type, params Type[] paramTypes)
{
return GetConstructorAccepting(type, paramTypes, true);
}
public static ConstructorInfo GetPublicConstructorAccepting(this Type type, params Type[] paramTypes)
{
return GetConstructorAccepting(type, paramTypes, false);
}
public static ConstructorInfo GetDefaultConstructor(this Type type)
{
return GetConstructorAccepting(type);
}
public static ConstructorInfo GetPublicDefaultConstructor(this Type type)
{
return GetPublicConstructorAccepting(type);
}
public static MemberInfo[] GetExtendedMember(this Type type, string name, MemberTypes types, BindingFlags flags)
{
var members = type.GetMember(name, types, flags).ToList();
if (types.HasFlag(MemberTypes.Method)) // Check for extension methods
{
members.AddRange(type.GetExtensionMethods()
.Where(extension => extension.Name == name)
.Cast<MemberInfo>());
}
return members.ToArray();
}
public static MemberInfo[] GetExtendedMembers(this Type type, BindingFlags flags)
{
var members = type.GetMembers(flags).ToHashSet();
foreach (var extensionMethod in type.GetExtensionMethods())
{
members.Add(extensionMethod);
}
return members.ToArray();
}
#region Signature Disambiguation
private static bool NameMatches(this MemberInfo member, string name)
{
return member.Name == name;
}
private static bool ParametersMatch(this MethodBase methodBase, IEnumerable<Type> parameterTypes, bool invokeAsExtension)
{
Ensure.That(nameof(parameterTypes)).IsNotNull(parameterTypes);
return methodBase.GetInvocationParameters(invokeAsExtension).Select(paramInfo => paramInfo.ParameterType).SequenceEqual(parameterTypes);
}
private static bool GenericArgumentsMatch(this MethodInfo method, IEnumerable<Type> genericArgumentTypes)
{
Ensure.That(nameof(genericArgumentTypes)).IsNotNull(genericArgumentTypes);
if (method.ContainsGenericParameters)
{
return false;
}
return method.GetGenericArguments().SequenceEqual(genericArgumentTypes);
}
public static bool SignatureMatches(this FieldInfo field, string name)
{
return field.NameMatches(name);
}
public static bool SignatureMatches(this PropertyInfo property, string name)
{
return property.NameMatches(name);
}
public static bool SignatureMatches(this ConstructorInfo constructor, string name, IEnumerable<Type> parameterTypes)
{
return constructor.NameMatches(name) && constructor.ParametersMatch(parameterTypes, false);
}
public static bool SignatureMatches(this MethodInfo method, string name, IEnumerable<Type> parameterTypes, bool invokeAsExtension)
{
return method.NameMatches(name) && method.ParametersMatch(parameterTypes, invokeAsExtension) && !method.ContainsGenericParameters;
}
public static bool SignatureMatches(this MethodInfo method, string name, IEnumerable<Type> parameterTypes, IEnumerable<Type> genericArgumentTypes, bool invokeAsExtension)
{
return method.NameMatches(name) && method.ParametersMatch(parameterTypes, invokeAsExtension) && method.GenericArgumentsMatch(genericArgumentTypes);
}
public static FieldInfo GetFieldUnambiguous(this Type type, string name, BindingFlags flags)
{
Ensure.That(nameof(type)).IsNotNull(type);
Ensure.That(nameof(name)).IsNotNull(name);
flags |= BindingFlags.DeclaredOnly;
while (type != null)
{
var field = type.GetField(name, flags);
if (field != null)
{
return field;
}
type = type.BaseType;
}
return null;
}
public static PropertyInfo GetPropertyUnambiguous(this Type type, string name, BindingFlags flags)
{
Ensure.That(nameof(type)).IsNotNull(type);
Ensure.That(nameof(name)).IsNotNull(name);
flags |= BindingFlags.DeclaredOnly;
while (type != null)
{
var property = type.GetProperty(name, flags);
if (property != null)
{
return property;
}
type = type.BaseType;
}
return null;
}
public static MethodInfo GetMethodUnambiguous(this Type type, string name, BindingFlags flags)
{
Ensure.That(nameof(type)).IsNotNull(type);
Ensure.That(nameof(name)).IsNotNull(name);
flags |= BindingFlags.DeclaredOnly;
while (type != null)
{
var method = type.GetMethod(name, flags);
if (method != null)
{
return method;
}
type = type.BaseType;
}
return null;
}
private static TMemberInfo DisambiguateHierarchy<TMemberInfo>(this IEnumerable<TMemberInfo> members, Type type) where TMemberInfo : MemberInfo
{
while (type != null)
{
foreach (var member in members)
{
var methodInfo = member as MethodInfo;
var invokedAsExtension = methodInfo != null && methodInfo.IsInvokedAsExtension(type);
if (member.ExtendedDeclaringType(invokedAsExtension) == type)
{
return member;
}
}
type = type.BaseType;
}
return null;
}
public static FieldInfo Disambiguate(this IEnumerable<FieldInfo> fields, Type type)
{
Ensure.That(nameof(fields)).IsNotNull(fields);
Ensure.That(nameof(type)).IsNotNull(type);
return fields.DisambiguateHierarchy(type);
}
public static PropertyInfo Disambiguate(this IEnumerable<PropertyInfo> properties, Type type)
{
Ensure.That(nameof(properties)).IsNotNull(properties);
Ensure.That(nameof(type)).IsNotNull(type);
return properties.DisambiguateHierarchy(type);
}
public static ConstructorInfo Disambiguate(this IEnumerable<ConstructorInfo> constructors, Type type, IEnumerable<Type> parameterTypes)
{
Ensure.That(nameof(constructors)).IsNotNull(constructors);
Ensure.That(nameof(type)).IsNotNull(type);
Ensure.That(nameof(parameterTypes)).IsNotNull(parameterTypes);
return constructors.Where(m => m.ParametersMatch(parameterTypes, false) && !m.ContainsGenericParameters).DisambiguateHierarchy(type);
}
public static MethodInfo Disambiguate(this IEnumerable<MethodInfo> methods, Type type, IEnumerable<Type> parameterTypes)
{
Ensure.That(nameof(methods)).IsNotNull(methods);
Ensure.That(nameof(type)).IsNotNull(type);
Ensure.That(nameof(parameterTypes)).IsNotNull(parameterTypes);
return methods.Where(m => m.ParametersMatch(parameterTypes, m.IsInvokedAsExtension(type)) && !m.ContainsGenericParameters).DisambiguateHierarchy(type);
}
public static MethodInfo Disambiguate(this IEnumerable<MethodInfo> methods, Type type, IEnumerable<Type> parameterTypes, IEnumerable<Type> genericArgumentTypes)
{
Ensure.That(nameof(methods)).IsNotNull(methods);
Ensure.That(nameof(type)).IsNotNull(type);
Ensure.That(nameof(parameterTypes)).IsNotNull(parameterTypes);
Ensure.That(nameof(genericArgumentTypes)).IsNotNull(genericArgumentTypes);
return methods.Where(m => m.ParametersMatch(parameterTypes, m.IsInvokedAsExtension(type)) && m.GenericArgumentsMatch(genericArgumentTypes)).DisambiguateHierarchy(type);
}
#endregion
}
}

View File

@@ -0,0 +1,172 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Unity.VisualScripting
{
public sealed class Namespace
{
private Namespace(string fullName)
{
FullName = fullName;
if (fullName != null)
{
var parts = fullName.Split('.');
Name = parts[parts.Length - 1];
if (parts.Length > 1)
{
Root = parts[0];
Parent = fullName.Substring(0, fullName.LastIndexOf('.'));
}
else
{
Root = this;
IsRoot = true;
Parent = Global;
}
}
else
{
Root = this;
IsRoot = true;
IsGlobal = true;
}
}
public Namespace Root { get; }
public Namespace Parent { get; }
public string FullName { get; }
public string Name { get; }
public bool IsRoot { get; }
public bool IsGlobal { get; }
public IEnumerable<Namespace> Ancestors
{
get
{
var ancestor = Parent;
while (ancestor != null)
{
yield return ancestor;
ancestor = ancestor.Parent;
}
}
}
public IEnumerable<Namespace> AndAncestors()
{
yield return this;
foreach (var ancestor in Ancestors)
{
yield return ancestor;
}
}
public override int GetHashCode()
{
if (FullName == null)
{
return 0;
}
return FullName.GetHashCode();
}
public override string ToString()
{
return FullName;
}
static Namespace()
{
collection = new Collection();
}
private static readonly Collection collection;
public static Namespace Global { get; } = new Namespace(null);
public static Namespace FromFullName(string fullName)
{
if (fullName == null)
{
return Global;
}
Namespace @namespace;
if (!collection.TryGetValue(fullName, out @namespace))
{
@namespace = new Namespace(fullName);
collection.Add(@namespace);
}
return @namespace;
}
public override bool Equals(object obj)
{
var other = obj as Namespace;
if (other == null)
{
return false;
}
return FullName == other.FullName;
}
public static implicit operator Namespace(string fullName)
{
return FromFullName(fullName);
}
public static implicit operator string(Namespace @namespace)
{
return @namespace.FullName;
}
public static bool operator ==(Namespace a, Namespace b)
{
if (ReferenceEquals(a, b))
{
return true;
}
if (((object)a == null) || ((object)b == null))
{
return false;
}
return a.Equals(b);
}
public static bool operator !=(Namespace a, Namespace b)
{
return !(a == b);
}
private class Collection : KeyedCollection<string, Namespace>, IKeyedCollection<string, Namespace>
{
protected override string GetKeyForItem(Namespace item)
{
return item.FullName;
}
public bool TryGetValue(string key, out Namespace value)
{
if (Dictionary == null)
{
value = default(Namespace);
return false;
}
return Dictionary.TryGetValue(key, out value);
}
}
}
}

View File

@@ -0,0 +1,150 @@
namespace Unity.VisualScripting
{
public sealed class AdditionHandler : BinaryOperatorHandler
{
public AdditionHandler() : base("Addition", "Add", "+", "op_Addition")
{
Handle<byte, byte>((a, b) => a + b);
Handle<byte, sbyte>((a, b) => a + b);
Handle<byte, short>((a, b) => a + b);
Handle<byte, ushort>((a, b) => a + b);
Handle<byte, int>((a, b) => a + b);
Handle<byte, uint>((a, b) => a + b);
Handle<byte, long>((a, b) => a + b);
Handle<byte, ulong>((a, b) => a + b);
Handle<byte, float>((a, b) => a + b);
Handle<byte, decimal>((a, b) => a + b);
Handle<byte, double>((a, b) => a + b);
Handle<sbyte, byte>((a, b) => a + b);
Handle<sbyte, sbyte>((a, b) => a + b);
Handle<sbyte, short>((a, b) => a + b);
Handle<sbyte, ushort>((a, b) => a + b);
Handle<sbyte, int>((a, b) => a + b);
Handle<sbyte, uint>((a, b) => a + b);
Handle<sbyte, long>((a, b) => a + b);
//Handle<sbyte, ulong>((a, b) => a + b);
Handle<sbyte, float>((a, b) => a + b);
Handle<sbyte, decimal>((a, b) => a + b);
Handle<sbyte, double>((a, b) => a + b);
Handle<short, byte>((a, b) => a + b);
Handle<short, sbyte>((a, b) => a + b);
Handle<short, short>((a, b) => a + b);
Handle<short, ushort>((a, b) => a + b);
Handle<short, int>((a, b) => a + b);
Handle<short, uint>((a, b) => a + b);
Handle<short, long>((a, b) => a + b);
//Handle<short, ulong>((a, b) => a + b);
Handle<short, float>((a, b) => a + b);
Handle<short, decimal>((a, b) => a + b);
Handle<short, double>((a, b) => a + b);
Handle<ushort, byte>((a, b) => a + b);
Handle<ushort, sbyte>((a, b) => a + b);
Handle<ushort, short>((a, b) => a + b);
Handle<ushort, ushort>((a, b) => a + b);
Handle<ushort, int>((a, b) => a + b);
Handle<ushort, uint>((a, b) => a + b);
Handle<ushort, long>((a, b) => a + b);
Handle<ushort, ulong>((a, b) => a + b);
Handle<ushort, float>((a, b) => a + b);
Handle<ushort, decimal>((a, b) => a + b);
Handle<ushort, double>((a, b) => a + b);
Handle<int, byte>((a, b) => a + b);
Handle<int, sbyte>((a, b) => a + b);
Handle<int, short>((a, b) => a + b);
Handle<int, ushort>((a, b) => a + b);
Handle<int, int>((a, b) => a + b);
Handle<int, uint>((a, b) => a + b);
Handle<int, long>((a, b) => a + b);
//Handle<int, ulong>((a, b) => a + b);
Handle<int, float>((a, b) => a + b);
Handle<int, decimal>((a, b) => a + b);
Handle<int, double>((a, b) => a + b);
Handle<uint, byte>((a, b) => a + b);
Handle<uint, sbyte>((a, b) => a + b);
Handle<uint, short>((a, b) => a + b);
Handle<uint, ushort>((a, b) => a + b);
Handle<uint, int>((a, b) => a + b);
Handle<uint, uint>((a, b) => a + b);
Handle<uint, long>((a, b) => a + b);
Handle<uint, ulong>((a, b) => a + b);
Handle<uint, float>((a, b) => a + b);
Handle<uint, decimal>((a, b) => a + b);
Handle<uint, double>((a, b) => a + b);
Handle<long, byte>((a, b) => a + b);
Handle<long, sbyte>((a, b) => a + b);
Handle<long, short>((a, b) => a + b);
Handle<long, ushort>((a, b) => a + b);
Handle<long, int>((a, b) => a + b);
Handle<long, uint>((a, b) => a + b);
Handle<long, long>((a, b) => a + b);
//Handle<long, ulong>((a, b) => a + b);
Handle<long, float>((a, b) => a + b);
Handle<long, decimal>((a, b) => a + b);
Handle<long, double>((a, b) => a + b);
Handle<ulong, byte>((a, b) => a + b);
//Handle<ulong, sbyte>((a, b) => a + b);
//Handle<ulong, short>((a, b) => a + b);
Handle<ulong, ushort>((a, b) => a + b);
//Handle<ulong, int>((a, b) => a + b);
Handle<ulong, uint>((a, b) => a + b);
//Handle<ulong, long>((a, b) => a + b);
Handle<ulong, ulong>((a, b) => a + b);
Handle<ulong, float>((a, b) => a + b);
Handle<ulong, decimal>((a, b) => a + b);
Handle<ulong, double>((a, b) => a + b);
Handle<float, byte>((a, b) => a + b);
Handle<float, sbyte>((a, b) => a + b);
Handle<float, short>((a, b) => a + b);
Handle<float, ushort>((a, b) => a + b);
Handle<float, int>((a, b) => a + b);
Handle<float, uint>((a, b) => a + b);
Handle<float, long>((a, b) => a + b);
Handle<float, ulong>((a, b) => a + b);
Handle<float, float>((a, b) => a + b);
//Handle<float, decimal>((a, b) => a + b);
Handle<float, double>((a, b) => a + b);
Handle<decimal, byte>((a, b) => a + b);
Handle<decimal, sbyte>((a, b) => a + b);
Handle<decimal, short>((a, b) => a + b);
Handle<decimal, ushort>((a, b) => a + b);
Handle<decimal, int>((a, b) => a + b);
Handle<decimal, uint>((a, b) => a + b);
Handle<decimal, long>((a, b) => a + b);
Handle<decimal, ulong>((a, b) => a + b);
//Handle<decimal, float>((a, b) => a + b);
Handle<decimal, decimal>((a, b) => a + b);
//Handle<decimal, double>((a, b) => a + b);
Handle<double, byte>((a, b) => a + b);
Handle<double, sbyte>((a, b) => a + b);
Handle<double, short>((a, b) => a + b);
Handle<double, ushort>((a, b) => a + b);
Handle<double, int>((a, b) => a + b);
Handle<double, uint>((a, b) => a + b);
Handle<double, long>((a, b) => a + b);
Handle<double, ulong>((a, b) => a + b);
Handle<double, float>((a, b) => a + b);
//Handle<double, decimal>((a, b) => a + b);
Handle<double, double>((a, b) => a + b);
}
protected override object CustomHandling(object leftOperand, object rightOperand)
{
if (leftOperand is string || rightOperand is string)
{
return string.Concat(leftOperand, rightOperand);
}
return base.CustomHandling(leftOperand, rightOperand);
}
}
}

View File

@@ -0,0 +1,9 @@
using System;
namespace Unity.VisualScripting
{
public sealed class AmbiguousOperatorException : OperatorException
{
public AmbiguousOperatorException(string symbol, Type leftType, Type rightType) : base($"Ambiguous use of operator '{symbol}' between types '{leftType?.ToString() ?? "null"}' and '{rightType?.ToString() ?? "null"}'.") { }
}
}

View File

@@ -0,0 +1,82 @@
namespace Unity.VisualScripting
{
public class AndHandler : BinaryOperatorHandler
{
public AndHandler() : base("And", "And", "&", "op_BitwiseAnd")
{
Handle<bool, bool>((a, b) => a & b);
Handle<byte, byte>((a, b) => a & b);
Handle<byte, sbyte>((a, b) => a & b);
Handle<byte, short>((a, b) => a & b);
Handle<byte, ushort>((a, b) => a & b);
Handle<byte, int>((a, b) => a & b);
Handle<byte, uint>((a, b) => a & b);
Handle<byte, long>((a, b) => a & b);
Handle<byte, ulong>((a, b) => a & b);
Handle<sbyte, byte>((a, b) => a & b);
Handle<sbyte, sbyte>((a, b) => a & b);
Handle<sbyte, short>((a, b) => a & b);
Handle<sbyte, ushort>((a, b) => a & b);
Handle<sbyte, int>((a, b) => a & b);
Handle<sbyte, uint>((a, b) => a & b);
Handle<sbyte, long>((a, b) => a & b);
//Handle<sbyte, ulong>((a, b) => a & b);
Handle<short, byte>((a, b) => a & b);
Handle<short, sbyte>((a, b) => a & b);
Handle<short, short>((a, b) => a & b);
Handle<short, ushort>((a, b) => a & b);
Handle<short, int>((a, b) => a & b);
Handle<short, uint>((a, b) => a & b);
Handle<short, long>((a, b) => a & b);
//Handle<short, ulong>((a, b) => a & b);
Handle<ushort, byte>((a, b) => a & b);
Handle<ushort, sbyte>((a, b) => a & b);
Handle<ushort, short>((a, b) => a & b);
Handle<ushort, ushort>((a, b) => a & b);
Handle<ushort, int>((a, b) => a & b);
Handle<ushort, uint>((a, b) => a & b);
Handle<ushort, long>((a, b) => a & b);
Handle<ushort, ulong>((a, b) => a & b);
Handle<int, byte>((a, b) => a & b);
Handle<int, sbyte>((a, b) => a & b);
Handle<int, short>((a, b) => a & b);
Handle<int, ushort>((a, b) => a & b);
Handle<int, int>((a, b) => a & b);
Handle<int, uint>((a, b) => a & b);
Handle<int, long>((a, b) => a & b);
//Handle<int, ulong>((a, b) => a & b);
Handle<uint, byte>((a, b) => a & b);
Handle<uint, sbyte>((a, b) => a & b);
Handle<uint, short>((a, b) => a & b);
Handle<uint, ushort>((a, b) => a & b);
Handle<uint, int>((a, b) => a & b);
Handle<uint, uint>((a, b) => a & b);
Handle<uint, long>((a, b) => a & b);
Handle<uint, ulong>((a, b) => a & b);
Handle<long, byte>((a, b) => a & b);
Handle<long, sbyte>((a, b) => a & b);
Handle<long, short>((a, b) => a & b);
Handle<long, ushort>((a, b) => a & b);
Handle<long, int>((a, b) => a & b);
Handle<long, uint>((a, b) => a & b);
Handle<long, long>((a, b) => a & b);
//Handle<long, ulong>((a, b) => a & b);
Handle<ulong, byte>((a, b) => a & b);
//Handle<ulong, sbyte>((a, b) => a & b);
//Handle<ulong, short>((a, b) => a & b);
Handle<ulong, ushort>((a, b) => a & b);
//Handle<ulong, int>((a, b) => a & b);
Handle<ulong, uint>((a, b) => a & b);
//Handle<ulong, long>((a, b) => a & b);
Handle<ulong, ulong>((a, b) => a & b);
}
}
}

View File

@@ -0,0 +1,22 @@
namespace Unity.VisualScripting
{
public enum BinaryOperator
{
Addition,
Subtraction,
Multiplication,
Division,
Modulo,
And,
Or,
ExclusiveOr,
Equality,
Inequality,
GreaterThan,
LessThan,
GreaterThanOrEqual,
LessThanOrEqual,
LeftShift,
RightShift,
}
}

View File

@@ -0,0 +1,180 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Unity.VisualScripting
{
public abstract class BinaryOperatorHandler : OperatorHandler
{
protected BinaryOperatorHandler(string name, string verb, string symbol, string customMethodName)
: base(name, verb, symbol, customMethodName) { }
private readonly Dictionary<OperatorQuery, Func<object, object, object>> handlers = new Dictionary<OperatorQuery, Func<object, object, object>>();
private readonly Dictionary<OperatorQuery, IOptimizedInvoker> userDefinedOperators = new Dictionary<OperatorQuery, IOptimizedInvoker>();
private readonly Dictionary<OperatorQuery, OperatorQuery> userDefinedOperandTypes = new Dictionary<OperatorQuery, OperatorQuery>();
public virtual object Operate(object leftOperand, object rightOperand)
{
OperatorQuery query;
var leftType = leftOperand?.GetType();
var rightType = rightOperand?.GetType();
if (leftType != null && rightType != null)
{
query = new OperatorQuery(leftType, rightType);
}
else if (leftType != null && leftType.IsNullable())
{
query = new OperatorQuery(leftType, leftType);
}
else if (rightType != null && rightType.IsNullable())
{
query = new OperatorQuery(rightType, rightType);
}
else if (leftType == null && rightType == null)
{
return BothNullHandling();
}
else
{
return SingleNullHandling();
}
if (handlers.ContainsKey(query))
{
return handlers[query](leftOperand, rightOperand);
}
if (customMethodName != null)
{
if (!userDefinedOperators.ContainsKey(query))
{
var leftMethod = query.leftType.GetMethod(customMethodName, BindingFlags.Public | BindingFlags.Static, null, new[] { query.leftType, query.rightType }, null);
if (query.leftType != query.rightType)
{
var rightMethod = query.rightType.GetMethod(customMethodName, BindingFlags.Public | BindingFlags.Static, null, new[] { query.leftType, query.rightType }, null);
if (leftMethod != null && rightMethod != null)
{
throw new AmbiguousOperatorException(symbol, query.leftType, query.rightType);
}
var method = (leftMethod ?? rightMethod);
if (method != null)
{
userDefinedOperandTypes.Add(query, ResolveUserDefinedOperandTypes(method));
}
userDefinedOperators.Add(query, method?.Prewarm());
}
else
{
if (leftMethod != null)
{
userDefinedOperandTypes.Add(query, ResolveUserDefinedOperandTypes(leftMethod));
}
userDefinedOperators.Add(query, leftMethod?.Prewarm());
}
}
if (userDefinedOperators[query] != null)
{
leftOperand = ConversionUtility.Convert(leftOperand, userDefinedOperandTypes[query].leftType);
rightOperand = ConversionUtility.Convert(rightOperand, userDefinedOperandTypes[query].rightType);
return userDefinedOperators[query].Invoke(null, leftOperand, rightOperand);
}
}
return CustomHandling(leftOperand, rightOperand);
}
protected virtual object CustomHandling(object leftOperand, object rightOperand)
{
throw new InvalidOperatorException(symbol, leftOperand?.GetType(), rightOperand?.GetType());
}
protected virtual object BothNullHandling()
{
throw new InvalidOperatorException(symbol, null, null);
}
protected virtual object SingleNullHandling()
{
throw new InvalidOperatorException(symbol, null, null);
}
protected void Handle<TLeft, TRight>(Func<TLeft, TRight, object> handler, bool reverse = false)
{
var query = new OperatorQuery(typeof(TLeft), typeof(TRight));
if (handlers.ContainsKey(query))
{
throw new ArgumentException($"A handler is already registered for '{typeof(TLeft)} {symbol} {typeof(TRight)}'.");
}
handlers.Add(query, (left, right) => handler((TLeft)left, (TRight)right));
if (reverse && typeof(TLeft) != typeof(TRight))
{
var reverseQuery = new OperatorQuery(typeof(TRight), typeof(TLeft));
if (!handlers.ContainsKey(reverseQuery))
{
handlers.Add(reverseQuery, (left, right) => handler((TLeft)left, (TRight)right));
}
}
}
private static OperatorQuery ResolveUserDefinedOperandTypes(MethodInfo userDefinedOperator)
{
// We will need to convert the operands to the argument types,
// because .NET is actually permissive of implicit conversion
// in its GetMethod calls. For example, an operator requiring
// a float operand will accept an int. However, our optimized
// reflection code is more strict, and will only accept the
// exact type, hence why we need to manually store the expected
// parameter types here to convert them later.
var parameters = userDefinedOperator.GetParameters();
return new OperatorQuery(parameters[0].ParameterType, parameters[1].ParameterType);
}
private struct OperatorQuery : IEquatable<OperatorQuery>
{
public readonly Type leftType;
public readonly Type rightType;
public OperatorQuery(Type leftType, Type rightType)
{
this.leftType = leftType;
this.rightType = rightType;
}
public bool Equals(OperatorQuery other)
{
return
leftType == other.leftType &&
rightType == other.rightType;
}
public override bool Equals(object obj)
{
if (!(obj is OperatorQuery))
{
return false;
}
return Equals((OperatorQuery)obj);
}
public override int GetHashCode()
{
return HashUtility.GetHashCode(leftType, rightType);
}
}
}
}

View File

@@ -0,0 +1,20 @@
namespace Unity.VisualScripting
{
public sealed class DecrementHandler : UnaryOperatorHandler
{
public DecrementHandler() : base("Decrement", "Decrement", "--", "op_Decrement")
{
Handle<byte>(a => --a);
Handle<sbyte>(a => --a);
Handle<short>(a => --a);
Handle<ushort>(a => --a);
Handle<int>(a => --a);
Handle<uint>(a => --a);
Handle<long>(a => --a);
Handle<ulong>(a => --a);
Handle<float>(a => --a);
Handle<decimal>(a => --a);
Handle<double>(a => --a);
}
}
}

View File

@@ -0,0 +1,140 @@
namespace Unity.VisualScripting
{
public sealed class DivisionHandler : BinaryOperatorHandler
{
public DivisionHandler() : base("Division", "Divide", "/", "op_Division")
{
Handle<byte, byte>((a, b) => a / b);
Handle<byte, sbyte>((a, b) => a / b);
Handle<byte, short>((a, b) => a / b);
Handle<byte, ushort>((a, b) => a / b);
Handle<byte, int>((a, b) => a / b);
Handle<byte, uint>((a, b) => a / b);
Handle<byte, long>((a, b) => a / b);
Handle<byte, ulong>((a, b) => a / b);
Handle<byte, float>((a, b) => a / b);
Handle<byte, decimal>((a, b) => a / b);
Handle<byte, double>((a, b) => a / b);
Handle<sbyte, byte>((a, b) => a / b);
Handle<sbyte, sbyte>((a, b) => a / b);
Handle<sbyte, short>((a, b) => a / b);
Handle<sbyte, ushort>((a, b) => a / b);
Handle<sbyte, int>((a, b) => a / b);
Handle<sbyte, uint>((a, b) => a / b);
Handle<sbyte, long>((a, b) => a / b);
//Handle<sbyte, ulong>((a, b) => a / b);
Handle<sbyte, float>((a, b) => a / b);
Handle<sbyte, decimal>((a, b) => a / b);
Handle<sbyte, double>((a, b) => a / b);
Handle<short, byte>((a, b) => a / b);
Handle<short, sbyte>((a, b) => a / b);
Handle<short, short>((a, b) => a / b);
Handle<short, ushort>((a, b) => a / b);
Handle<short, int>((a, b) => a / b);
Handle<short, uint>((a, b) => a / b);
Handle<short, long>((a, b) => a / b);
//Handle<short, ulong>((a, b) => a / b);
Handle<short, float>((a, b) => a / b);
Handle<short, decimal>((a, b) => a / b);
Handle<short, double>((a, b) => a / b);
Handle<ushort, byte>((a, b) => a / b);
Handle<ushort, sbyte>((a, b) => a / b);
Handle<ushort, short>((a, b) => a / b);
Handle<ushort, ushort>((a, b) => a / b);
Handle<ushort, int>((a, b) => a / b);
Handle<ushort, uint>((a, b) => a / b);
Handle<ushort, long>((a, b) => a / b);
Handle<ushort, ulong>((a, b) => a / b);
Handle<ushort, float>((a, b) => a / b);
Handle<ushort, decimal>((a, b) => a / b);
Handle<ushort, double>((a, b) => a / b);
Handle<int, byte>((a, b) => a / b);
Handle<int, sbyte>((a, b) => a / b);
Handle<int, short>((a, b) => a / b);
Handle<int, ushort>((a, b) => a / b);
Handle<int, int>((a, b) => a / b);
Handle<int, uint>((a, b) => a / b);
Handle<int, long>((a, b) => a / b);
//Handle<int, ulong>((a, b) => a / b);
Handle<int, float>((a, b) => a / b);
Handle<int, decimal>((a, b) => a / b);
Handle<int, double>((a, b) => a / b);
Handle<uint, byte>((a, b) => a / b);
Handle<uint, sbyte>((a, b) => a / b);
Handle<uint, short>((a, b) => a / b);
Handle<uint, ushort>((a, b) => a / b);
Handle<uint, int>((a, b) => a / b);
Handle<uint, uint>((a, b) => a / b);
Handle<uint, long>((a, b) => a / b);
Handle<uint, ulong>((a, b) => a / b);
Handle<uint, float>((a, b) => a / b);
Handle<uint, decimal>((a, b) => a / b);
Handle<uint, double>((a, b) => a / b);
Handle<long, byte>((a, b) => a / b);
Handle<long, sbyte>((a, b) => a / b);
Handle<long, short>((a, b) => a / b);
Handle<long, ushort>((a, b) => a / b);
Handle<long, int>((a, b) => a / b);
Handle<long, uint>((a, b) => a / b);
Handle<long, long>((a, b) => a / b);
//Handle<long, ulong>((a, b) => a / b);
Handle<long, float>((a, b) => a / b);
Handle<long, decimal>((a, b) => a / b);
Handle<long, double>((a, b) => a / b);
Handle<ulong, byte>((a, b) => a / b);
//Handle<ulong, sbyte>((a, b) => a / b);
//Handle<ulong, short>((a, b) => a / b);
Handle<ulong, ushort>((a, b) => a / b);
//Handle<ulong, int>((a, b) => a / b);
Handle<ulong, uint>((a, b) => a / b);
//Handle<ulong, long>((a, b) => a / b);
Handle<ulong, ulong>((a, b) => a / b);
Handle<ulong, float>((a, b) => a / b);
Handle<ulong, decimal>((a, b) => a / b);
Handle<ulong, double>((a, b) => a / b);
Handle<float, byte>((a, b) => a / b);
Handle<float, sbyte>((a, b) => a / b);
Handle<float, short>((a, b) => a / b);
Handle<float, ushort>((a, b) => a / b);
Handle<float, int>((a, b) => a / b);
Handle<float, uint>((a, b) => a / b);
Handle<float, long>((a, b) => a / b);
Handle<float, ulong>((a, b) => a / b);
Handle<float, float>((a, b) => a / b);
//Handle<float, decimal>((a, b) => a / b);
Handle<float, double>((a, b) => a / b);
Handle<decimal, byte>((a, b) => a / b);
Handle<decimal, sbyte>((a, b) => a / b);
Handle<decimal, short>((a, b) => a / b);
Handle<decimal, ushort>((a, b) => a / b);
Handle<decimal, int>((a, b) => a / b);
Handle<decimal, uint>((a, b) => a / b);
Handle<decimal, long>((a, b) => a / b);
Handle<decimal, ulong>((a, b) => a / b);
//Handle<decimal, float>((a, b) => a / b);
Handle<decimal, decimal>((a, b) => a / b);
//Handle<decimal, double>((a, b) => a / b);
Handle<double, byte>((a, b) => a / b);
Handle<double, sbyte>((a, b) => a / b);
Handle<double, short>((a, b) => a / b);
Handle<double, ushort>((a, b) => a / b);
Handle<double, int>((a, b) => a / b);
Handle<double, uint>((a, b) => a / b);
Handle<double, long>((a, b) => a / b);
Handle<double, ulong>((a, b) => a / b);
Handle<double, float>((a, b) => a / b);
//Handle<double, decimal>((a, b) => a / b);
Handle<double, double>((a, b) => a / b);
}
}
}

View File

@@ -0,0 +1,157 @@
using UnityEngine;
namespace Unity.VisualScripting
{
public class EqualityHandler : BinaryOperatorHandler
{
public EqualityHandler() : base("Equality", "Equal", "==", "op_Equality")
{
Handle<byte, byte>((a, b) => a == b);
Handle<byte, sbyte>((a, b) => a == b);
Handle<byte, short>((a, b) => a == b);
Handle<byte, ushort>((a, b) => a == b);
Handle<byte, int>((a, b) => a == b);
Handle<byte, uint>((a, b) => a == b);
Handle<byte, long>((a, b) => a == b);
Handle<byte, ulong>((a, b) => a == b);
Handle<byte, float>((a, b) => a == b);
Handle<byte, decimal>((a, b) => a == b);
Handle<byte, double>((a, b) => a == b);
Handle<sbyte, byte>((a, b) => a == b);
Handle<sbyte, sbyte>((a, b) => a == b);
Handle<sbyte, short>((a, b) => a == b);
Handle<sbyte, ushort>((a, b) => a == b);
Handle<sbyte, int>((a, b) => a == b);
Handle<sbyte, uint>((a, b) => a == b);
Handle<sbyte, long>((a, b) => a == b);
//Handle<sbyte, ulong>((a, b) => a == b);
Handle<sbyte, float>((a, b) => a == b);
Handle<sbyte, decimal>((a, b) => a == b);
Handle<sbyte, double>((a, b) => a == b);
Handle<short, byte>((a, b) => a == b);
Handle<short, sbyte>((a, b) => a == b);
Handle<short, short>((a, b) => a == b);
Handle<short, ushort>((a, b) => a == b);
Handle<short, int>((a, b) => a == b);
Handle<short, uint>((a, b) => a == b);
Handle<short, long>((a, b) => a == b);
//Handle<short, ulong>((a, b) => a == b);
Handle<short, float>((a, b) => a == b);
Handle<short, decimal>((a, b) => a == b);
Handle<short, double>((a, b) => a == b);
Handle<ushort, byte>((a, b) => a == b);
Handle<ushort, sbyte>((a, b) => a == b);
Handle<ushort, short>((a, b) => a == b);
Handle<ushort, ushort>((a, b) => a == b);
Handle<ushort, int>((a, b) => a == b);
Handle<ushort, uint>((a, b) => a == b);
Handle<ushort, long>((a, b) => a == b);
Handle<ushort, ulong>((a, b) => a == b);
Handle<ushort, float>((a, b) => a == b);
Handle<ushort, decimal>((a, b) => a == b);
Handle<ushort, double>((a, b) => a == b);
Handle<int, byte>((a, b) => a == b);
Handle<int, sbyte>((a, b) => a == b);
Handle<int, short>((a, b) => a == b);
Handle<int, ushort>((a, b) => a == b);
Handle<int, int>((a, b) => a == b);
Handle<int, uint>((a, b) => a == b);
Handle<int, long>((a, b) => a == b);
//Handle<int, ulong>((a, b) => a == b);
Handle<int, float>((a, b) => a == b);
Handle<int, decimal>((a, b) => a == b);
Handle<int, double>((a, b) => a == b);
Handle<uint, byte>((a, b) => a == b);
Handle<uint, sbyte>((a, b) => a == b);
Handle<uint, short>((a, b) => a == b);
Handle<uint, ushort>((a, b) => a == b);
Handle<uint, int>((a, b) => a == b);
Handle<uint, uint>((a, b) => a == b);
Handle<uint, long>((a, b) => a == b);
Handle<uint, ulong>((a, b) => a == b);
Handle<uint, float>((a, b) => a == b);
Handle<uint, decimal>((a, b) => a == b);
Handle<uint, double>((a, b) => a == b);
Handle<long, byte>((a, b) => a == b);
Handle<long, sbyte>((a, b) => a == b);
Handle<long, short>((a, b) => a == b);
Handle<long, ushort>((a, b) => a == b);
Handle<long, int>((a, b) => a == b);
Handle<long, uint>((a, b) => a == b);
Handle<long, long>((a, b) => a == b);
//Handle<long, ulong>((a, b) => a == b);
Handle<long, float>((a, b) => a == b);
Handle<long, decimal>((a, b) => a == b);
Handle<long, double>((a, b) => a == b);
Handle<ulong, byte>((a, b) => a == b);
//Handle<ulong, sbyte>((a, b) => a == b);
//Handle<ulong, short>((a, b) => a == b);
Handle<ulong, ushort>((a, b) => a == b);
//Handle<ulong, int>((a, b) => a == b);
Handle<ulong, uint>((a, b) => a == b);
//Handle<ulong, long>((a, b) => a == b);
Handle<ulong, ulong>((a, b) => a == b);
Handle<ulong, float>((a, b) => a == b);
Handle<ulong, decimal>((a, b) => a == b);
Handle<ulong, double>((a, b) => a == b);
Handle<float, byte>((a, b) => a == b);
Handle<float, sbyte>((a, b) => a == b);
Handle<float, short>((a, b) => a == b);
Handle<float, ushort>((a, b) => a == b);
Handle<float, int>((a, b) => a == b);
Handle<float, uint>((a, b) => a == b);
Handle<float, long>((a, b) => a == b);
Handle<float, ulong>((a, b) => a == b);
Handle<float, float>((a, b) => Mathf.Approximately(a, b));
//Handle<float, decimal>((a, b) => a == b);
Handle<float, double>((a, b) => a == b);
Handle<decimal, byte>((a, b) => a == b);
Handle<decimal, sbyte>((a, b) => a == b);
Handle<decimal, short>((a, b) => a == b);
Handle<decimal, ushort>((a, b) => a == b);
Handle<decimal, int>((a, b) => a == b);
Handle<decimal, uint>((a, b) => a == b);
Handle<decimal, long>((a, b) => a == b);
Handle<decimal, ulong>((a, b) => a == b);
//Handle<decimal, float>((a, b) => a == b);
Handle<decimal, decimal>((a, b) => a == b);
//Handle<decimal, double>((a, b) => a == b);
Handle<double, byte>((a, b) => a == b);
Handle<double, sbyte>((a, b) => a == b);
Handle<double, short>((a, b) => a == b);
Handle<double, ushort>((a, b) => a == b);
Handle<double, int>((a, b) => a == b);
Handle<double, uint>((a, b) => a == b);
Handle<double, long>((a, b) => a == b);
Handle<double, ulong>((a, b) => a == b);
Handle<double, float>((a, b) => a == b);
//Handle<double, decimal>((a, b) => a == b);
Handle<double, double>((a, b) => a == b);
}
protected override object BothNullHandling()
{
return true;
}
protected override object SingleNullHandling()
{
return false;
}
protected override object CustomHandling(object leftOperand, object rightOperand)
{
return Equals(leftOperand, rightOperand);
}
}
}

View File

@@ -0,0 +1,82 @@
namespace Unity.VisualScripting
{
public class ExclusiveOrHandler : BinaryOperatorHandler
{
public ExclusiveOrHandler() : base("Exclusive Or", "Exclusive Or", "^", "op_ExclusiveOr")
{
Handle<bool, bool>((a, b) => a ^ b);
Handle<byte, byte>((a, b) => a ^ b);
Handle<byte, sbyte>((a, b) => a ^ b);
Handle<byte, short>((a, b) => a ^ b);
Handle<byte, ushort>((a, b) => a ^ b);
Handle<byte, int>((a, b) => a ^ b);
Handle<byte, uint>((a, b) => a ^ b);
Handle<byte, long>((a, b) => a ^ b);
Handle<byte, ulong>((a, b) => a ^ b);
Handle<sbyte, byte>((a, b) => a ^ b);
Handle<sbyte, sbyte>((a, b) => a ^ b);
Handle<sbyte, short>((a, b) => a ^ b);
Handle<sbyte, ushort>((a, b) => a ^ b);
Handle<sbyte, int>((a, b) => a ^ b);
Handle<sbyte, uint>((a, b) => a ^ b);
Handle<sbyte, long>((a, b) => a ^ b);
//Handle<sbyte, ulong>((a, b) => a ^ b);
Handle<short, byte>((a, b) => a ^ b);
Handle<short, sbyte>((a, b) => a ^ b);
Handle<short, short>((a, b) => a ^ b);
Handle<short, ushort>((a, b) => a ^ b);
Handle<short, int>((a, b) => a ^ b);
Handle<short, uint>((a, b) => a ^ b);
Handle<short, long>((a, b) => a ^ b);
//Handle<short, ulong>((a, b) => a ^ b);
Handle<ushort, byte>((a, b) => a ^ b);
Handle<ushort, sbyte>((a, b) => a ^ b);
Handle<ushort, short>((a, b) => a ^ b);
Handle<ushort, ushort>((a, b) => a ^ b);
Handle<ushort, int>((a, b) => a ^ b);
Handle<ushort, uint>((a, b) => a ^ b);
Handle<ushort, long>((a, b) => a ^ b);
Handle<ushort, ulong>((a, b) => a ^ b);
Handle<int, byte>((a, b) => a ^ b);
Handle<int, sbyte>((a, b) => a ^ b);
Handle<int, short>((a, b) => a ^ b);
Handle<int, ushort>((a, b) => a ^ b);
Handle<int, int>((a, b) => a ^ b);
Handle<int, uint>((a, b) => a ^ b);
Handle<int, long>((a, b) => a ^ b);
//Handle<int, ulong>((a, b) => a ^ b);
Handle<uint, byte>((a, b) => a ^ b);
Handle<uint, sbyte>((a, b) => a ^ b);
Handle<uint, short>((a, b) => a ^ b);
Handle<uint, ushort>((a, b) => a ^ b);
Handle<uint, int>((a, b) => a ^ b);
Handle<uint, uint>((a, b) => a ^ b);
Handle<uint, long>((a, b) => a ^ b);
Handle<uint, ulong>((a, b) => a ^ b);
Handle<long, byte>((a, b) => a ^ b);
Handle<long, sbyte>((a, b) => a ^ b);
Handle<long, short>((a, b) => a ^ b);
Handle<long, ushort>((a, b) => a ^ b);
Handle<long, int>((a, b) => a ^ b);
Handle<long, uint>((a, b) => a ^ b);
Handle<long, long>((a, b) => a ^ b);
//Handle<long, ulong>((a, b) => a ^ b);
Handle<ulong, byte>((a, b) => a ^ b);
//Handle<ulong, sbyte>((a, b) => a ^ b);
//Handle<ulong, short>((a, b) => a ^ b);
Handle<ulong, ushort>((a, b) => a ^ b);
//Handle<ulong, int>((a, b) => a ^ b);
Handle<ulong, uint>((a, b) => a ^ b);
//Handle<ulong, long>((a, b) => a ^ b);
Handle<ulong, ulong>((a, b) => a ^ b);
}
}
}

View File

@@ -0,0 +1,140 @@
namespace Unity.VisualScripting
{
public class GreaterThanHandler : BinaryOperatorHandler
{
public GreaterThanHandler() : base("Greather Than", "Greather Than", ">", "op_GreaterThan")
{
Handle<byte, byte>((a, b) => a > b);
Handle<byte, sbyte>((a, b) => a > b);
Handle<byte, short>((a, b) => a > b);
Handle<byte, ushort>((a, b) => a > b);
Handle<byte, int>((a, b) => a > b);
Handle<byte, uint>((a, b) => a > b);
Handle<byte, long>((a, b) => a > b);
Handle<byte, ulong>((a, b) => a > b);
Handle<byte, float>((a, b) => a > b);
Handle<byte, decimal>((a, b) => a > b);
Handle<byte, double>((a, b) => a > b);
Handle<sbyte, byte>((a, b) => a > b);
Handle<sbyte, sbyte>((a, b) => a > b);
Handle<sbyte, short>((a, b) => a > b);
Handle<sbyte, ushort>((a, b) => a > b);
Handle<sbyte, int>((a, b) => a > b);
Handle<sbyte, uint>((a, b) => a > b);
Handle<sbyte, long>((a, b) => a > b);
//Handle<sbyte, ulong>((a, b) => a > b);
Handle<sbyte, float>((a, b) => a > b);
Handle<sbyte, decimal>((a, b) => a > b);
Handle<sbyte, double>((a, b) => a > b);
Handle<short, byte>((a, b) => a > b);
Handle<short, sbyte>((a, b) => a > b);
Handle<short, short>((a, b) => a > b);
Handle<short, ushort>((a, b) => a > b);
Handle<short, int>((a, b) => a > b);
Handle<short, uint>((a, b) => a > b);
Handle<short, long>((a, b) => a > b);
//Handle<short, ulong>((a, b) => a > b);
Handle<short, float>((a, b) => a > b);
Handle<short, decimal>((a, b) => a > b);
Handle<short, double>((a, b) => a > b);
Handle<ushort, byte>((a, b) => a > b);
Handle<ushort, sbyte>((a, b) => a > b);
Handle<ushort, short>((a, b) => a > b);
Handle<ushort, ushort>((a, b) => a > b);
Handle<ushort, int>((a, b) => a > b);
Handle<ushort, uint>((a, b) => a > b);
Handle<ushort, long>((a, b) => a > b);
Handle<ushort, ulong>((a, b) => a > b);
Handle<ushort, float>((a, b) => a > b);
Handle<ushort, decimal>((a, b) => a > b);
Handle<ushort, double>((a, b) => a > b);
Handle<int, byte>((a, b) => a > b);
Handle<int, sbyte>((a, b) => a > b);
Handle<int, short>((a, b) => a > b);
Handle<int, ushort>((a, b) => a > b);
Handle<int, int>((a, b) => a > b);
Handle<int, uint>((a, b) => a > b);
Handle<int, long>((a, b) => a > b);
//Handle<int, ulong>((a, b) => a > b);
Handle<int, float>((a, b) => a > b);
Handle<int, decimal>((a, b) => a > b);
Handle<int, double>((a, b) => a > b);
Handle<uint, byte>((a, b) => a > b);
Handle<uint, sbyte>((a, b) => a > b);
Handle<uint, short>((a, b) => a > b);
Handle<uint, ushort>((a, b) => a > b);
Handle<uint, int>((a, b) => a > b);
Handle<uint, uint>((a, b) => a > b);
Handle<uint, long>((a, b) => a > b);
Handle<uint, ulong>((a, b) => a > b);
Handle<uint, float>((a, b) => a > b);
Handle<uint, decimal>((a, b) => a > b);
Handle<uint, double>((a, b) => a > b);
Handle<long, byte>((a, b) => a > b);
Handle<long, sbyte>((a, b) => a > b);
Handle<long, short>((a, b) => a > b);
Handle<long, ushort>((a, b) => a > b);
Handle<long, int>((a, b) => a > b);
Handle<long, uint>((a, b) => a > b);
Handle<long, long>((a, b) => a > b);
//Handle<long, ulong>((a, b) => a > b);
Handle<long, float>((a, b) => a > b);
Handle<long, decimal>((a, b) => a > b);
Handle<long, double>((a, b) => a > b);
Handle<ulong, byte>((a, b) => a > b);
//Handle<ulong, sbyte>((a, b) => a > b);
//Handle<ulong, short>((a, b) => a > b);
Handle<ulong, ushort>((a, b) => a > b);
//Handle<ulong, int>((a, b) => a > b);
Handle<ulong, uint>((a, b) => a > b);
//Handle<ulong, long>((a, b) => a > b);
Handle<ulong, ulong>((a, b) => a > b);
Handle<ulong, float>((a, b) => a > b);
Handle<ulong, decimal>((a, b) => a > b);
Handle<ulong, double>((a, b) => a > b);
Handle<float, byte>((a, b) => a > b);
Handle<float, sbyte>((a, b) => a > b);
Handle<float, short>((a, b) => a > b);
Handle<float, ushort>((a, b) => a > b);
Handle<float, int>((a, b) => a > b);
Handle<float, uint>((a, b) => a > b);
Handle<float, long>((a, b) => a > b);
Handle<float, ulong>((a, b) => a > b);
Handle<float, float>((a, b) => a > b);
//Handle<float, decimal>((a, b) => a > b);
Handle<float, double>((a, b) => a > b);
Handle<decimal, byte>((a, b) => a > b);
Handle<decimal, sbyte>((a, b) => a > b);
Handle<decimal, short>((a, b) => a > b);
Handle<decimal, ushort>((a, b) => a > b);
Handle<decimal, int>((a, b) => a > b);
Handle<decimal, uint>((a, b) => a > b);
Handle<decimal, long>((a, b) => a > b);
Handle<decimal, ulong>((a, b) => a > b);
//Handle<decimal, float>((a, b) => a > b);
Handle<decimal, decimal>((a, b) => a > b);
//Handle<decimal, double>((a, b) => a > b);
Handle<double, byte>((a, b) => a > b);
Handle<double, sbyte>((a, b) => a > b);
Handle<double, short>((a, b) => a > b);
Handle<double, ushort>((a, b) => a > b);
Handle<double, int>((a, b) => a > b);
Handle<double, uint>((a, b) => a > b);
Handle<double, long>((a, b) => a > b);
Handle<double, ulong>((a, b) => a > b);
Handle<double, float>((a, b) => a > b);
//Handle<double, decimal>((a, b) => a > b);
Handle<double, double>((a, b) => a > b);
}
}
}

View File

@@ -0,0 +1,142 @@
using UnityEngine;
namespace Unity.VisualScripting
{
public class GreaterThanOrEqualHandler : BinaryOperatorHandler
{
public GreaterThanOrEqualHandler() : base("Greater Than Or Equal", "Greater Than Or Equal", ">=", "op_GreaterThanOrEqual")
{
Handle<byte, byte>((a, b) => a >= b);
Handle<byte, sbyte>((a, b) => a >= b);
Handle<byte, short>((a, b) => a >= b);
Handle<byte, ushort>((a, b) => a >= b);
Handle<byte, int>((a, b) => a >= b);
Handle<byte, uint>((a, b) => a >= b);
Handle<byte, long>((a, b) => a >= b);
Handle<byte, ulong>((a, b) => a >= b);
Handle<byte, float>((a, b) => a >= b);
Handle<byte, decimal>((a, b) => a >= b);
Handle<byte, double>((a, b) => a >= b);
Handle<sbyte, byte>((a, b) => a >= b);
Handle<sbyte, sbyte>((a, b) => a >= b);
Handle<sbyte, short>((a, b) => a >= b);
Handle<sbyte, ushort>((a, b) => a >= b);
Handle<sbyte, int>((a, b) => a >= b);
Handle<sbyte, uint>((a, b) => a >= b);
Handle<sbyte, long>((a, b) => a >= b);
//Handle<sbyte, ulong>((a, b) => a >= b);
Handle<sbyte, float>((a, b) => a >= b);
Handle<sbyte, decimal>((a, b) => a >= b);
Handle<sbyte, double>((a, b) => a >= b);
Handle<short, byte>((a, b) => a >= b);
Handle<short, sbyte>((a, b) => a >= b);
Handle<short, short>((a, b) => a >= b);
Handle<short, ushort>((a, b) => a >= b);
Handle<short, int>((a, b) => a >= b);
Handle<short, uint>((a, b) => a >= b);
Handle<short, long>((a, b) => a >= b);
//Handle<short, ulong>((a, b) => a >= b);
Handle<short, float>((a, b) => a >= b);
Handle<short, decimal>((a, b) => a >= b);
Handle<short, double>((a, b) => a >= b);
Handle<ushort, byte>((a, b) => a >= b);
Handle<ushort, sbyte>((a, b) => a >= b);
Handle<ushort, short>((a, b) => a >= b);
Handle<ushort, ushort>((a, b) => a >= b);
Handle<ushort, int>((a, b) => a >= b);
Handle<ushort, uint>((a, b) => a >= b);
Handle<ushort, long>((a, b) => a >= b);
Handle<ushort, ulong>((a, b) => a >= b);
Handle<ushort, float>((a, b) => a >= b);
Handle<ushort, decimal>((a, b) => a >= b);
Handle<ushort, double>((a, b) => a >= b);
Handle<int, byte>((a, b) => a >= b);
Handle<int, sbyte>((a, b) => a >= b);
Handle<int, short>((a, b) => a >= b);
Handle<int, ushort>((a, b) => a >= b);
Handle<int, int>((a, b) => a >= b);
Handle<int, uint>((a, b) => a >= b);
Handle<int, long>((a, b) => a >= b);
//Handle<int, ulong>((a, b) => a >= b);
Handle<int, float>((a, b) => a >= b);
Handle<int, decimal>((a, b) => a >= b);
Handle<int, double>((a, b) => a >= b);
Handle<uint, byte>((a, b) => a >= b);
Handle<uint, sbyte>((a, b) => a >= b);
Handle<uint, short>((a, b) => a >= b);
Handle<uint, ushort>((a, b) => a >= b);
Handle<uint, int>((a, b) => a >= b);
Handle<uint, uint>((a, b) => a >= b);
Handle<uint, long>((a, b) => a >= b);
Handle<uint, ulong>((a, b) => a >= b);
Handle<uint, float>((a, b) => a >= b);
Handle<uint, decimal>((a, b) => a >= b);
Handle<uint, double>((a, b) => a >= b);
Handle<long, byte>((a, b) => a >= b);
Handle<long, sbyte>((a, b) => a >= b);
Handle<long, short>((a, b) => a >= b);
Handle<long, ushort>((a, b) => a >= b);
Handle<long, int>((a, b) => a >= b);
Handle<long, uint>((a, b) => a >= b);
Handle<long, long>((a, b) => a >= b);
//Handle<long, ulong>((a, b) => a >= b);
Handle<long, float>((a, b) => a >= b);
Handle<long, decimal>((a, b) => a >= b);
Handle<long, double>((a, b) => a >= b);
Handle<ulong, byte>((a, b) => a >= b);
//Handle<ulong, sbyte>((a, b) => a >= b);
//Handle<ulong, short>((a, b) => a >= b);
Handle<ulong, ushort>((a, b) => a >= b);
//Handle<ulong, int>((a, b) => a >= b);
Handle<ulong, uint>((a, b) => a >= b);
//Handle<ulong, long>((a, b) => a >= b);
Handle<ulong, ulong>((a, b) => a >= b);
Handle<ulong, float>((a, b) => a >= b);
Handle<ulong, decimal>((a, b) => a >= b);
Handle<ulong, double>((a, b) => a >= b);
Handle<float, byte>((a, b) => a >= b);
Handle<float, sbyte>((a, b) => a >= b);
Handle<float, short>((a, b) => a >= b);
Handle<float, ushort>((a, b) => a >= b);
Handle<float, int>((a, b) => a >= b);
Handle<float, uint>((a, b) => a >= b);
Handle<float, long>((a, b) => a >= b);
Handle<float, ulong>((a, b) => a >= b);
Handle<float, float>((a, b) => a >= b || Mathf.Approximately(a, b));
//Handle<float, decimal>((a, b) => a >= b);
Handle<float, double>((a, b) => a >= b);
Handle<decimal, byte>((a, b) => a >= b);
Handle<decimal, sbyte>((a, b) => a >= b);
Handle<decimal, short>((a, b) => a >= b);
Handle<decimal, ushort>((a, b) => a >= b);
Handle<decimal, int>((a, b) => a >= b);
Handle<decimal, uint>((a, b) => a >= b);
Handle<decimal, long>((a, b) => a >= b);
Handle<decimal, ulong>((a, b) => a >= b);
//Handle<decimal, float>((a, b) => a >= b);
Handle<decimal, decimal>((a, b) => a >= b);
//Handle<decimal, double>((a, b) => a >= b);
Handle<double, byte>((a, b) => a >= b);
Handle<double, sbyte>((a, b) => a >= b);
Handle<double, short>((a, b) => a >= b);
Handle<double, ushort>((a, b) => a >= b);
Handle<double, int>((a, b) => a >= b);
Handle<double, uint>((a, b) => a >= b);
Handle<double, long>((a, b) => a >= b);
Handle<double, ulong>((a, b) => a >= b);
Handle<double, float>((a, b) => a >= b);
//Handle<double, decimal>((a, b) => a >= b);
Handle<double, double>((a, b) => a >= b);
}
}
}

View File

@@ -0,0 +1,20 @@
namespace Unity.VisualScripting
{
public sealed class IncrementHandler : UnaryOperatorHandler
{
public IncrementHandler() : base("Increment", "Increment", "++", "op_Increment")
{
Handle<byte>(a => ++a);
Handle<sbyte>(a => ++a);
Handle<short>(a => ++a);
Handle<ushort>(a => ++a);
Handle<int>(a => ++a);
Handle<uint>(a => ++a);
Handle<long>(a => ++a);
Handle<ulong>(a => ++a);
Handle<float>(a => ++a);
Handle<decimal>(a => ++a);
Handle<double>(a => ++a);
}
}
}

View File

@@ -0,0 +1,157 @@
using UnityEngine;
namespace Unity.VisualScripting
{
public class InequalityHandler : BinaryOperatorHandler
{
public InequalityHandler() : base("Inequality", "Not Equal", "!=", "op_Inequality")
{
Handle<byte, byte>((a, b) => a != b);
Handle<byte, sbyte>((a, b) => a != b);
Handle<byte, short>((a, b) => a != b);
Handle<byte, ushort>((a, b) => a != b);
Handle<byte, int>((a, b) => a != b);
Handle<byte, uint>((a, b) => a != b);
Handle<byte, long>((a, b) => a != b);
Handle<byte, ulong>((a, b) => a != b);
Handle<byte, float>((a, b) => a != b);
Handle<byte, decimal>((a, b) => a != b);
Handle<byte, double>((a, b) => a != b);
Handle<sbyte, byte>((a, b) => a != b);
Handle<sbyte, sbyte>((a, b) => a != b);
Handle<sbyte, short>((a, b) => a != b);
Handle<sbyte, ushort>((a, b) => a != b);
Handle<sbyte, int>((a, b) => a != b);
Handle<sbyte, uint>((a, b) => a != b);
Handle<sbyte, long>((a, b) => a != b);
//Handle<sbyte, ulong>((a, b) => a != b);
Handle<sbyte, float>((a, b) => a != b);
Handle<sbyte, decimal>((a, b) => a != b);
Handle<sbyte, double>((a, b) => a != b);
Handle<short, byte>((a, b) => a != b);
Handle<short, sbyte>((a, b) => a != b);
Handle<short, short>((a, b) => a != b);
Handle<short, ushort>((a, b) => a != b);
Handle<short, int>((a, b) => a != b);
Handle<short, uint>((a, b) => a != b);
Handle<short, long>((a, b) => a != b);
//Handle<short, ulong>((a, b) => a != b);
Handle<short, float>((a, b) => a != b);
Handle<short, decimal>((a, b) => a != b);
Handle<short, double>((a, b) => a != b);
Handle<ushort, byte>((a, b) => a != b);
Handle<ushort, sbyte>((a, b) => a != b);
Handle<ushort, short>((a, b) => a != b);
Handle<ushort, ushort>((a, b) => a != b);
Handle<ushort, int>((a, b) => a != b);
Handle<ushort, uint>((a, b) => a != b);
Handle<ushort, long>((a, b) => a != b);
Handle<ushort, ulong>((a, b) => a != b);
Handle<ushort, float>((a, b) => a != b);
Handle<ushort, decimal>((a, b) => a != b);
Handle<ushort, double>((a, b) => a != b);
Handle<int, byte>((a, b) => a != b);
Handle<int, sbyte>((a, b) => a != b);
Handle<int, short>((a, b) => a != b);
Handle<int, ushort>((a, b) => a != b);
Handle<int, int>((a, b) => a != b);
Handle<int, uint>((a, b) => a != b);
Handle<int, long>((a, b) => a != b);
//Handle<int, ulong>((a, b) => a != b);
Handle<int, float>((a, b) => a != b);
Handle<int, decimal>((a, b) => a != b);
Handle<int, double>((a, b) => a != b);
Handle<uint, byte>((a, b) => a != b);
Handle<uint, sbyte>((a, b) => a != b);
Handle<uint, short>((a, b) => a != b);
Handle<uint, ushort>((a, b) => a != b);
Handle<uint, int>((a, b) => a != b);
Handle<uint, uint>((a, b) => a != b);
Handle<uint, long>((a, b) => a != b);
Handle<uint, ulong>((a, b) => a != b);
Handle<uint, float>((a, b) => a != b);
Handle<uint, decimal>((a, b) => a != b);
Handle<uint, double>((a, b) => a != b);
Handle<long, byte>((a, b) => a != b);
Handle<long, sbyte>((a, b) => a != b);
Handle<long, short>((a, b) => a != b);
Handle<long, ushort>((a, b) => a != b);
Handle<long, int>((a, b) => a != b);
Handle<long, uint>((a, b) => a != b);
Handle<long, long>((a, b) => a != b);
//Handle<long, ulong>((a, b) => a != b);
Handle<long, float>((a, b) => a != b);
Handle<long, decimal>((a, b) => a != b);
Handle<long, double>((a, b) => a != b);
Handle<ulong, byte>((a, b) => a != b);
//Handle<ulong, sbyte>((a, b) => a != b);
//Handle<ulong, short>((a, b) => a != b);
Handle<ulong, ushort>((a, b) => a != b);
//Handle<ulong, int>((a, b) => a != b);
Handle<ulong, uint>((a, b) => a != b);
//Handle<ulong, long>((a, b) => a != b);
Handle<ulong, ulong>((a, b) => a != b);
Handle<ulong, float>((a, b) => a != b);
Handle<ulong, decimal>((a, b) => a != b);
Handle<ulong, double>((a, b) => a != b);
Handle<float, byte>((a, b) => a != b);
Handle<float, sbyte>((a, b) => a != b);
Handle<float, short>((a, b) => a != b);
Handle<float, ushort>((a, b) => a != b);
Handle<float, int>((a, b) => a != b);
Handle<float, uint>((a, b) => a != b);
Handle<float, long>((a, b) => a != b);
Handle<float, ulong>((a, b) => a != b);
Handle<float, float>((a, b) => !Mathf.Approximately(a, b));
//Handle<float, decimal>((a, b) => a != b);
Handle<float, double>((a, b) => a != b);
Handle<decimal, byte>((a, b) => a != b);
Handle<decimal, sbyte>((a, b) => a != b);
Handle<decimal, short>((a, b) => a != b);
Handle<decimal, ushort>((a, b) => a != b);
Handle<decimal, int>((a, b) => a != b);
Handle<decimal, uint>((a, b) => a != b);
Handle<decimal, long>((a, b) => a != b);
Handle<decimal, ulong>((a, b) => a != b);
//Handle<decimal, float>((a, b) => a != b);
Handle<decimal, decimal>((a, b) => a != b);
//Handle<decimal, double>((a, b) => a != b);
Handle<double, byte>((a, b) => a != b);
Handle<double, sbyte>((a, b) => a != b);
Handle<double, short>((a, b) => a != b);
Handle<double, ushort>((a, b) => a != b);
Handle<double, int>((a, b) => a != b);
Handle<double, uint>((a, b) => a != b);
Handle<double, long>((a, b) => a != b);
Handle<double, ulong>((a, b) => a != b);
Handle<double, float>((a, b) => a != b);
//Handle<double, decimal>((a, b) => a != b);
Handle<double, double>((a, b) => a != b);
}
protected override object BothNullHandling()
{
return false;
}
protected override object SingleNullHandling()
{
return false;
}
public override object Operate(object leftOperand, object rightOperand)
{
return !Equals(leftOperand, rightOperand);
}
}
}

View File

@@ -0,0 +1,10 @@
using System;
namespace Unity.VisualScripting
{
public sealed class InvalidOperatorException : OperatorException
{
public InvalidOperatorException(string symbol, Type type) : base($"Operator '{symbol}' cannot be applied to operand of type '{type?.ToString() ?? "null"}'.") { }
public InvalidOperatorException(string symbol, Type leftType, Type rightType) : base($"Operator '{symbol}' cannot be applied to operands of type '{leftType?.ToString() ?? "null"}' and '{rightType?.ToString() ?? "null"}'.") { }
}
}

View File

@@ -0,0 +1,56 @@
namespace Unity.VisualScripting
{
public class LeftShiftHandler : BinaryOperatorHandler
{
public LeftShiftHandler() : base("Left Shift", "Left Shift", "<<", "op_LeftShift")
{
Handle<byte, byte>((a, b) => a << b);
Handle<byte, sbyte>((a, b) => a << b);
Handle<byte, short>((a, b) => a << b);
Handle<byte, ushort>((a, b) => a << b);
Handle<byte, int>((a, b) => a << b);
Handle<sbyte, byte>((a, b) => a << b);
Handle<sbyte, sbyte>((a, b) => a << b);
Handle<sbyte, short>((a, b) => a << b);
Handle<sbyte, ushort>((a, b) => a << b);
Handle<sbyte, int>((a, b) => a << b);
Handle<short, byte>((a, b) => a << b);
Handle<short, sbyte>((a, b) => a << b);
Handle<short, short>((a, b) => a << b);
Handle<short, ushort>((a, b) => a << b);
Handle<short, int>((a, b) => a << b);
Handle<ushort, byte>((a, b) => a << b);
Handle<ushort, sbyte>((a, b) => a << b);
Handle<ushort, short>((a, b) => a << b);
Handle<ushort, ushort>((a, b) => a << b);
Handle<ushort, int>((a, b) => a << b);
Handle<int, byte>((a, b) => a << b);
Handle<int, sbyte>((a, b) => a << b);
Handle<int, short>((a, b) => a << b);
Handle<int, ushort>((a, b) => a << b);
Handle<int, int>((a, b) => a << b);
Handle<uint, byte>((a, b) => a << b);
Handle<uint, sbyte>((a, b) => a << b);
Handle<uint, short>((a, b) => a << b);
Handle<uint, ushort>((a, b) => a << b);
Handle<uint, int>((a, b) => a << b);
Handle<long, byte>((a, b) => a << b);
Handle<long, sbyte>((a, b) => a << b);
Handle<long, short>((a, b) => a << b);
Handle<long, ushort>((a, b) => a << b);
Handle<long, int>((a, b) => a << b);
Handle<ulong, byte>((a, b) => a << b);
Handle<ulong, sbyte>((a, b) => a << b);
Handle<ulong, short>((a, b) => a << b);
Handle<ulong, ushort>((a, b) => a << b);
Handle<ulong, int>((a, b) => a << b);
}
}
}

View File

@@ -0,0 +1,140 @@
namespace Unity.VisualScripting
{
public class LessThanHandler : BinaryOperatorHandler
{
public LessThanHandler() : base("Less Than", "Less Than", "<", "op_LessThan")
{
Handle<byte, byte>((a, b) => a < b);
Handle<byte, sbyte>((a, b) => a < b);
Handle<byte, short>((a, b) => a < b);
Handle<byte, ushort>((a, b) => a < b);
Handle<byte, int>((a, b) => a < b);
Handle<byte, uint>((a, b) => a < b);
Handle<byte, long>((a, b) => a < b);
Handle<byte, ulong>((a, b) => a < b);
Handle<byte, float>((a, b) => a < b);
Handle<byte, decimal>((a, b) => a < b);
Handle<byte, double>((a, b) => a < b);
Handle<sbyte, byte>((a, b) => a < b);
Handle<sbyte, sbyte>((a, b) => a < b);
Handle<sbyte, short>((a, b) => a < b);
Handle<sbyte, ushort>((a, b) => a < b);
Handle<sbyte, int>((a, b) => a < b);
Handle<sbyte, uint>((a, b) => a < b);
Handle<sbyte, long>((a, b) => a < b);
//Handle<sbyte, ulong>((a, b) => a < b);
Handle<sbyte, float>((a, b) => a < b);
Handle<sbyte, decimal>((a, b) => a < b);
Handle<sbyte, double>((a, b) => a < b);
Handle<short, byte>((a, b) => a < b);
Handle<short, sbyte>((a, b) => a < b);
Handle<short, short>((a, b) => a < b);
Handle<short, ushort>((a, b) => a < b);
Handle<short, int>((a, b) => a < b);
Handle<short, uint>((a, b) => a < b);
Handle<short, long>((a, b) => a < b);
//Handle<short, ulong>((a, b) => a < b);
Handle<short, float>((a, b) => a < b);
Handle<short, decimal>((a, b) => a < b);
Handle<short, double>((a, b) => a < b);
Handle<ushort, byte>((a, b) => a < b);
Handle<ushort, sbyte>((a, b) => a < b);
Handle<ushort, short>((a, b) => a < b);
Handle<ushort, ushort>((a, b) => a < b);
Handle<ushort, int>((a, b) => a < b);
Handle<ushort, uint>((a, b) => a < b);
Handle<ushort, long>((a, b) => a < b);
Handle<ushort, ulong>((a, b) => a < b);
Handle<ushort, float>((a, b) => a < b);
Handle<ushort, decimal>((a, b) => a < b);
Handle<ushort, double>((a, b) => a < b);
Handle<int, byte>((a, b) => a < b);
Handle<int, sbyte>((a, b) => a < b);
Handle<int, short>((a, b) => a < b);
Handle<int, ushort>((a, b) => a < b);
Handle<int, int>((a, b) => a < b);
Handle<int, uint>((a, b) => a < b);
Handle<int, long>((a, b) => a < b);
//Handle<int, ulong>((a, b) => a < b);
Handle<int, float>((a, b) => a < b);
Handle<int, decimal>((a, b) => a < b);
Handle<int, double>((a, b) => a < b);
Handle<uint, byte>((a, b) => a < b);
Handle<uint, sbyte>((a, b) => a < b);
Handle<uint, short>((a, b) => a < b);
Handle<uint, ushort>((a, b) => a < b);
Handle<uint, int>((a, b) => a < b);
Handle<uint, uint>((a, b) => a < b);
Handle<uint, long>((a, b) => a < b);
Handle<uint, ulong>((a, b) => a < b);
Handle<uint, float>((a, b) => a < b);
Handle<uint, decimal>((a, b) => a < b);
Handle<uint, double>((a, b) => a < b);
Handle<long, byte>((a, b) => a < b);
Handle<long, sbyte>((a, b) => a < b);
Handle<long, short>((a, b) => a < b);
Handle<long, ushort>((a, b) => a < b);
Handle<long, int>((a, b) => a < b);
Handle<long, uint>((a, b) => a < b);
Handle<long, long>((a, b) => a < b);
//Handle<long, ulong>((a, b) => a < b);
Handle<long, float>((a, b) => a < b);
Handle<long, decimal>((a, b) => a < b);
Handle<long, double>((a, b) => a < b);
Handle<ulong, byte>((a, b) => a < b);
//Handle<ulong, sbyte>((a, b) => a < b);
//Handle<ulong, short>((a, b) => a < b);
Handle<ulong, ushort>((a, b) => a < b);
//Handle<ulong, int>((a, b) => a < b);
Handle<ulong, uint>((a, b) => a < b);
//Handle<ulong, long>((a, b) => a < b);
Handle<ulong, ulong>((a, b) => a < b);
Handle<ulong, float>((a, b) => a < b);
Handle<ulong, decimal>((a, b) => a < b);
Handle<ulong, double>((a, b) => a < b);
Handle<float, byte>((a, b) => a < b);
Handle<float, sbyte>((a, b) => a < b);
Handle<float, short>((a, b) => a < b);
Handle<float, ushort>((a, b) => a < b);
Handle<float, int>((a, b) => a < b);
Handle<float, uint>((a, b) => a < b);
Handle<float, long>((a, b) => a < b);
Handle<float, ulong>((a, b) => a < b);
Handle<float, float>((a, b) => a < b);
//Handle<float, decimal>((a, b) => a < b);
Handle<float, double>((a, b) => a < b);
Handle<decimal, byte>((a, b) => a < b);
Handle<decimal, sbyte>((a, b) => a < b);
Handle<decimal, short>((a, b) => a < b);
Handle<decimal, ushort>((a, b) => a < b);
Handle<decimal, int>((a, b) => a < b);
Handle<decimal, uint>((a, b) => a < b);
Handle<decimal, long>((a, b) => a < b);
Handle<decimal, ulong>((a, b) => a < b);
//Handle<decimal, float>((a, b) => a < b);
Handle<decimal, decimal>((a, b) => a < b);
//Handle<decimal, double>((a, b) => a < b);
Handle<double, byte>((a, b) => a < b);
Handle<double, sbyte>((a, b) => a < b);
Handle<double, short>((a, b) => a < b);
Handle<double, ushort>((a, b) => a < b);
Handle<double, int>((a, b) => a < b);
Handle<double, uint>((a, b) => a < b);
Handle<double, long>((a, b) => a < b);
Handle<double, ulong>((a, b) => a < b);
Handle<double, float>((a, b) => a < b);
//Handle<double, decimal>((a, b) => a < b);
Handle<double, double>((a, b) => a < b);
}
}
}

View File

@@ -0,0 +1,142 @@
using UnityEngine;
namespace Unity.VisualScripting
{
public class LessThanOrEqualHandler : BinaryOperatorHandler
{
public LessThanOrEqualHandler() : base("Less Than Or Equal", "Less Than Or Equal", ">=", "op_LessThanOrEqual")
{
Handle<byte, byte>((a, b) => a <= b);
Handle<byte, sbyte>((a, b) => a <= b);
Handle<byte, short>((a, b) => a <= b);
Handle<byte, ushort>((a, b) => a <= b);
Handle<byte, int>((a, b) => a <= b);
Handle<byte, uint>((a, b) => a <= b);
Handle<byte, long>((a, b) => a <= b);
Handle<byte, ulong>((a, b) => a <= b);
Handle<byte, float>((a, b) => a <= b);
Handle<byte, decimal>((a, b) => a <= b);
Handle<byte, double>((a, b) => a <= b);
Handle<sbyte, byte>((a, b) => a <= b);
Handle<sbyte, sbyte>((a, b) => a <= b);
Handle<sbyte, short>((a, b) => a <= b);
Handle<sbyte, ushort>((a, b) => a <= b);
Handle<sbyte, int>((a, b) => a <= b);
Handle<sbyte, uint>((a, b) => a <= b);
Handle<sbyte, long>((a, b) => a <= b);
//Handle<sbyte, ulong>((a, b) => a <= b);
Handle<sbyte, float>((a, b) => a <= b);
Handle<sbyte, decimal>((a, b) => a <= b);
Handle<sbyte, double>((a, b) => a <= b);
Handle<short, byte>((a, b) => a <= b);
Handle<short, sbyte>((a, b) => a <= b);
Handle<short, short>((a, b) => a <= b);
Handle<short, ushort>((a, b) => a <= b);
Handle<short, int>((a, b) => a <= b);
Handle<short, uint>((a, b) => a <= b);
Handle<short, long>((a, b) => a <= b);
//Handle<short, ulong>((a, b) => a <= b);
Handle<short, float>((a, b) => a <= b);
Handle<short, decimal>((a, b) => a <= b);
Handle<short, double>((a, b) => a <= b);
Handle<ushort, byte>((a, b) => a <= b);
Handle<ushort, sbyte>((a, b) => a <= b);
Handle<ushort, short>((a, b) => a <= b);
Handle<ushort, ushort>((a, b) => a <= b);
Handle<ushort, int>((a, b) => a <= b);
Handle<ushort, uint>((a, b) => a <= b);
Handle<ushort, long>((a, b) => a <= b);
Handle<ushort, ulong>((a, b) => a <= b);
Handle<ushort, float>((a, b) => a <= b);
Handle<ushort, decimal>((a, b) => a <= b);
Handle<ushort, double>((a, b) => a <= b);
Handle<int, byte>((a, b) => a <= b);
Handle<int, sbyte>((a, b) => a <= b);
Handle<int, short>((a, b) => a <= b);
Handle<int, ushort>((a, b) => a <= b);
Handle<int, int>((a, b) => a <= b);
Handle<int, uint>((a, b) => a <= b);
Handle<int, long>((a, b) => a <= b);
//Handle<int, ulong>((a, b) => a <= b);
Handle<int, float>((a, b) => a <= b);
Handle<int, decimal>((a, b) => a <= b);
Handle<int, double>((a, b) => a <= b);
Handle<uint, byte>((a, b) => a <= b);
Handle<uint, sbyte>((a, b) => a <= b);
Handle<uint, short>((a, b) => a <= b);
Handle<uint, ushort>((a, b) => a <= b);
Handle<uint, int>((a, b) => a <= b);
Handle<uint, uint>((a, b) => a <= b);
Handle<uint, long>((a, b) => a <= b);
Handle<uint, ulong>((a, b) => a <= b);
Handle<uint, float>((a, b) => a <= b);
Handle<uint, decimal>((a, b) => a <= b);
Handle<uint, double>((a, b) => a <= b);
Handle<long, byte>((a, b) => a <= b);
Handle<long, sbyte>((a, b) => a <= b);
Handle<long, short>((a, b) => a <= b);
Handle<long, ushort>((a, b) => a <= b);
Handle<long, int>((a, b) => a <= b);
Handle<long, uint>((a, b) => a <= b);
Handle<long, long>((a, b) => a <= b);
//Handle<long, ulong>((a, b) => a <= b);
Handle<long, float>((a, b) => a <= b);
Handle<long, decimal>((a, b) => a <= b);
Handle<long, double>((a, b) => a <= b);
Handle<ulong, byte>((a, b) => a <= b);
//Handle<ulong, sbyte>((a, b) => a <= b);
//Handle<ulong, short>((a, b) => a <= b);
Handle<ulong, ushort>((a, b) => a <= b);
//Handle<ulong, int>((a, b) => a <= b);
Handle<ulong, uint>((a, b) => a <= b);
//Handle<ulong, long>((a, b) => a <= b);
Handle<ulong, ulong>((a, b) => a <= b);
Handle<ulong, float>((a, b) => a <= b);
Handle<ulong, decimal>((a, b) => a <= b);
Handle<ulong, double>((a, b) => a <= b);
Handle<float, byte>((a, b) => a <= b);
Handle<float, sbyte>((a, b) => a <= b);
Handle<float, short>((a, b) => a <= b);
Handle<float, ushort>((a, b) => a <= b);
Handle<float, int>((a, b) => a <= b);
Handle<float, uint>((a, b) => a <= b);
Handle<float, long>((a, b) => a <= b);
Handle<float, ulong>((a, b) => a <= b);
Handle<float, float>((a, b) => a <= b || Mathf.Approximately(a, b));
//Handle<float, decimal>((a, b) => a <= b);
Handle<float, double>((a, b) => a <= b);
Handle<decimal, byte>((a, b) => a <= b);
Handle<decimal, sbyte>((a, b) => a <= b);
Handle<decimal, short>((a, b) => a <= b);
Handle<decimal, ushort>((a, b) => a <= b);
Handle<decimal, int>((a, b) => a <= b);
Handle<decimal, uint>((a, b) => a <= b);
Handle<decimal, long>((a, b) => a <= b);
Handle<decimal, ulong>((a, b) => a <= b);
//Handle<decimal, float>((a, b) => a <= b);
Handle<decimal, decimal>((a, b) => a <= b);
//Handle<decimal, double>((a, b) => a <= b);
Handle<double, byte>((a, b) => a <= b);
Handle<double, sbyte>((a, b) => a <= b);
Handle<double, short>((a, b) => a <= b);
Handle<double, ushort>((a, b) => a <= b);
Handle<double, int>((a, b) => a <= b);
Handle<double, uint>((a, b) => a <= b);
Handle<double, long>((a, b) => a <= b);
Handle<double, ulong>((a, b) => a <= b);
Handle<double, float>((a, b) => a <= b);
//Handle<double, decimal>((a, b) => a <= b);
Handle<double, double>((a, b) => a <= b);
}
}
}

View File

@@ -0,0 +1,18 @@
namespace Unity.VisualScripting
{
public sealed class LogicalNegationHandler : UnaryOperatorHandler
{
public LogicalNegationHandler() : base("Logical Negation", "Not", "~", "op_OnesComplement")
{
Handle<bool>(a => !a);
Handle<byte>(a => ~a);
Handle<sbyte>(a => ~a);
Handle<short>(a => ~a);
Handle<ushort>(a => ~a);
Handle<int>(a => ~a);
Handle<uint>(a => ~a);
Handle<long>(a => ~a);
Handle<ulong>(a => ~a);
}
}
}

View File

@@ -0,0 +1,140 @@
namespace Unity.VisualScripting
{
public sealed class ModuloHandler : BinaryOperatorHandler
{
public ModuloHandler() : base("Modulo", "Modulo", "%", "op_Modulus")
{
Handle<byte, byte>((a, b) => a % b);
Handle<byte, sbyte>((a, b) => a % b);
Handle<byte, short>((a, b) => a % b);
Handle<byte, ushort>((a, b) => a % b);
Handle<byte, int>((a, b) => a % b);
Handle<byte, uint>((a, b) => a % b);
Handle<byte, long>((a, b) => a % b);
Handle<byte, ulong>((a, b) => a % b);
Handle<byte, float>((a, b) => a % b);
Handle<byte, decimal>((a, b) => a % b);
Handle<byte, double>((a, b) => a % b);
Handle<sbyte, byte>((a, b) => a % b);
Handle<sbyte, sbyte>((a, b) => a % b);
Handle<sbyte, short>((a, b) => a % b);
Handle<sbyte, ushort>((a, b) => a % b);
Handle<sbyte, int>((a, b) => a % b);
Handle<sbyte, uint>((a, b) => a % b);
Handle<sbyte, long>((a, b) => a % b);
//Handle<sbyte, ulong>((a, b) => a % b);
Handle<sbyte, float>((a, b) => a % b);
Handle<sbyte, decimal>((a, b) => a % b);
Handle<sbyte, double>((a, b) => a % b);
Handle<short, byte>((a, b) => a % b);
Handle<short, sbyte>((a, b) => a % b);
Handle<short, short>((a, b) => a % b);
Handle<short, ushort>((a, b) => a % b);
Handle<short, int>((a, b) => a % b);
Handle<short, uint>((a, b) => a % b);
Handle<short, long>((a, b) => a % b);
//Handle<short, ulong>((a, b) => a % b);
Handle<short, float>((a, b) => a % b);
Handle<short, decimal>((a, b) => a % b);
Handle<short, double>((a, b) => a % b);
Handle<ushort, byte>((a, b) => a % b);
Handle<ushort, sbyte>((a, b) => a % b);
Handle<ushort, short>((a, b) => a % b);
Handle<ushort, ushort>((a, b) => a % b);
Handle<ushort, int>((a, b) => a % b);
Handle<ushort, uint>((a, b) => a % b);
Handle<ushort, long>((a, b) => a % b);
Handle<ushort, ulong>((a, b) => a % b);
Handle<ushort, float>((a, b) => a % b);
Handle<ushort, decimal>((a, b) => a % b);
Handle<ushort, double>((a, b) => a % b);
Handle<int, byte>((a, b) => a % b);
Handle<int, sbyte>((a, b) => a % b);
Handle<int, short>((a, b) => a % b);
Handle<int, ushort>((a, b) => a % b);
Handle<int, int>((a, b) => a % b);
Handle<int, uint>((a, b) => a % b);
Handle<int, long>((a, b) => a % b);
//Handle<int, ulong>((a, b) => a % b);
Handle<int, float>((a, b) => a % b);
Handle<int, decimal>((a, b) => a % b);
Handle<int, double>((a, b) => a % b);
Handle<uint, byte>((a, b) => a % b);
Handle<uint, sbyte>((a, b) => a % b);
Handle<uint, short>((a, b) => a % b);
Handle<uint, ushort>((a, b) => a % b);
Handle<uint, int>((a, b) => a % b);
Handle<uint, uint>((a, b) => a % b);
Handle<uint, long>((a, b) => a % b);
Handle<uint, ulong>((a, b) => a % b);
Handle<uint, float>((a, b) => a % b);
Handle<uint, decimal>((a, b) => a % b);
Handle<uint, double>((a, b) => a % b);
Handle<long, byte>((a, b) => a % b);
Handle<long, sbyte>((a, b) => a % b);
Handle<long, short>((a, b) => a % b);
Handle<long, ushort>((a, b) => a % b);
Handle<long, int>((a, b) => a % b);
Handle<long, uint>((a, b) => a % b);
Handle<long, long>((a, b) => a % b);
//Handle<long, ulong>((a, b) => a % b);
Handle<long, float>((a, b) => a % b);
Handle<long, decimal>((a, b) => a % b);
Handle<long, double>((a, b) => a % b);
Handle<ulong, byte>((a, b) => a % b);
//Handle<ulong, sbyte>((a, b) => a % b);
//Handle<ulong, short>((a, b) => a % b);
Handle<ulong, ushort>((a, b) => a % b);
//Handle<ulong, int>((a, b) => a % b);
Handle<ulong, uint>((a, b) => a % b);
//Handle<ulong, long>((a, b) => a % b);
Handle<ulong, ulong>((a, b) => a % b);
Handle<ulong, float>((a, b) => a % b);
Handle<ulong, decimal>((a, b) => a % b);
Handle<ulong, double>((a, b) => a % b);
Handle<float, byte>((a, b) => a % b);
Handle<float, sbyte>((a, b) => a % b);
Handle<float, short>((a, b) => a % b);
Handle<float, ushort>((a, b) => a % b);
Handle<float, int>((a, b) => a % b);
Handle<float, uint>((a, b) => a % b);
Handle<float, long>((a, b) => a % b);
Handle<float, ulong>((a, b) => a % b);
Handle<float, float>((a, b) => a % b);
//Handle<float, decimal>((a, b) => a % b);
Handle<float, double>((a, b) => a % b);
Handle<decimal, byte>((a, b) => a % b);
Handle<decimal, sbyte>((a, b) => a % b);
Handle<decimal, short>((a, b) => a % b);
Handle<decimal, ushort>((a, b) => a % b);
Handle<decimal, int>((a, b) => a % b);
Handle<decimal, uint>((a, b) => a % b);
Handle<decimal, long>((a, b) => a % b);
Handle<decimal, ulong>((a, b) => a % b);
//Handle<decimal, float>((a, b) => a % b);
Handle<decimal, decimal>((a, b) => a % b);
//Handle<decimal, double>((a, b) => a % b);
Handle<double, byte>((a, b) => a % b);
Handle<double, sbyte>((a, b) => a % b);
Handle<double, short>((a, b) => a % b);
Handle<double, ushort>((a, b) => a % b);
Handle<double, int>((a, b) => a % b);
Handle<double, uint>((a, b) => a % b);
Handle<double, long>((a, b) => a % b);
Handle<double, ulong>((a, b) => a % b);
Handle<double, float>((a, b) => a % b);
//Handle<double, decimal>((a, b) => a % b);
Handle<double, double>((a, b) => a % b);
}
}
}

View File

@@ -0,0 +1,140 @@
namespace Unity.VisualScripting
{
public sealed class MultiplicationHandler : BinaryOperatorHandler
{
public MultiplicationHandler() : base("Multiplication", "Multiply", "*", "op_Multiply")
{
Handle<byte, byte>((a, b) => a * b);
Handle<byte, sbyte>((a, b) => a * b);
Handle<byte, short>((a, b) => a * b);
Handle<byte, ushort>((a, b) => a * b);
Handle<byte, int>((a, b) => a * b);
Handle<byte, uint>((a, b) => a * b);
Handle<byte, long>((a, b) => a * b);
Handle<byte, ulong>((a, b) => a * b);
Handle<byte, float>((a, b) => a * b);
Handle<byte, decimal>((a, b) => a * b);
Handle<byte, double>((a, b) => a * b);
Handle<sbyte, byte>((a, b) => a * b);
Handle<sbyte, sbyte>((a, b) => a * b);
Handle<sbyte, short>((a, b) => a * b);
Handle<sbyte, ushort>((a, b) => a * b);
Handle<sbyte, int>((a, b) => a * b);
Handle<sbyte, uint>((a, b) => a * b);
Handle<sbyte, long>((a, b) => a * b);
//Handle<sbyte, ulong>((a, b) => a * b);
Handle<sbyte, float>((a, b) => a * b);
Handle<sbyte, decimal>((a, b) => a * b);
Handle<sbyte, double>((a, b) => a * b);
Handle<short, byte>((a, b) => a * b);
Handle<short, sbyte>((a, b) => a * b);
Handle<short, short>((a, b) => a * b);
Handle<short, ushort>((a, b) => a * b);
Handle<short, int>((a, b) => a * b);
Handle<short, uint>((a, b) => a * b);
Handle<short, long>((a, b) => a * b);
//Handle<short, ulong>((a, b) => a * b);
Handle<short, float>((a, b) => a * b);
Handle<short, decimal>((a, b) => a * b);
Handle<short, double>((a, b) => a * b);
Handle<ushort, byte>((a, b) => a * b);
Handle<ushort, sbyte>((a, b) => a * b);
Handle<ushort, short>((a, b) => a * b);
Handle<ushort, ushort>((a, b) => a * b);
Handle<ushort, int>((a, b) => a * b);
Handle<ushort, uint>((a, b) => a * b);
Handle<ushort, long>((a, b) => a * b);
Handle<ushort, ulong>((a, b) => a * b);
Handle<ushort, float>((a, b) => a * b);
Handle<ushort, decimal>((a, b) => a * b);
Handle<ushort, double>((a, b) => a * b);
Handle<int, byte>((a, b) => a * b);
Handle<int, sbyte>((a, b) => a * b);
Handle<int, short>((a, b) => a * b);
Handle<int, ushort>((a, b) => a * b);
Handle<int, int>((a, b) => a * b);
Handle<int, uint>((a, b) => a * b);
Handle<int, long>((a, b) => a * b);
//Handle<int, ulong>((a, b) => a * b);
Handle<int, float>((a, b) => a * b);
Handle<int, decimal>((a, b) => a * b);
Handle<int, double>((a, b) => a * b);
Handle<uint, byte>((a, b) => a * b);
Handle<uint, sbyte>((a, b) => a * b);
Handle<uint, short>((a, b) => a * b);
Handle<uint, ushort>((a, b) => a * b);
Handle<uint, int>((a, b) => a * b);
Handle<uint, uint>((a, b) => a * b);
Handle<uint, long>((a, b) => a * b);
Handle<uint, ulong>((a, b) => a * b);
Handle<uint, float>((a, b) => a * b);
Handle<uint, decimal>((a, b) => a * b);
Handle<uint, double>((a, b) => a * b);
Handle<long, byte>((a, b) => a * b);
Handle<long, sbyte>((a, b) => a * b);
Handle<long, short>((a, b) => a * b);
Handle<long, ushort>((a, b) => a * b);
Handle<long, int>((a, b) => a * b);
Handle<long, uint>((a, b) => a * b);
Handle<long, long>((a, b) => a * b);
//Handle<long, ulong>((a, b) => a * b);
Handle<long, float>((a, b) => a * b);
Handle<long, decimal>((a, b) => a * b);
Handle<long, double>((a, b) => a * b);
Handle<ulong, byte>((a, b) => a * b);
//Handle<ulong, sbyte>((a, b) => a * b);
//Handle<ulong, short>((a, b) => a * b);
Handle<ulong, ushort>((a, b) => a * b);
//Handle<ulong, int>((a, b) => a * b);
Handle<ulong, uint>((a, b) => a * b);
//Handle<ulong, long>((a, b) => a * b);
Handle<ulong, ulong>((a, b) => a * b);
Handle<ulong, float>((a, b) => a * b);
Handle<ulong, decimal>((a, b) => a * b);
Handle<ulong, double>((a, b) => a * b);
Handle<float, byte>((a, b) => a * b);
Handle<float, sbyte>((a, b) => a * b);
Handle<float, short>((a, b) => a * b);
Handle<float, ushort>((a, b) => a * b);
Handle<float, int>((a, b) => a * b);
Handle<float, uint>((a, b) => a * b);
Handle<float, long>((a, b) => a * b);
Handle<float, ulong>((a, b) => a * b);
Handle<float, float>((a, b) => a * b);
//Handle<float, decimal>((a, b) => a * b);
Handle<float, double>((a, b) => a * b);
Handle<decimal, byte>((a, b) => a * b);
Handle<decimal, sbyte>((a, b) => a * b);
Handle<decimal, short>((a, b) => a * b);
Handle<decimal, ushort>((a, b) => a * b);
Handle<decimal, int>((a, b) => a * b);
Handle<decimal, uint>((a, b) => a * b);
Handle<decimal, long>((a, b) => a * b);
Handle<decimal, ulong>((a, b) => a * b);
//Handle<decimal, float>((a, b) => a * b);
Handle<decimal, decimal>((a, b) => a * b);
//Handle<decimal, double>((a, b) => a * b);
Handle<double, byte>((a, b) => a * b);
Handle<double, sbyte>((a, b) => a * b);
Handle<double, short>((a, b) => a * b);
Handle<double, ushort>((a, b) => a * b);
Handle<double, int>((a, b) => a * b);
Handle<double, uint>((a, b) => a * b);
Handle<double, long>((a, b) => a * b);
Handle<double, ulong>((a, b) => a * b);
Handle<double, float>((a, b) => a * b);
//Handle<double, decimal>((a, b) => a * b);
Handle<double, double>((a, b) => a * b);
}
}
}

View File

@@ -0,0 +1,20 @@
namespace Unity.VisualScripting
{
public sealed class NumericNegationHandler : UnaryOperatorHandler
{
public NumericNegationHandler() : base("Numeric Negation", "Negate", "-", "op_UnaryNegation")
{
Handle<byte>(a => -a);
Handle<sbyte>(a => -a);
Handle<short>(a => -a);
Handle<ushort>(a => -a);
Handle<int>(a => -a);
Handle<uint>(a => -a);
Handle<long>(a => -a);
//Handle<ulong>(a => -a);
Handle<float>(a => -a);
Handle<decimal>(a => -a);
Handle<double>(a => -a);
}
}
}

View File

@@ -0,0 +1,11 @@
using System;
namespace Unity.VisualScripting
{
public abstract class OperatorException : InvalidCastException
{
protected OperatorException() : base() { }
protected OperatorException(string message) : base(message) { }
protected OperatorException(string message, Exception innerException) : base(message, innerException) { }
}
}

View File

@@ -0,0 +1,22 @@
namespace Unity.VisualScripting
{
public abstract class OperatorHandler
{
protected OperatorHandler(string name, string verb, string symbol, string customMethodName)
{
Ensure.That(nameof(name)).IsNotNull(name);
Ensure.That(nameof(verb)).IsNotNull(verb);
Ensure.That(nameof(symbol)).IsNotNull(symbol);
this.name = name;
this.verb = verb;
this.symbol = symbol;
this.customMethodName = customMethodName;
}
public string name { get; }
public string verb { get; }
public string symbol { get; }
public string customMethodName { get; }
}
}

View File

@@ -0,0 +1,311 @@
using System.Collections.Generic;
namespace Unity.VisualScripting
{
public static class OperatorUtility
{
static OperatorUtility()
{
unaryOperatorHandlers.Add(UnaryOperator.LogicalNegation, logicalNegationHandler);
unaryOperatorHandlers.Add(UnaryOperator.NumericNegation, numericNegationHandler);
unaryOperatorHandlers.Add(UnaryOperator.Increment, incrementHandler);
unaryOperatorHandlers.Add(UnaryOperator.Decrement, decrementHandler);
unaryOperatorHandlers.Add(UnaryOperator.Plus, plusHandler);
binaryOpeatorHandlers.Add(BinaryOperator.Addition, additionHandler);
binaryOpeatorHandlers.Add(BinaryOperator.Subtraction, subtractionHandler);
binaryOpeatorHandlers.Add(BinaryOperator.Multiplication, multiplicationHandler);
binaryOpeatorHandlers.Add(BinaryOperator.Division, divisionHandler);
binaryOpeatorHandlers.Add(BinaryOperator.Modulo, moduloHandler);
binaryOpeatorHandlers.Add(BinaryOperator.And, andHandler);
binaryOpeatorHandlers.Add(BinaryOperator.Or, orHandler);
binaryOpeatorHandlers.Add(BinaryOperator.ExclusiveOr, exclusiveOrHandler);
binaryOpeatorHandlers.Add(BinaryOperator.Equality, equalityHandler);
binaryOpeatorHandlers.Add(BinaryOperator.Inequality, inequalityHandler);
binaryOpeatorHandlers.Add(BinaryOperator.GreaterThan, greaterThanHandler);
binaryOpeatorHandlers.Add(BinaryOperator.LessThan, lessThanHandler);
binaryOpeatorHandlers.Add(BinaryOperator.GreaterThanOrEqual, greaterThanOrEqualHandler);
binaryOpeatorHandlers.Add(BinaryOperator.LessThanOrEqual, lessThanOrEqualHandler);
binaryOpeatorHandlers.Add(BinaryOperator.LeftShift, leftShiftHandler);
binaryOpeatorHandlers.Add(BinaryOperator.RightShift, rightShiftHandler);
}
// https://msdn.microsoft.com/en-us/library/2sk3x8a7(v=vs.71).aspx
public static readonly Dictionary<string, string> operatorNames = new Dictionary<string, string>
{
{ "op_Addition", "+" },
{ "op_Subtraction", "-" },
{ "op_Multiply", "*" },
{ "op_Division", "/" },
{ "op_Modulus", "%" },
{ "op_ExclusiveOr", "^" },
{ "op_BitwiseAnd", "&" },
{ "op_BitwiseOr", "|" },
{ "op_LogicalAnd", "&&" },
{ "op_LogicalOr", "||" },
{ "op_Assign", "=" },
{ "op_LeftShift", "<<" },
{ "op_RightShift", ">>" },
{ "op_Equality", "==" },
{ "op_GreaterThan", ">" },
{ "op_LessThan", "<" },
{ "op_Inequality", "!=" },
{ "op_GreaterThanOrEqual", ">=" },
{ "op_LessThanOrEqual", "<=" },
{ "op_MultiplicationAssignment", "*=" },
{ "op_SubtractionAssignment", "-=" },
{ "op_ExclusiveOrAssignment", "^=" },
{ "op_LeftShiftAssignment", "<<=" },
{ "op_ModulusAssignment", "%=" },
{ "op_AdditionAssignment", "+=" },
{ "op_BitwiseAndAssignment", "&=" },
{ "op_BitwiseOrAssignment", "|=" },
{ "op_Comma", "," },
{ "op_DivisionAssignment", "/=" },
{ "op_Decrement", "--" },
{ "op_Increment", "++" },
{ "op_UnaryNegation", "-" },
{ "op_UnaryPlus", "+" },
{ "op_OnesComplement", "~" },
};
public static readonly Dictionary<string, int> operatorRanks = new Dictionary<string, int>
{
{ "op_Addition", 2 },
{ "op_Subtraction", 2 },
{ "op_Multiply", 2 },
{ "op_Division", 2 },
{ "op_Modulus", 2 },
{ "op_ExclusiveOr", 2 },
{ "op_BitwiseAnd", 2 },
{ "op_BitwiseOr", 2 },
{ "op_LogicalAnd", 2 },
{ "op_LogicalOr", 2 },
{ "op_Assign", 2 },
{ "op_LeftShift", 2 },
{ "op_RightShift", 2 },
{ "op_Equality", 2 },
{ "op_GreaterThan", 2 },
{ "op_LessThan", 2 },
{ "op_Inequality", 2 },
{ "op_GreaterThanOrEqual", 2 },
{ "op_LessThanOrEqual", 2 },
{ "op_MultiplicationAssignment", 2 },
{ "op_SubtractionAssignment", 2 },
{ "op_ExclusiveOrAssignment", 2 },
{ "op_LeftShiftAssignment", 2 },
{ "op_ModulusAssignment", 2 },
{ "op_AdditionAssignment", 2 },
{ "op_BitwiseAndAssignment", 2 },
{ "op_BitwiseOrAssignment", 2 },
{ "op_Comma", 2 },
{ "op_DivisionAssignment", 2 },
{ "op_Decrement", 1 },
{ "op_Increment", 1 },
{ "op_UnaryNegation", 1 },
{ "op_UnaryPlus", 1 },
{ "op_OnesComplement", 1 },
};
private static readonly Dictionary<UnaryOperator, UnaryOperatorHandler> unaryOperatorHandlers = new Dictionary<UnaryOperator, UnaryOperatorHandler>();
private static readonly Dictionary<BinaryOperator, BinaryOperatorHandler> binaryOpeatorHandlers = new Dictionary<BinaryOperator, BinaryOperatorHandler>();
private static readonly LogicalNegationHandler logicalNegationHandler = new LogicalNegationHandler();
private static readonly NumericNegationHandler numericNegationHandler = new NumericNegationHandler();
private static readonly IncrementHandler incrementHandler = new IncrementHandler();
private static readonly DecrementHandler decrementHandler = new DecrementHandler();
private static readonly PlusHandler plusHandler = new PlusHandler();
private static readonly AdditionHandler additionHandler = new AdditionHandler();
private static readonly SubtractionHandler subtractionHandler = new SubtractionHandler();
private static readonly MultiplicationHandler multiplicationHandler = new MultiplicationHandler();
private static readonly DivisionHandler divisionHandler = new DivisionHandler();
private static readonly ModuloHandler moduloHandler = new ModuloHandler();
private static readonly AndHandler andHandler = new AndHandler();
private static readonly OrHandler orHandler = new OrHandler();
private static readonly ExclusiveOrHandler exclusiveOrHandler = new ExclusiveOrHandler();
private static readonly EqualityHandler equalityHandler = new EqualityHandler();
private static readonly InequalityHandler inequalityHandler = new InequalityHandler();
private static readonly GreaterThanHandler greaterThanHandler = new GreaterThanHandler();
private static readonly LessThanHandler lessThanHandler = new LessThanHandler();
private static readonly GreaterThanOrEqualHandler greaterThanOrEqualHandler = new GreaterThanOrEqualHandler();
private static readonly LessThanOrEqualHandler lessThanOrEqualHandler = new LessThanOrEqualHandler();
private static readonly LeftShiftHandler leftShiftHandler = new LeftShiftHandler();
private static readonly RightShiftHandler rightShiftHandler = new RightShiftHandler();
public static UnaryOperatorHandler GetHandler(UnaryOperator @operator)
{
if (unaryOperatorHandlers.ContainsKey(@operator))
{
return unaryOperatorHandlers[@operator];
}
throw new UnexpectedEnumValueException<UnaryOperator>(@operator);
}
public static BinaryOperatorHandler GetHandler(BinaryOperator @operator)
{
if (binaryOpeatorHandlers.ContainsKey(@operator))
{
return binaryOpeatorHandlers[@operator];
}
throw new UnexpectedEnumValueException<BinaryOperator>(@operator);
}
public static string Symbol(this UnaryOperator @operator)
{
return GetHandler(@operator).symbol;
}
public static string Symbol(this BinaryOperator @operator)
{
return GetHandler(@operator).symbol;
}
public static string Name(this UnaryOperator @operator)
{
return GetHandler(@operator).name;
}
public static string Name(this BinaryOperator @operator)
{
return GetHandler(@operator).name;
}
public static string Verb(this UnaryOperator @operator)
{
return GetHandler(@operator).verb;
}
public static string Verb(this BinaryOperator @operator)
{
return GetHandler(@operator).verb;
}
public static object Operate(UnaryOperator @operator, object x)
{
if (!unaryOperatorHandlers.ContainsKey(@operator))
{
throw new UnexpectedEnumValueException<UnaryOperator>(@operator);
}
return unaryOperatorHandlers[@operator].Operate(x);
}
public static object Operate(BinaryOperator @operator, object a, object b)
{
if (!binaryOpeatorHandlers.ContainsKey(@operator))
{
throw new UnexpectedEnumValueException<BinaryOperator>(@operator);
}
return binaryOpeatorHandlers[@operator].Operate(a, b);
}
public static object Negate(object x)
{
return numericNegationHandler.Operate(x);
}
public static object Not(object x)
{
return logicalNegationHandler.Operate(x);
}
public static object UnaryPlus(object x)
{
return plusHandler.Operate(x);
}
public static object Increment(object x)
{
return incrementHandler.Operate(x);
}
public static object Decrement(object x)
{
return decrementHandler.Operate(x);
}
public static object And(object a, object b)
{
return andHandler.Operate(a, b);
}
public static object Or(object a, object b)
{
return orHandler.Operate(a, b);
}
public static object ExclusiveOr(object a, object b)
{
return exclusiveOrHandler.Operate(a, b);
}
public static object Add(object a, object b)
{
return additionHandler.Operate(a, b);
}
public static object Subtract(object a, object b)
{
return subtractionHandler.Operate(a, b);
}
public static object Multiply(object a, object b)
{
return multiplicationHandler.Operate(a, b);
}
public static object Divide(object a, object b)
{
return divisionHandler.Operate(a, b);
}
public static object Modulo(object a, object b)
{
return moduloHandler.Operate(a, b);
}
public static bool Equal(object a, object b)
{
return (bool)equalityHandler.Operate(a, b);
}
public static bool NotEqual(object a, object b)
{
return (bool)inequalityHandler.Operate(a, b);
}
public static bool GreaterThan(object a, object b)
{
return (bool)greaterThanHandler.Operate(a, b);
}
public static bool LessThan(object a, object b)
{
return (bool)lessThanHandler.Operate(a, b);
}
public static bool GreaterThanOrEqual(object a, object b)
{
return (bool)greaterThanOrEqualHandler.Operate(a, b);
}
public static bool LessThanOrEqual(object a, object b)
{
return (bool)lessThanOrEqualHandler.Operate(a, b);
}
public static object LeftShift(object a, object b)
{
return leftShiftHandler.Operate(a, b);
}
public static object RightShift(object a, object b)
{
return rightShiftHandler.Operate(a, b);
}
}
}

View File

@@ -0,0 +1,84 @@
#pragma warning disable 675
namespace Unity.VisualScripting
{
public class OrHandler : BinaryOperatorHandler
{
public OrHandler() : base("Or", "Or", "|", "op_BitwiseOr")
{
Handle<bool, bool>((a, b) => a | b);
Handle<byte, byte>((a, b) => a | b);
Handle<byte, sbyte>((a, b) => a | b);
Handle<byte, short>((a, b) => a | b);
Handle<byte, ushort>((a, b) => a | b);
Handle<byte, int>((a, b) => a | b);
Handle<byte, uint>((a, b) => a | b);
Handle<byte, long>((a, b) => a | b);
Handle<byte, ulong>((a, b) => a | b);
Handle<sbyte, byte>((a, b) => a | b);
Handle<sbyte, sbyte>((a, b) => a | b);
Handle<sbyte, short>((a, b) => a | b);
Handle<sbyte, ushort>((a, b) => a | b);
Handle<sbyte, int>((a, b) => a | b);
Handle<sbyte, uint>((a, b) => a | b);
Handle<sbyte, long>((a, b) => a | b);
//Handle<sbyte, ulong>((a, b) => a | b);
Handle<short, byte>((a, b) => a | b);
Handle<short, sbyte>((a, b) => a | b);
Handle<short, short>((a, b) => a | b);
Handle<short, ushort>((a, b) => a | b);
Handle<short, int>((a, b) => a | b);
Handle<short, uint>((a, b) => a | b);
Handle<short, long>((a, b) => a | b);
//Handle<short, ulong>((a, b) => a | b);
Handle<ushort, byte>((a, b) => a | b);
Handle<ushort, sbyte>((a, b) => a | b);
Handle<ushort, short>((a, b) => a | b);
Handle<ushort, ushort>((a, b) => a | b);
Handle<ushort, int>((a, b) => a | b);
Handle<ushort, uint>((a, b) => a | b);
Handle<ushort, long>((a, b) => a | b);
Handle<ushort, ulong>((a, b) => a | b);
Handle<int, byte>((a, b) => a | b);
Handle<int, sbyte>((a, b) => a | b);
Handle<int, short>((a, b) => a | b);
Handle<int, ushort>((a, b) => a | b);
Handle<int, int>((a, b) => a | b);
Handle<int, uint>((a, b) => a | b);
Handle<int, long>((a, b) => a | b);
//Handle<int, ulong>((a, b) => a | b);
Handle<uint, byte>((a, b) => a | b);
Handle<uint, sbyte>((a, b) => a | b);
Handle<uint, short>((a, b) => a | b);
Handle<uint, ushort>((a, b) => a | b);
Handle<uint, int>((a, b) => a | b);
Handle<uint, uint>((a, b) => a | b);
Handle<uint, long>((a, b) => a | b);
Handle<uint, ulong>((a, b) => a | b);
Handle<long, byte>((a, b) => a | b);
Handle<long, sbyte>((a, b) => a | b);
Handle<long, short>((a, b) => a | b);
Handle<long, ushort>((a, b) => a | b);
Handle<long, int>((a, b) => a | b);
Handle<long, uint>((a, b) => a | b);
Handle<long, long>((a, b) => a | b);
//Handle<long, ulong>((a, b) => a | b);
Handle<ulong, byte>((a, b) => a | b);
//Handle<ulong, sbyte>((a, b) => a | b);
//Handle<ulong, short>((a, b) => a | b);
Handle<ulong, ushort>((a, b) => a | b);
//Handle<ulong, int>((a, b) => a | b);
Handle<ulong, uint>((a, b) => a | b);
//Handle<ulong, long>((a, b) => a | b);
Handle<ulong, ulong>((a, b) => a | b);
}
}
}

View File

@@ -0,0 +1,20 @@
namespace Unity.VisualScripting
{
public sealed class PlusHandler : UnaryOperatorHandler
{
public PlusHandler() : base("Plus", "Plus", "+", "op_UnaryPlus")
{
Handle<byte>(a => +a);
Handle<sbyte>(a => +a);
Handle<short>(a => +a);
Handle<ushort>(a => +a);
Handle<int>(a => +a);
Handle<uint>(a => +a);
Handle<long>(a => +a);
Handle<ulong>(a => +a);
Handle<float>(a => +a);
Handle<decimal>(a => +a);
Handle<double>(a => +a);
}
}
}

View File

@@ -0,0 +1,56 @@
namespace Unity.VisualScripting
{
public class RightShiftHandler : BinaryOperatorHandler
{
public RightShiftHandler() : base("Right Shift", "Right Shift", ">>", "op_RightShift")
{
Handle<byte, byte>((a, b) => a >> b);
Handle<byte, sbyte>((a, b) => a >> b);
Handle<byte, short>((a, b) => a >> b);
Handle<byte, ushort>((a, b) => a >> b);
Handle<byte, int>((a, b) => a >> b);
Handle<sbyte, byte>((a, b) => a >> b);
Handle<sbyte, sbyte>((a, b) => a >> b);
Handle<sbyte, short>((a, b) => a >> b);
Handle<sbyte, ushort>((a, b) => a >> b);
Handle<sbyte, int>((a, b) => a >> b);
Handle<short, byte>((a, b) => a >> b);
Handle<short, sbyte>((a, b) => a >> b);
Handle<short, short>((a, b) => a >> b);
Handle<short, ushort>((a, b) => a >> b);
Handle<short, int>((a, b) => a >> b);
Handle<ushort, byte>((a, b) => a >> b);
Handle<ushort, sbyte>((a, b) => a >> b);
Handle<ushort, short>((a, b) => a >> b);
Handle<ushort, ushort>((a, b) => a >> b);
Handle<ushort, int>((a, b) => a >> b);
Handle<int, byte>((a, b) => a >> b);
Handle<int, sbyte>((a, b) => a >> b);
Handle<int, short>((a, b) => a >> b);
Handle<int, ushort>((a, b) => a >> b);
Handle<int, int>((a, b) => a >> b);
Handle<uint, byte>((a, b) => a >> b);
Handle<uint, sbyte>((a, b) => a >> b);
Handle<uint, short>((a, b) => a >> b);
Handle<uint, ushort>((a, b) => a >> b);
Handle<uint, int>((a, b) => a >> b);
Handle<long, byte>((a, b) => a >> b);
Handle<long, sbyte>((a, b) => a >> b);
Handle<long, short>((a, b) => a >> b);
Handle<long, ushort>((a, b) => a >> b);
Handle<long, int>((a, b) => a >> b);
Handle<ulong, byte>((a, b) => a >> b);
Handle<ulong, sbyte>((a, b) => a >> b);
Handle<ulong, short>((a, b) => a >> b);
Handle<ulong, ushort>((a, b) => a >> b);
Handle<ulong, int>((a, b) => a >> b);
}
}
}

View File

@@ -0,0 +1,140 @@
namespace Unity.VisualScripting
{
public sealed class SubtractionHandler : BinaryOperatorHandler
{
public SubtractionHandler() : base("Subtraction", "Subtract", "-", "op_Subtraction")
{
Handle<byte, byte>((a, b) => a - b);
Handle<byte, sbyte>((a, b) => a - b);
Handle<byte, short>((a, b) => a - b);
Handle<byte, ushort>((a, b) => a - b);
Handle<byte, int>((a, b) => a - b);
Handle<byte, uint>((a, b) => a - b);
Handle<byte, long>((a, b) => a - b);
Handle<byte, ulong>((a, b) => a - b);
Handle<byte, float>((a, b) => a - b);
Handle<byte, decimal>((a, b) => a - b);
Handle<byte, double>((a, b) => a - b);
Handle<sbyte, byte>((a, b) => a - b);
Handle<sbyte, sbyte>((a, b) => a - b);
Handle<sbyte, short>((a, b) => a - b);
Handle<sbyte, ushort>((a, b) => a - b);
Handle<sbyte, int>((a, b) => a - b);
Handle<sbyte, uint>((a, b) => a - b);
Handle<sbyte, long>((a, b) => a - b);
//Handle<sbyte, ulong>((a, b) => a - b);
Handle<sbyte, float>((a, b) => a - b);
Handle<sbyte, decimal>((a, b) => a - b);
Handle<sbyte, double>((a, b) => a - b);
Handle<short, byte>((a, b) => a - b);
Handle<short, sbyte>((a, b) => a - b);
Handle<short, short>((a, b) => a - b);
Handle<short, ushort>((a, b) => a - b);
Handle<short, int>((a, b) => a - b);
Handle<short, uint>((a, b) => a - b);
Handle<short, long>((a, b) => a - b);
//Handle<short, ulong>((a, b) => a - b);
Handle<short, float>((a, b) => a - b);
Handle<short, decimal>((a, b) => a - b);
Handle<short, double>((a, b) => a - b);
Handle<ushort, byte>((a, b) => a - b);
Handle<ushort, sbyte>((a, b) => a - b);
Handle<ushort, short>((a, b) => a - b);
Handle<ushort, ushort>((a, b) => a - b);
Handle<ushort, int>((a, b) => a - b);
Handle<ushort, uint>((a, b) => a - b);
Handle<ushort, long>((a, b) => a - b);
Handle<ushort, ulong>((a, b) => a - b);
Handle<ushort, float>((a, b) => a - b);
Handle<ushort, decimal>((a, b) => a - b);
Handle<ushort, double>((a, b) => a - b);
Handle<int, byte>((a, b) => a - b);
Handle<int, sbyte>((a, b) => a - b);
Handle<int, short>((a, b) => a - b);
Handle<int, ushort>((a, b) => a - b);
Handle<int, int>((a, b) => a - b);
Handle<int, uint>((a, b) => a - b);
Handle<int, long>((a, b) => a - b);
//Handle<int, ulong>((a, b) => a - b);
Handle<int, float>((a, b) => a - b);
Handle<int, decimal>((a, b) => a - b);
Handle<int, double>((a, b) => a - b);
Handle<uint, byte>((a, b) => a - b);
Handle<uint, sbyte>((a, b) => a - b);
Handle<uint, short>((a, b) => a - b);
Handle<uint, ushort>((a, b) => a - b);
Handle<uint, int>((a, b) => a - b);
Handle<uint, uint>((a, b) => a - b);
Handle<uint, long>((a, b) => a - b);
Handle<uint, ulong>((a, b) => a - b);
Handle<uint, float>((a, b) => a - b);
Handle<uint, decimal>((a, b) => a - b);
Handle<uint, double>((a, b) => a - b);
Handle<long, byte>((a, b) => a - b);
Handle<long, sbyte>((a, b) => a - b);
Handle<long, short>((a, b) => a - b);
Handle<long, ushort>((a, b) => a - b);
Handle<long, int>((a, b) => a - b);
Handle<long, uint>((a, b) => a - b);
Handle<long, long>((a, b) => a - b);
//Handle<long, ulong>((a, b) => a - b);
Handle<long, float>((a, b) => a - b);
Handle<long, decimal>((a, b) => a - b);
Handle<long, double>((a, b) => a - b);
Handle<ulong, byte>((a, b) => a - b);
//Handle<ulong, sbyte>((a, b) => a - b);
//Handle<ulong, short>((a, b) => a - b);
Handle<ulong, ushort>((a, b) => a - b);
//Handle<ulong, int>((a, b) => a - b);
Handle<ulong, uint>((a, b) => a - b);
//Handle<ulong, long>((a, b) => a - b);
Handle<ulong, ulong>((a, b) => a - b);
Handle<ulong, float>((a, b) => a - b);
Handle<ulong, decimal>((a, b) => a - b);
Handle<ulong, double>((a, b) => a - b);
Handle<float, byte>((a, b) => a - b);
Handle<float, sbyte>((a, b) => a - b);
Handle<float, short>((a, b) => a - b);
Handle<float, ushort>((a, b) => a - b);
Handle<float, int>((a, b) => a - b);
Handle<float, uint>((a, b) => a - b);
Handle<float, long>((a, b) => a - b);
Handle<float, ulong>((a, b) => a - b);
Handle<float, float>((a, b) => a - b);
//Handle<float, decimal>((a, b) => a - b);
Handle<float, double>((a, b) => a - b);
Handle<decimal, byte>((a, b) => a - b);
Handle<decimal, sbyte>((a, b) => a - b);
Handle<decimal, short>((a, b) => a - b);
Handle<decimal, ushort>((a, b) => a - b);
Handle<decimal, int>((a, b) => a - b);
Handle<decimal, uint>((a, b) => a - b);
Handle<decimal, long>((a, b) => a - b);
Handle<decimal, ulong>((a, b) => a - b);
//Handle<decimal, float>((a, b) => a - b);
Handle<decimal, decimal>((a, b) => a - b);
//Handle<decimal, double>((a, b) => a - b);
Handle<double, byte>((a, b) => a - b);
Handle<double, sbyte>((a, b) => a - b);
Handle<double, short>((a, b) => a - b);
Handle<double, ushort>((a, b) => a - b);
Handle<double, int>((a, b) => a - b);
Handle<double, uint>((a, b) => a - b);
Handle<double, long>((a, b) => a - b);
Handle<double, ulong>((a, b) => a - b);
Handle<double, float>((a, b) => a - b);
//Handle<double, decimal>((a, b) => a - b);
Handle<double, double>((a, b) => a - b);
}
}
}

View File

@@ -0,0 +1,11 @@
namespace Unity.VisualScripting
{
public enum UnaryOperator
{
LogicalNegation,
NumericNegation,
Increment,
Decrement,
Plus
}
}

View File

@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Unity.VisualScripting
{
public abstract class UnaryOperatorHandler : OperatorHandler
{
protected UnaryOperatorHandler(string name, string verb, string symbol, string customMethodName)
: base(name, verb, symbol, customMethodName) { }
private readonly Dictionary<Type, Func<object, object>> manualHandlers = new Dictionary<Type, Func<object, object>>();
private readonly Dictionary<Type, IOptimizedInvoker> userDefinedOperators = new Dictionary<Type, IOptimizedInvoker>();
private readonly Dictionary<Type, Type> userDefinedOperandTypes = new Dictionary<Type, Type>();
public object Operate(object operand)
{
Ensure.That(nameof(operand)).IsNotNull(operand);
var type = operand.GetType();
if (manualHandlers.ContainsKey(type))
{
return manualHandlers[type](operand);
}
if (customMethodName != null)
{
if (!userDefinedOperators.ContainsKey(type))
{
var method = type.GetMethod(customMethodName, BindingFlags.Public | BindingFlags.Static);
if (method != null)
{
userDefinedOperandTypes.Add(type, ResolveUserDefinedOperandType(method));
}
userDefinedOperators.Add(type, method?.Prewarm());
}
if (userDefinedOperators[type] != null)
{
operand = ConversionUtility.Convert(operand, userDefinedOperandTypes[type]);
return userDefinedOperators[type].Invoke(null, operand);
}
}
return CustomHandling(operand);
}
protected virtual object CustomHandling(object operand)
{
throw new InvalidOperatorException(symbol, operand.GetType());
}
protected void Handle<T>(Func<T, object> handler)
{
manualHandlers.Add(typeof(T), operand => handler((T)operand));
}
private static Type ResolveUserDefinedOperandType(MethodInfo userDefinedOperator)
{
// See comment in BinaryOperatorHandler
return userDefinedOperator.GetParameters()[0].ParameterType;
}
}
}

View File

@@ -0,0 +1,4 @@
namespace Unity.VisualScripting
{
public delegate void Action<T1, T2, T3, T4, T5>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
}

View File

@@ -0,0 +1,4 @@
namespace Unity.VisualScripting
{
public delegate void Action<T1, T2, T3, T4, T5, T6>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
}

View File

@@ -0,0 +1,4 @@
namespace Unity.VisualScripting
{
public delegate TResult Func<T1, T2, T3, T4, T5, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
}

View File

@@ -0,0 +1,4 @@
namespace Unity.VisualScripting
{
public delegate TResult Func<T1, T2, T3, T4, T5, T6, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
}

View File

@@ -0,0 +1,9 @@
namespace Unity.VisualScripting
{
public interface IOptimizedAccessor
{
void Compile();
object GetValue(object target);
void SetValue(object target, object value);
}
}

View File

@@ -0,0 +1,14 @@
namespace Unity.VisualScripting
{
public interface IOptimizedInvoker
{
void Compile();
object Invoke(object target);
object Invoke(object target, object arg0);
object Invoke(object target, object arg0, object arg1);
object Invoke(object target, object arg0, object arg1, object arg2);
object Invoke(object target, object arg0, object arg1, object arg2, object arg3);
object Invoke(object target, object arg0, object arg1, object arg2, object arg3, object arg4);
object Invoke(object target, params object[] args);
}
}

View File

@@ -0,0 +1,9 @@
using System.Reflection;
namespace Unity.VisualScripting
{
public abstract class InstanceActionInvokerBase<TTarget> : InstanceInvokerBase<TTarget>
{
protected InstanceActionInvokerBase(MethodInfo methodInfo) : base(methodInfo) { }
}
}

View File

@@ -0,0 +1,70 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class InstanceActionInvoker<TTarget> : InstanceActionInvokerBase<TTarget>
{
public InstanceActionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Action<TTarget> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 0)
{
throw new TargetParameterCountException();
}
return Invoke(target);
}
public override object Invoke(object target)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
try
{
return InvokeUnsafe(target);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target);
}
}
private object InvokeUnsafe(object target)
{
invoke.Invoke((TTarget)target);
return null;
}
protected override Type[] GetParameterTypes()
{
return Type.EmptyTypes;
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Action<TTarget>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (Action<TTarget>)methodInfo.CreateDelegate(typeof(Action<TTarget>));
}
}
}

View File

@@ -0,0 +1,71 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class InstanceActionInvoker<TTarget, TParam0> : InstanceActionInvokerBase<TTarget>
{
public InstanceActionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Action<TTarget, TParam0> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 1)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0]);
}
public override object Invoke(object target, object arg0)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
try
{
return InvokeUnsafe(target, arg0);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0);
}
}
private object InvokeUnsafe(object target, object arg0)
{
invoke.Invoke((TTarget)target, (TParam0)arg0);
return null;
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Action<TTarget, TParam0>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (Action<TTarget, TParam0>)methodInfo.CreateDelegate(typeof(Action<TTarget, TParam0>));
}
}
}

View File

@@ -0,0 +1,72 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class InstanceActionInvoker<TTarget, TParam0, TParam1> : InstanceActionInvokerBase<TTarget>
{
public InstanceActionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Action<TTarget, TParam0, TParam1> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 2)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0], args[1]);
}
public override object Invoke(object target, object arg0, object arg1)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
VerifyArgument<TParam1>(methodInfo, 0, arg1);
try
{
return InvokeUnsafe(target, arg0, arg1);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0, arg1);
}
}
public object InvokeUnsafe(object target, object arg0, object arg1)
{
invoke.Invoke((TTarget)target, (TParam0)arg0, (TParam1)arg1);
return null;
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0), typeof(TParam1) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Action<TTarget, TParam0, TParam1>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (Action<TTarget, TParam0, TParam1>)methodInfo.CreateDelegate(typeof(Action<TTarget, TParam0, TParam1>));
}
}
}

View File

@@ -0,0 +1,73 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class InstanceActionInvoker<TTarget, TParam0, TParam1, TParam2> : InstanceActionInvokerBase<TTarget>
{
public InstanceActionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Action<TTarget, TParam0, TParam1, TParam2> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 3)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0], args[1], args[2]);
}
public override object Invoke(object target, object arg0, object arg1, object arg2)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
VerifyArgument<TParam1>(methodInfo, 1, arg1);
VerifyArgument<TParam2>(methodInfo, 2, arg2);
try
{
return InvokeUnsafe(target, arg0, arg1, arg2);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0, arg1, arg2);
}
}
public object InvokeUnsafe(object target, object arg0, object arg1, object arg2)
{
invoke.Invoke((TTarget)target, (TParam0)arg0, (TParam1)arg1, (TParam2)arg2);
return null;
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0), typeof(TParam1), typeof(TParam2) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Action<TTarget, TParam0, TParam1, TParam2>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (Action<TTarget, TParam0, TParam1, TParam2>)methodInfo.CreateDelegate(typeof(Action<TTarget, TParam0, TParam1, TParam2>));
}
}
}

View File

@@ -0,0 +1,73 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class InstanceActionInvoker<TTarget, TParam0, TParam1, TParam2, TParam3> : InstanceActionInvokerBase<TTarget>
{
public InstanceActionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Action<TTarget, TParam0, TParam1, TParam2, TParam3> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 4)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0], args[1], args[2], args[3]);
}
public override object Invoke(object target, object arg0, object arg1, object arg2, object arg3)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
VerifyArgument<TParam1>(methodInfo, 1, arg1);
VerifyArgument<TParam2>(methodInfo, 2, arg2);
VerifyArgument<TParam3>(methodInfo, 3, arg3);
try
{
return InvokeUnsafe(target, arg0, arg1, arg2, arg3);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0, arg1, arg2, arg3);
}
}
public object InvokeUnsafe(object target, object arg0, object arg1, object arg2, object arg3)
{
invoke.Invoke((TTarget)target, (TParam0)arg0, (TParam1)arg1, (TParam2)arg2, (TParam3)arg3);
return null;
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0), typeof(TParam1), typeof(TParam2), typeof(TParam3) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Action<TTarget, TParam0, TParam1, TParam2, TParam3>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (Action<TTarget, TParam0, TParam1, TParam2, TParam3>)methodInfo.CreateDelegate(typeof(Action<TTarget, TParam0, TParam1, TParam2, TParam3>));
}
}
}

View File

@@ -0,0 +1,75 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class InstanceActionInvoker<TTarget, TParam0, TParam1, TParam2, TParam3, TParam4> : InstanceActionInvokerBase<TTarget>
{
public InstanceActionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Action<TTarget, TParam0, TParam1, TParam2, TParam3, TParam4> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 5)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0], args[1], args[2], args[3], args[4]);
}
public override object Invoke(object target, object arg0, object arg1, object arg2, object arg3, object arg4)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
VerifyArgument<TParam1>(methodInfo, 1, arg1);
VerifyArgument<TParam2>(methodInfo, 2, arg2);
VerifyArgument<TParam3>(methodInfo, 3, arg3);
VerifyArgument<TParam4>(methodInfo, 4, arg4);
try
{
return InvokeUnsafe(target, arg0, arg1, arg2, arg3, arg4);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0, arg1, arg2, arg3, arg4);
}
}
public object InvokeUnsafe(object target, object arg0, object arg1, object arg2, object arg3, object arg4)
{
invoke.Invoke((TTarget)target, (TParam0)arg0, (TParam1)arg1, (TParam2)arg2, (TParam3)arg3, (TParam4)arg4);
return null;
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0), typeof(TParam1), typeof(TParam2), typeof(TParam3), typeof(TParam4) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Action<TTarget, TParam0, TParam1, TParam2, TParam3, TParam4>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (Action<TTarget, TParam0, TParam1, TParam2, TParam3, TParam4>)methodInfo.CreateDelegate(typeof(Action<TTarget, TParam0, TParam1, TParam2, TParam3, TParam4>));
}
}
}

View File

@@ -0,0 +1,171 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
using UnityEngine;
namespace Unity.VisualScripting
{
public class InstanceFieldAccessor<TTarget, TField> : IOptimizedAccessor
{
public InstanceFieldAccessor(FieldInfo fieldInfo)
{
if (OptimizedReflection.safeMode)
{
Ensure.That(nameof(fieldInfo)).IsNotNull(fieldInfo);
if (fieldInfo.DeclaringType != typeof(TTarget))
{
throw new ArgumentException("Declaring type of field info doesn't match generic type.", nameof(fieldInfo));
}
if (fieldInfo.FieldType != typeof(TField))
{
throw new ArgumentException("Field type of field info doesn't match generic type.", nameof(fieldInfo));
}
if (fieldInfo.IsStatic)
{
throw new ArgumentException("The field is static.", nameof(fieldInfo));
}
}
this.fieldInfo = fieldInfo;
}
private readonly FieldInfo fieldInfo;
private Func<TTarget, TField> getter;
private Action<TTarget, TField> setter;
public void Compile()
{
if (OptimizedReflection.useJit)
{
var targetExpression = Expression.Parameter(typeof(TTarget), "target");
// Getter
var fieldExpression = Expression.Field(targetExpression, fieldInfo);
getter = Expression.Lambda<Func<TTarget, TField>>(fieldExpression, targetExpression).Compile();
// Setter
if (fieldInfo.CanWrite())
{
#if UNITY_2018_3_OR_NEWER
try
{
var valueExpression = Expression.Parameter(typeof(TField));
var assignExpression = Expression.Assign(fieldExpression, valueExpression);
setter = Expression.Lambda<Action<TTarget, TField>>(assignExpression, targetExpression, valueExpression).Compile();
}
catch
{
Debug.Log("Failed instance field: " + fieldInfo);
throw;
}
#else
var setterMethod = new DynamicMethod
(
"setter",
typeof(void),
new[] { typeof(TTarget), typeof(TField) },
typeof(TTarget),
true
);
var setterIL = setterMethod.GetILGenerator();
setterIL.Emit(OpCodes.Ldarg_0);
setterIL.Emit(OpCodes.Ldarg_1);
setterIL.Emit(OpCodes.Stfld, fieldInfo);
setterIL.Emit(OpCodes.Ret);
setter = (Action<TTarget, TField>)setterMethod.CreateDelegate(typeof(Action<TTarget, TField>));
#endif
}
}
else
{
getter = (instance) => (TField)fieldInfo.GetValue(instance);
if (fieldInfo.CanWrite())
{
setter = (instance, value) => fieldInfo.SetValue(instance, value);
}
}
}
public object GetValue(object target)
{
if (OptimizedReflection.safeMode)
{
OptimizedReflection.VerifyInstanceTarget<TTarget>(target);
try
{
return GetValueUnsafe(target);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return GetValueUnsafe(target);
}
}
private object GetValueUnsafe(object target)
{
// No need for special handling of value types, because field accessing cannot have side effects.
// Therefore, working on a copy of the instance is faster and equivalent.
return getter.Invoke((TTarget)target);
}
public void SetValue(object target, object value)
{
if (OptimizedReflection.safeMode)
{
OptimizedReflection.VerifyInstanceTarget<TTarget>(target);
if (setter == null)
{
throw new TargetException($"The field '{typeof(TTarget)}.{fieldInfo.Name}' cannot be assigned.");
}
if (!typeof(TField).IsAssignableFrom(value))
{
throw new ArgumentException($"The provided value for '{typeof(TTarget)}.{fieldInfo.Name}' does not match the field type.\nProvided: {value?.GetType()?.ToString() ?? "null"}\nExpected: {typeof(TField)}");
}
try
{
SetValueUnsafe(target, value);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
SetValueUnsafe(target, value);
}
}
private void SetValueUnsafe(object target, object value)
{
setter.Invoke((TTarget)target, (TField)value);
}
}
}

View File

@@ -0,0 +1,19 @@
using System;
using System.Reflection;
namespace Unity.VisualScripting
{
public abstract class InstanceFunctionInvokerBase<TTarget, TResult> : InstanceInvokerBase<TTarget>
{
protected InstanceFunctionInvokerBase(MethodInfo methodInfo) : base(methodInfo)
{
if (OptimizedReflection.safeMode)
{
if (methodInfo.ReturnType != typeof(TResult))
{
throw new ArgumentException("Return type of method info doesn't match generic type.", nameof(methodInfo));
}
}
}
}
}

View File

@@ -0,0 +1,68 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class InstanceFunctionInvoker<TTarget, TResult> : InstanceFunctionInvokerBase<TTarget, TResult>
{
public InstanceFunctionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Func<TTarget, TResult> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 0)
{
throw new TargetParameterCountException();
}
return Invoke(target);
}
public override object Invoke(object target)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
try
{
return InvokeUnsafe(target);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target);
}
}
public object InvokeUnsafe(object target)
{
return invoke.Invoke((TTarget)target);
}
protected override Type[] GetParameterTypes()
{
return Type.EmptyTypes;
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Func<TTarget, TResult>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (Func<TTarget, TResult>)methodInfo.CreateDelegate(typeof(Func<TTarget, TResult>));
}
}
}

View File

@@ -0,0 +1,69 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class InstanceFunctionInvoker<TTarget, TParam0, TResult> : InstanceFunctionInvokerBase<TTarget, TResult>
{
public InstanceFunctionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Func<TTarget, TParam0, TResult> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 1)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0]);
}
public override object Invoke(object target, object arg0)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
try
{
return InvokeUnsafe(target, arg0);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0);
}
}
public object InvokeUnsafe(object target, object arg0)
{
return invoke.Invoke((TTarget)target, (TParam0)arg0);
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Func<TTarget, TParam0, TResult>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (Func<TTarget, TParam0, TResult>)methodInfo.CreateDelegate(typeof(Func<TTarget, TParam0, TResult>));
}
}
}

View File

@@ -0,0 +1,70 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class InstanceFunctionInvoker<TTarget, TParam0, TParam1, TResult> : InstanceFunctionInvokerBase<TTarget, TResult>
{
public InstanceFunctionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Func<TTarget, TParam0, TParam1, TResult> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 2)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0], args[1]);
}
public override object Invoke(object target, object arg0, object arg1)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
VerifyArgument<TParam1>(methodInfo, 1, arg1);
try
{
return InvokeUnsafe(target, arg0, arg1);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0, arg1);
}
}
public object InvokeUnsafe(object target, object arg0, object arg1)
{
return invoke.Invoke((TTarget)target, (TParam0)arg0, (TParam1)arg1);
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0), typeof(TParam1) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Func<TTarget, TParam0, TParam1, TResult>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (Func<TTarget, TParam0, TParam1, TResult>)methodInfo.CreateDelegate(typeof(Func<TTarget, TParam0, TParam1, TResult>));
}
}
}

View File

@@ -0,0 +1,71 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class InstanceFunctionInvoker<TTarget, TParam0, TParam1, TParam2, TResult> : InstanceFunctionInvokerBase<TTarget, TResult>
{
public InstanceFunctionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Func<TTarget, TParam0, TParam1, TParam2, TResult> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 3)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0], args[1], args[2]);
}
public override object Invoke(object target, object arg0, object arg1, object arg2)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
VerifyArgument<TParam1>(methodInfo, 1, arg1);
VerifyArgument<TParam2>(methodInfo, 2, arg2);
try
{
return InvokeUnsafe(target, arg0, arg1, arg2);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0, arg1, arg2);
}
}
public object InvokeUnsafe(object target, object arg0, object arg1, object arg2)
{
return invoke.Invoke((TTarget)target, (TParam0)arg0, (TParam1)arg1, (TParam2)arg2);
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0), typeof(TParam1), typeof(TParam2) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Func<TTarget, TParam0, TParam1, TParam2, TResult>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (Func<TTarget, TParam0, TParam1, TParam2, TResult>)methodInfo.CreateDelegate(typeof(Func<TTarget, TParam0, TParam1, TParam2, TResult>));
}
}
}

View File

@@ -0,0 +1,72 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class InstanceFunctionInvoker<TTarget, TParam0, TParam1, TParam2, TParam3, TResult> : InstanceFunctionInvokerBase<TTarget, TResult>
{
public InstanceFunctionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Func<TTarget, TParam0, TParam1, TParam2, TParam3, TResult> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 4)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0], args[1], args[2], args[3]);
}
public override object Invoke(object target, object arg0, object arg1, object arg2, object arg3)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
VerifyArgument<TParam1>(methodInfo, 1, arg1);
VerifyArgument<TParam2>(methodInfo, 2, arg2);
VerifyArgument<TParam3>(methodInfo, 3, arg3);
try
{
return InvokeUnsafe(target, arg0, arg1, arg2, arg3);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0, arg1, arg2, arg3);
}
}
public object InvokeUnsafe(object target, object arg0, object arg1, object arg2, object arg3)
{
return invoke.Invoke((TTarget)target, (TParam0)arg0, (TParam1)arg1, (TParam2)arg2, (TParam3)arg3);
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0), typeof(TParam1), typeof(TParam2), typeof(TParam3) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Func<TTarget, TParam0, TParam1, TParam2, TParam3, TResult>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (Func<TTarget, TParam0, TParam1, TParam2, TParam3, TResult>)methodInfo.CreateDelegate(typeof(Func<TTarget, TParam0, TParam1, TParam2, TParam3, TResult>));
}
}
}

View File

@@ -0,0 +1,73 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class InstanceFunctionInvoker<TTarget, TParam0, TParam1, TParam2, TParam3, TParam4, TResult> : InstanceFunctionInvokerBase<TTarget, TResult>
{
public InstanceFunctionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Func<TTarget, TParam0, TParam1, TParam2, TParam3, TParam4, TResult> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 5)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0], args[1], args[2], args[3], args[4]);
}
public override object Invoke(object target, object arg0, object arg1, object arg2, object arg3, object arg4)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
VerifyArgument<TParam1>(methodInfo, 1, arg1);
VerifyArgument<TParam2>(methodInfo, 2, arg2);
VerifyArgument<TParam3>(methodInfo, 3, arg3);
VerifyArgument<TParam4>(methodInfo, 4, arg4);
try
{
return InvokeUnsafe(target, arg0, arg1, arg2, arg3, arg4);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0, arg1, arg2, arg3, arg4);
}
}
public object InvokeUnsafe(object target, object arg0, object arg1, object arg2, object arg3, object arg4)
{
return invoke.Invoke((TTarget)target, (TParam0)arg0, (TParam1)arg1, (TParam2)arg2, (TParam3)arg3, (TParam4)arg4);
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0), typeof(TParam1), typeof(TParam2), typeof(TParam3), typeof(TParam4) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Func<TTarget, TParam0, TParam1, TParam2, TParam3, TParam4, TResult>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (Func<TTarget, TParam0, TParam1, TParam2, TParam3, TParam4, TResult>)methodInfo.CreateDelegate(typeof(Func<TTarget, TParam0, TParam1, TParam2, TParam3, TParam4, TResult>));
}
}
}

View File

@@ -0,0 +1,47 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public abstract class InstanceInvokerBase<TTarget> : InvokerBase
{
protected InstanceInvokerBase(MethodInfo methodInfo) : base(methodInfo)
{
if (OptimizedReflection.safeMode)
{
if (methodInfo.DeclaringType != typeof(TTarget))
{
throw new ArgumentException("Declaring type of method info doesn't match generic type.", nameof(methodInfo));
}
if (methodInfo.IsStatic)
{
throw new ArgumentException("The method is static.", nameof(methodInfo));
}
}
}
protected sealed override void CompileExpression()
{
var targetExpression = Expression.Parameter(typeof(TTarget), "target");
var parameterExpressions = GetParameterExpressions();
var parameterExpressionsIncludingTarget = new ParameterExpression[1 + parameterExpressions.Length];
parameterExpressionsIncludingTarget[0] = targetExpression;
Array.Copy(parameterExpressions, 0, parameterExpressionsIncludingTarget, 1, parameterExpressions.Length);
var callExpression = Expression.Call(targetExpression, methodInfo, parameterExpressions);
CompileExpression(callExpression, parameterExpressionsIncludingTarget);
}
protected abstract void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions);
protected override void VerifyTarget(object target)
{
OptimizedReflection.VerifyInstanceTarget<TTarget>(target);
}
}
}

View File

@@ -0,0 +1,147 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public class InstancePropertyAccessor<TTarget, TProperty> : IOptimizedAccessor
{
public InstancePropertyAccessor(PropertyInfo propertyInfo)
{
if (OptimizedReflection.safeMode)
{
Ensure.That(nameof(propertyInfo)).IsNotNull(propertyInfo);
if (propertyInfo.DeclaringType != typeof(TTarget))
{
throw new ArgumentException("The declaring type of the property info doesn't match the generic type.", nameof(propertyInfo));
}
if (propertyInfo.PropertyType != typeof(TProperty))
{
throw new ArgumentException("The property type of the property info doesn't match the generic type.", nameof(propertyInfo));
}
if (propertyInfo.IsStatic())
{
throw new ArgumentException("The property is static.", nameof(propertyInfo));
}
}
this.propertyInfo = propertyInfo;
}
private readonly PropertyInfo propertyInfo;
private Func<TTarget, TProperty> getter;
private Action<TTarget, TProperty> setter;
public void Compile()
{
var getterInfo = propertyInfo.GetGetMethod(true);
var setterInfo = propertyInfo.GetSetMethod(true);
if (OptimizedReflection.useJit)
{
var targetExpression = Expression.Parameter(typeof(TTarget), "target");
if (getterInfo != null)
{
var propertyExpression = Expression.Property(targetExpression, propertyInfo);
getter = Expression.Lambda<Func<TTarget, TProperty>>(propertyExpression, targetExpression).Compile();
}
if (setterInfo != null)
{
setter = (Action<TTarget, TProperty>)setterInfo.CreateDelegate(typeof(Action<TTarget, TProperty>));
}
}
else
{
if (getterInfo != null)
{
getter = (Func<TTarget, TProperty>)getterInfo.CreateDelegate(typeof(Func<TTarget, TProperty>));
}
if (setterInfo != null)
{
setter = (Action<TTarget, TProperty>)setterInfo.CreateDelegate(typeof(Action<TTarget, TProperty>));
}
}
}
public object GetValue(object target)
{
if (OptimizedReflection.safeMode)
{
OptimizedReflection.VerifyInstanceTarget<TTarget>(target);
if (getter == null)
{
throw new TargetException($"The property '{typeof(TTarget)}.{propertyInfo.Name}' has no get accessor.");
}
try
{
return GetValueUnsafe(target);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return GetValueUnsafe(target);
}
}
private object GetValueUnsafe(object target)
{
return getter.Invoke((TTarget)target);
}
public void SetValue(object target, object value)
{
if (OptimizedReflection.safeMode)
{
OptimizedReflection.VerifyInstanceTarget<TTarget>(target);
if (setter == null)
{
throw new TargetException($"The property '{typeof(TTarget)}.{propertyInfo.Name}' has no set accessor.");
}
if (!typeof(TProperty).IsAssignableFrom(value))
{
throw new ArgumentException($"The provided value for '{typeof(TTarget)}.{propertyInfo.Name}' does not match the property type.\nProvided: {value?.GetType()?.ToString() ?? "null"}\nExpected: {typeof(TProperty)}");
}
try
{
SetValueUnsafe(target, value);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
SetValueUnsafe(target, value);
}
}
private void SetValueUnsafe(object target, object value)
{
setter.Invoke((TTarget)target, (TProperty)value);
}
}
}

View File

@@ -0,0 +1,114 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public abstract class InvokerBase : IOptimizedInvoker
{
protected InvokerBase(MethodInfo methodInfo)
{
if (OptimizedReflection.safeMode)
{
if (methodInfo == null)
{
throw new ArgumentNullException(nameof(methodInfo));
}
}
this.methodInfo = methodInfo;
targetType = methodInfo.DeclaringType;
}
protected readonly Type targetType;
protected readonly MethodInfo methodInfo;
protected void VerifyArgument<TParam>(MethodInfo methodInfo, int argIndex, object arg)
{
if (!typeof(TParam).IsAssignableFrom(arg))
{
throw new ArgumentException($"The provided argument value for '{targetType}.{methodInfo.Name}' does not match the parameter type.\nProvided: {arg?.GetType().ToString() ?? "null"}\nExpected: {typeof(TParam)}", methodInfo.GetParameters()[argIndex].Name);
}
}
public void Compile()
{
if (OptimizedReflection.useJit)
{
CompileExpression();
}
else
{
CreateDelegate();
}
}
protected ParameterExpression[] GetParameterExpressions()
{
var methodParameters = methodInfo.GetParameters();
var parameterTypes = GetParameterTypes();
if (methodParameters.Length != parameterTypes.Length)
{
throw new ArgumentException("Parameter count of method info doesn't match generic argument count.", nameof(methodInfo));
}
for (var i = 0; i < parameterTypes.Length; i++)
{
if (parameterTypes[i] != methodParameters[i].ParameterType)
{
throw new ArgumentException("Parameter type of method info doesn't match generic argument.", nameof(methodInfo));
}
}
var parameterExpressions = new ParameterExpression[parameterTypes.Length];
for (var i = 0; i < parameterTypes.Length; i++)
{
parameterExpressions[i] = Expression.Parameter(parameterTypes[i], "parameter" + i);
}
return parameterExpressions;
}
protected abstract Type[] GetParameterTypes();
public abstract object Invoke(object target, params object[] args);
public virtual object Invoke(object target)
{
throw new TargetParameterCountException();
}
public virtual object Invoke(object target, object arg0)
{
throw new TargetParameterCountException();
}
public virtual object Invoke(object target, object arg0, object arg1)
{
throw new TargetParameterCountException();
}
public virtual object Invoke(object target, object arg0, object arg1, object arg2)
{
throw new TargetParameterCountException();
}
public virtual object Invoke(object target, object arg0, object arg1, object arg2, object arg3)
{
throw new TargetParameterCountException();
}
public virtual object Invoke(object target, object arg0, object arg1, object arg2, object arg3, object arg4)
{
throw new TargetParameterCountException();
}
protected abstract void CompileExpression();
protected abstract void CreateDelegate();
protected abstract void VerifyTarget(object target);
}
}

View File

@@ -0,0 +1,489 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace Unity.VisualScripting
{
// Inspirations:
// http://stackoverflow.com/a/26733318
// http://stackoverflow.com/a/16136854
// http://stackoverflow.com/a/321686
public static class OptimizedReflection
{
static OptimizedReflection()
{
fieldAccessors = new Dictionary<FieldInfo, IOptimizedAccessor>();
propertyAccessors = new Dictionary<PropertyInfo, IOptimizedAccessor>();
methodInvokers = new Dictionary<MethodInfo, IOptimizedInvoker>();
jitAvailable = PlatformUtility.supportsJit;
}
private static readonly Dictionary<FieldInfo, IOptimizedAccessor> fieldAccessors;
private static readonly Dictionary<PropertyInfo, IOptimizedAccessor> propertyAccessors;
private static readonly Dictionary<MethodInfo, IOptimizedInvoker> methodInvokers;
public static readonly bool jitAvailable;
private static bool _useJitIfAvailable = true;
internal static bool useJit => useJitIfAvailable && jitAvailable;
public static bool useJitIfAvailable
{
get
{
return _useJitIfAvailable;
}
set
{
_useJitIfAvailable = value;
ClearCache();
}
}
public static bool safeMode { get; set; }
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void OnRuntimeMethodLoad()
{
safeMode = Application.isEditor || Debug.isDebugBuild;
}
public static void ClearCache()
{
fieldAccessors.Clear();
propertyAccessors.Clear();
methodInvokers.Clear();
}
internal static void VerifyStaticTarget(Type targetType, object target)
{
VerifyTarget(targetType, target, true);
}
internal static void VerifyInstanceTarget<TTArget>(object target)
{
VerifyTarget(typeof(TTArget), target, false);
}
private static void VerifyTarget(Type targetType, object target, bool @static)
{
Ensure.That(nameof(targetType)).IsNotNull(targetType);
if (@static)
{
if (target != null)
{
throw new TargetException($"Superfluous target object for '{targetType}'.");
}
}
else
{
if (target == null)
{
throw new TargetException($"Missing target object for '{targetType}'.");
}
if (!targetType.IsAssignableFrom(targetType))
{
throw new TargetException($"The target object does not match the target type.\nProvided: {target.GetType()}\nExpected: {targetType}");
}
}
}
private static bool SupportsOptimization(MemberInfo memberInfo)
{
// Instance members on value types require by-ref passing of the target object:
// https://stackoverflow.com/a/1212396/154502
// However, a bug in Unity's Mono version prevents by-ref delegates from working:
// http://stackoverflow.com/questions/34743176/#comment73561434_34744241
// Therefore, instance members on value types have to use reflection.
if (memberInfo.DeclaringType.IsValueType && !memberInfo.IsStatic())
{
return false;
}
return true;
}
#region Fields
public static IOptimizedAccessor Prewarm(this FieldInfo fieldInfo)
{
return GetFieldAccessor(fieldInfo);
}
public static object GetValueOptimized(this FieldInfo fieldInfo, object target)
{
return GetFieldAccessor(fieldInfo).GetValue(target);
}
public static void SetValueOptimized(this FieldInfo fieldInfo, object target, object value)
{
GetFieldAccessor(fieldInfo).SetValue(target, value);
}
public static bool SupportsOptimization(this FieldInfo fieldInfo)
{
if (!SupportsOptimization((MemberInfo)fieldInfo))
{
return false;
}
return true;
}
private static IOptimizedAccessor GetFieldAccessor(FieldInfo fieldInfo)
{
Ensure.That(nameof(fieldInfo)).IsNotNull(fieldInfo);
lock (fieldAccessors)
{
if (!fieldAccessors.TryGetValue(fieldInfo, out var accessor))
{
if (SupportsOptimization(fieldInfo))
{
Type accessorType;
if (fieldInfo.IsStatic)
{
accessorType = typeof(StaticFieldAccessor<>).MakeGenericType(fieldInfo.FieldType);
}
else
{
accessorType = typeof(InstanceFieldAccessor<,>).MakeGenericType(fieldInfo.DeclaringType, fieldInfo.FieldType);
}
accessor = (IOptimizedAccessor)Activator.CreateInstance(accessorType, fieldInfo);
}
else
{
accessor = new ReflectionFieldAccessor(fieldInfo);
}
accessor.Compile();
fieldAccessors.Add(fieldInfo, accessor);
}
return accessor;
}
}
#endregion
#region Properties
public static IOptimizedAccessor Prewarm(this PropertyInfo propertyInfo)
{
return GetPropertyAccessor(propertyInfo);
}
public static object GetValueOptimized(this PropertyInfo propertyInfo, object target)
{
return GetPropertyAccessor(propertyInfo).GetValue(target);
}
public static void SetValueOptimized(this PropertyInfo propertyInfo, object target, object value)
{
GetPropertyAccessor(propertyInfo).SetValue(target, value);
}
public static bool SupportsOptimization(this PropertyInfo propertyInfo)
{
if (!SupportsOptimization((MemberInfo)propertyInfo))
{
return false;
}
return true;
}
private static IOptimizedAccessor GetPropertyAccessor(PropertyInfo propertyInfo)
{
Ensure.That(nameof(propertyInfo)).IsNotNull(propertyInfo);
lock (propertyAccessors)
{
if (!propertyAccessors.TryGetValue(propertyInfo, out var accessor))
{
if (SupportsOptimization(propertyInfo))
{
Type accessorType;
if (propertyInfo.IsStatic())
{
accessorType = typeof(StaticPropertyAccessor<>).MakeGenericType(propertyInfo.PropertyType);
}
else
{
accessorType = typeof(InstancePropertyAccessor<,>).MakeGenericType(propertyInfo.DeclaringType, propertyInfo.PropertyType);
}
accessor = (IOptimizedAccessor)Activator.CreateInstance(accessorType, propertyInfo);
}
else
{
accessor = new ReflectionPropertyAccessor(propertyInfo);
}
accessor.Compile();
propertyAccessors.Add(propertyInfo, accessor);
}
return accessor;
}
}
#endregion
#region Methods
public static IOptimizedInvoker Prewarm(this MethodInfo methodInfo)
{
return GetMethodInvoker(methodInfo);
}
public static object InvokeOptimized(this MethodInfo methodInfo, object target, params object[] args)
{
return GetMethodInvoker(methodInfo).Invoke(target, args);
}
public static object InvokeOptimized(this MethodInfo methodInfo, object target)
{
return GetMethodInvoker(methodInfo).Invoke(target);
}
public static object InvokeOptimized(this MethodInfo methodInfo, object target, object arg0)
{
return GetMethodInvoker(methodInfo).Invoke(target, arg0);
}
public static object InvokeOptimized(this MethodInfo methodInfo, object target, object arg0, object arg1)
{
return GetMethodInvoker(methodInfo).Invoke(target, arg0, arg1);
}
public static object InvokeOptimized(this MethodInfo methodInfo, object target, object arg0, object arg1, object arg2)
{
return GetMethodInvoker(methodInfo).Invoke(target, arg0, arg1, arg2);
}
public static object InvokeOptimized(this MethodInfo methodInfo, object target, object arg0, object arg1, object arg2, object arg3)
{
return GetMethodInvoker(methodInfo).Invoke(target, arg0, arg1, arg2, arg3);
}
public static object InvokeOptimized(this MethodInfo methodInfo, object target, object arg0, object arg1, object arg2, object arg3, object arg4)
{
return GetMethodInvoker(methodInfo).Invoke(target, arg0, arg1, arg2, arg3, arg4);
}
public static bool SupportsOptimization(this MethodInfo methodInfo)
{
if (!SupportsOptimization((MemberInfo)methodInfo))
{
return false;
}
var parameters = methodInfo.GetParameters();
if (parameters.Length > 5)
{
return false;
}
if (parameters.Any(parameter => parameter.ParameterType.IsByRef))
{
return false;
}
// CreateDelegate in IL2CPP does not work properly for overridden methods, instead referring to the virtual method.
// https://support.ludiq.io/forums/5-bolt/topics/872-virtual-method-overrides-not-used-on-aot/
// https://fogbugz.unity3d.com/default.asp?980136_228np3be9idtbdtt
if (!jitAvailable && methodInfo.IsVirtual && !methodInfo.IsFinal)
{
return false;
}
// Undocumented __arglist keyword as used in the 4+ overload of String.Concat causes runtime crash
if (methodInfo.CallingConvention == CallingConventions.VarArgs)
{
return false;
}
return true;
}
private static IOptimizedInvoker GetMethodInvoker(MethodInfo methodInfo)
{
Ensure.That(nameof(methodInfo)).IsNotNull(methodInfo);
lock (methodInvokers)
{
if (!methodInvokers.TryGetValue(methodInfo, out var invoker))
{
if (SupportsOptimization(methodInfo))
{
Type invokerType;
var parameters = methodInfo.GetParameters();
if (methodInfo.ReturnType == typeof(void))
{
if (methodInfo.IsStatic)
{
if (parameters.Length == 0)
{
invokerType = typeof(StaticActionInvoker);
}
else if (parameters.Length == 1)
{
invokerType = typeof(StaticActionInvoker<>).MakeGenericType(parameters[0].ParameterType);
}
else if (parameters.Length == 2)
{
invokerType = typeof(StaticActionInvoker<,>).MakeGenericType(parameters[0].ParameterType, parameters[1].ParameterType);
}
else if (parameters.Length == 3)
{
invokerType = typeof(StaticActionInvoker<,,>).MakeGenericType(parameters[0].ParameterType, parameters[1].ParameterType, parameters[2].ParameterType);
}
else if (parameters.Length == 4)
{
invokerType = typeof(StaticActionInvoker<,,,>).MakeGenericType(parameters[0].ParameterType, parameters[1].ParameterType, parameters[2].ParameterType, parameters[3].ParameterType);
}
else if (parameters.Length == 5)
{
invokerType = typeof(StaticActionInvoker<,,,,>).MakeGenericType(parameters[0].ParameterType, parameters[1].ParameterType, parameters[2].ParameterType, parameters[3].ParameterType, parameters[4].ParameterType);
}
else
{
throw new NotSupportedException();
}
}
else
{
if (parameters.Length == 0)
{
invokerType = typeof(InstanceActionInvoker<>).MakeGenericType(methodInfo.DeclaringType);
}
else if (parameters.Length == 1)
{
invokerType = typeof(InstanceActionInvoker<,>).MakeGenericType(methodInfo.DeclaringType, parameters[0].ParameterType);
}
else if (parameters.Length == 2)
{
invokerType = typeof(InstanceActionInvoker<,,>).MakeGenericType(methodInfo.DeclaringType, parameters[0].ParameterType, parameters[1].ParameterType);
}
else if (parameters.Length == 3)
{
invokerType = typeof(InstanceActionInvoker<,,,>).MakeGenericType(methodInfo.DeclaringType, parameters[0].ParameterType, parameters[1].ParameterType, parameters[2].ParameterType);
}
else if (parameters.Length == 4)
{
invokerType = typeof(InstanceActionInvoker<,,,,>).MakeGenericType(methodInfo.DeclaringType, parameters[0].ParameterType, parameters[1].ParameterType, parameters[2].ParameterType, parameters[3].ParameterType);
}
else if (parameters.Length == 5)
{
invokerType = typeof(InstanceActionInvoker<,,,,,>).MakeGenericType(methodInfo.DeclaringType, parameters[0].ParameterType, parameters[1].ParameterType, parameters[2].ParameterType, parameters[3].ParameterType, parameters[4].ParameterType);
}
else
{
throw new NotSupportedException();
}
}
}
else
{
if (methodInfo.IsStatic)
{
if (parameters.Length == 0)
{
invokerType = typeof(StaticFunctionInvoker<>).MakeGenericType(methodInfo.ReturnType);
}
else if (parameters.Length == 1)
{
invokerType = typeof(StaticFunctionInvoker<,>).MakeGenericType(parameters[0].ParameterType, methodInfo.ReturnType);
}
else if (parameters.Length == 2)
{
invokerType = typeof(StaticFunctionInvoker<,,>).MakeGenericType(parameters[0].ParameterType, parameters[1].ParameterType, methodInfo.ReturnType);
}
else if (parameters.Length == 3)
{
invokerType = typeof(StaticFunctionInvoker<,,,>).MakeGenericType(parameters[0].ParameterType, parameters[1].ParameterType, parameters[2].ParameterType, methodInfo.ReturnType);
}
else if (parameters.Length == 4)
{
invokerType = typeof(StaticFunctionInvoker<,,,,>).MakeGenericType(parameters[0].ParameterType, parameters[1].ParameterType, parameters[2].ParameterType, parameters[3].ParameterType, methodInfo.ReturnType);
}
else if (parameters.Length == 5)
{
invokerType = typeof(StaticFunctionInvoker<,,,,,>).MakeGenericType(parameters[0].ParameterType, parameters[1].ParameterType, parameters[2].ParameterType, parameters[3].ParameterType, parameters[4].ParameterType, methodInfo.ReturnType);
}
else
{
throw new NotSupportedException();
}
}
else
{
if (parameters.Length == 0)
{
invokerType = typeof(InstanceFunctionInvoker<,>).MakeGenericType(methodInfo.DeclaringType, methodInfo.ReturnType);
}
else if (parameters.Length == 1)
{
invokerType = typeof(InstanceFunctionInvoker<,,>).MakeGenericType(methodInfo.DeclaringType, parameters[0].ParameterType, methodInfo.ReturnType);
}
else if (parameters.Length == 2)
{
invokerType = typeof(InstanceFunctionInvoker<,,,>).MakeGenericType(methodInfo.DeclaringType, parameters[0].ParameterType, parameters[1].ParameterType, methodInfo.ReturnType);
}
else if (parameters.Length == 3)
{
invokerType = typeof(InstanceFunctionInvoker<,,,,>).MakeGenericType(methodInfo.DeclaringType, parameters[0].ParameterType, parameters[1].ParameterType, parameters[2].ParameterType, methodInfo.ReturnType);
}
else if (parameters.Length == 4)
{
invokerType = typeof(InstanceFunctionInvoker<,,,,,>).MakeGenericType(methodInfo.DeclaringType, parameters[0].ParameterType, parameters[1].ParameterType, parameters[2].ParameterType, parameters[3].ParameterType, methodInfo.ReturnType);
}
else if (parameters.Length == 5)
{
invokerType = typeof(InstanceFunctionInvoker<,,,,,,>).MakeGenericType(methodInfo.DeclaringType, parameters[0].ParameterType, parameters[1].ParameterType, parameters[2].ParameterType, parameters[3].ParameterType, parameters[4].ParameterType, methodInfo.ReturnType);
}
else
{
throw new NotSupportedException();
}
}
}
invoker = (IOptimizedInvoker)Activator.CreateInstance(invokerType, methodInfo);
}
else
{
invoker = new ReflectionInvoker(methodInfo);
}
invoker.Compile();
methodInvokers.Add(methodInfo, invoker);
}
return invoker;
}
}
#endregion
}
}

View File

@@ -0,0 +1,31 @@
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class ReflectionFieldAccessor : IOptimizedAccessor
{
public ReflectionFieldAccessor(FieldInfo fieldInfo)
{
if (OptimizedReflection.safeMode)
{
Ensure.That(nameof(fieldInfo)).IsNotNull(fieldInfo);
}
this.fieldInfo = fieldInfo;
}
private readonly FieldInfo fieldInfo;
public void Compile() { }
public object GetValue(object target)
{
return fieldInfo.GetValue(target);
}
public void SetValue(object target, object value)
{
fieldInfo.SetValue(target, value);
}
}
}

View File

@@ -0,0 +1,65 @@
using System;
using System.Linq;
using System.Reflection;
namespace Unity.VisualScripting
{
public class ReflectionInvoker : IOptimizedInvoker
{
public ReflectionInvoker(MethodInfo methodInfo)
{
if (OptimizedReflection.safeMode)
{
Ensure.That(nameof(methodInfo)).IsNotNull(methodInfo);
}
this.methodInfo = methodInfo;
}
private readonly MethodInfo methodInfo;
public void Compile() { }
public object Invoke(object target, params object[] args)
{
return methodInfo.Invoke(target, args);
}
public object Invoke(object target)
{
return methodInfo.Invoke(target, EmptyObjects);
}
public object Invoke(object target, object arg0)
{
return methodInfo.Invoke(target, new[] { arg0 });
}
public object Invoke(object target, object arg0, object arg1)
{
return methodInfo.Invoke(target, new[] { arg0, arg1 });
}
public object Invoke(object target, object arg0, object arg1, object arg2)
{
return methodInfo.Invoke(target, new[] { arg0, arg1, arg2 });
}
public object Invoke(object target, object arg0, object arg1, object arg2, object arg3)
{
return methodInfo.Invoke(target, new[] { arg0, arg1, arg2, arg3 });
}
public object Invoke(object target, object arg0, object arg1, object arg2, object arg3, object arg4)
{
return methodInfo.Invoke(target, new[] { arg0, arg1, arg2, arg3, arg4 });
}
public Type[] GetParameterTypes()
{
return methodInfo.GetParameters().Select(pi => pi.ParameterType).ToArray();
}
private static readonly object[] EmptyObjects = new object[0];
}
}

View File

@@ -0,0 +1,31 @@
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class ReflectionPropertyAccessor : IOptimizedAccessor
{
public ReflectionPropertyAccessor(PropertyInfo propertyInfo)
{
if (OptimizedReflection.safeMode)
{
Ensure.That(nameof(propertyInfo)).IsNotNull(propertyInfo);
}
this.propertyInfo = propertyInfo;
}
private readonly PropertyInfo propertyInfo;
public void Compile() { }
public object GetValue(object target)
{
return propertyInfo.GetValue(target, null);
}
public void SetValue(object target, object value)
{
propertyInfo.SetValue(target, value, null);
}
}
}

View File

@@ -0,0 +1,9 @@
using System.Reflection;
namespace Unity.VisualScripting
{
public abstract class StaticActionInvokerBase : StaticInvokerBase
{
protected StaticActionInvokerBase(MethodInfo methodInfo) : base(methodInfo) { }
}
}

View File

@@ -0,0 +1,70 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class StaticActionInvoker : StaticActionInvokerBase
{
public StaticActionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Action invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 0)
{
throw new TargetParameterCountException();
}
return Invoke(target);
}
public override object Invoke(object target)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
try
{
return InvokeUnsafe(target);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target);
}
}
private object InvokeUnsafe(object target)
{
invoke.Invoke();
return null;
}
protected override Type[] GetParameterTypes()
{
return Type.EmptyTypes;
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Action>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = () => ((Action)methodInfo.CreateDelegate(typeof(Action)))();
}
}
}

View File

@@ -0,0 +1,71 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class StaticActionInvoker<TParam0> : StaticActionInvokerBase
{
public StaticActionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Action<TParam0> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 1)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0]);
}
public override object Invoke(object target, object arg0)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
try
{
return InvokeUnsafe(target, arg0);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0);
}
}
private object InvokeUnsafe(object target, object arg0)
{
invoke.Invoke((TParam0)arg0);
return null;
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Action<TParam0>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (param0) => ((Action<TParam0>)methodInfo.CreateDelegate(typeof(Action<TParam0>)))(param0);
}
}
}

View File

@@ -0,0 +1,72 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class StaticActionInvoker<TParam0, TParam1> : StaticActionInvokerBase
{
public StaticActionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Action<TParam0, TParam1> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 2)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0], args[1]);
}
public override object Invoke(object target, object arg0, object arg1)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
VerifyArgument<TParam1>(methodInfo, 0, arg1);
try
{
return InvokeUnsafe(target, arg0, arg1);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0, arg1);
}
}
public object InvokeUnsafe(object target, object arg0, object arg1)
{
invoke.Invoke((TParam0)arg0, (TParam1)arg1);
return null;
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0), typeof(TParam1) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Action<TParam0, TParam1>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (param0, param1) => ((Action<TParam0, TParam1>)methodInfo.CreateDelegate(typeof(Action<TParam0, TParam1>)))(param0, param1);
}
}
}

View File

@@ -0,0 +1,73 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class StaticActionInvoker<TParam0, TParam1, TParam2> : StaticActionInvokerBase
{
public StaticActionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Action<TParam0, TParam1, TParam2> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 3)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0], args[1], args[2]);
}
public override object Invoke(object target, object arg0, object arg1, object arg2)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
VerifyArgument<TParam1>(methodInfo, 1, arg1);
VerifyArgument<TParam2>(methodInfo, 2, arg2);
try
{
return InvokeUnsafe(target, arg0, arg1, arg2);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0, arg1, arg2);
}
}
public object InvokeUnsafe(object target, object arg0, object arg1, object arg2)
{
invoke.Invoke((TParam0)arg0, (TParam1)arg1, (TParam2)arg2);
return null;
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0), typeof(TParam1), typeof(TParam2) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Action<TParam0, TParam1, TParam2>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (param0, param1, param2) => ((Action<TParam0, TParam1, TParam2>)methodInfo.CreateDelegate(typeof(Action<TParam0, TParam1, TParam2>)))(param0, param1, param2);
}
}
}

View File

@@ -0,0 +1,74 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class StaticActionInvoker<TParam0, TParam1, TParam2, TParam3> : StaticActionInvokerBase
{
public StaticActionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Action<TParam0, TParam1, TParam2, TParam3> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 4)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0], args[1], args[2], args[3]);
}
public override object Invoke(object target, object arg0, object arg1, object arg2, object arg3)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
VerifyArgument<TParam1>(methodInfo, 1, arg1);
VerifyArgument<TParam2>(methodInfo, 2, arg2);
VerifyArgument<TParam3>(methodInfo, 3, arg3);
try
{
return InvokeUnsafe(target, arg0, arg1, arg2, arg3);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0, arg1, arg2, arg3);
}
}
public object InvokeUnsafe(object target, object arg0, object arg1, object arg2, object arg3)
{
invoke.Invoke((TParam0)arg0, (TParam1)arg1, (TParam2)arg2, (TParam3)arg3);
return null;
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0), typeof(TParam1), typeof(TParam2), typeof(TParam3) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Action<TParam0, TParam1, TParam2, TParam3>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (param0, param1, param2, param3) => ((Action<TParam0, TParam1, TParam2, TParam3>)methodInfo.CreateDelegate(typeof(Action<TParam0, TParam1, TParam2, TParam3>)))(param0, param1, param2, param3);
}
}
}

View File

@@ -0,0 +1,75 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class StaticActionInvoker<TParam0, TParam1, TParam2, TParam3, TParam4> : StaticActionInvokerBase
{
public StaticActionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Action<TParam0, TParam1, TParam2, TParam3, TParam4> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 5)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0], args[1], args[2], args[3], args[4]);
}
public override object Invoke(object target, object arg0, object arg1, object arg2, object arg3, object arg4)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
VerifyArgument<TParam1>(methodInfo, 1, arg1);
VerifyArgument<TParam2>(methodInfo, 2, arg2);
VerifyArgument<TParam3>(methodInfo, 3, arg3);
VerifyArgument<TParam4>(methodInfo, 4, arg4);
try
{
return InvokeUnsafe(target, arg0, arg1, arg2, arg3, arg4);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0, arg1, arg2, arg3, arg4);
}
}
public object InvokeUnsafe(object target, object arg0, object arg1, object arg2, object arg3, object arg4)
{
invoke.Invoke((TParam0)arg0, (TParam1)arg1, (TParam2)arg2, (TParam3)arg3, (TParam4)arg4);
return null;
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0), typeof(TParam1), typeof(TParam2), typeof(TParam3), typeof(TParam4) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Action<TParam0, TParam1, TParam2, TParam3, TParam4>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (param0, param1, param2, param3, param4) => ((Action<TParam0, TParam1, TParam2, TParam3, TParam4>)methodInfo.CreateDelegate(typeof(Action<TParam0, TParam1, TParam2, TParam3, TParam4>)))(param0, param1, param2, param3, param4);
}
}
}

View File

@@ -0,0 +1,164 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public class StaticFieldAccessor<TField> : IOptimizedAccessor
{
public StaticFieldAccessor(FieldInfo fieldInfo)
{
if (OptimizedReflection.safeMode)
{
if (fieldInfo == null)
{
throw new ArgumentNullException(nameof(fieldInfo));
}
if (fieldInfo.FieldType != typeof(TField))
{
throw new ArgumentException("Field type of field info doesn't match generic type.", nameof(fieldInfo));
}
if (!fieldInfo.IsStatic)
{
throw new ArgumentException("The field isn't static.", nameof(fieldInfo));
}
}
this.fieldInfo = fieldInfo;
targetType = fieldInfo.DeclaringType;
}
private readonly FieldInfo fieldInfo;
private Func<TField> getter;
private Action<TField> setter;
private Type targetType;
public void Compile()
{
if (fieldInfo.IsLiteral)
{
var constant = (TField)fieldInfo.GetValue(null);
getter = () => constant;
}
else
{
if (OptimizedReflection.useJit)
{
// Getter
var fieldExpression = Expression.Field(null, fieldInfo);
getter = Expression.Lambda<Func<TField>>(fieldExpression).Compile();
// Setter
if (fieldInfo.CanWrite())
{
#if UNITY_2018_3_OR_NEWER
var valueExpression = Expression.Parameter(typeof(TField));
var assignExpression = Expression.Assign(fieldExpression, valueExpression);
setter = Expression.Lambda<Action<TField>>(assignExpression, valueExpression).Compile();
#else
var setterMethod = new DynamicMethod
(
"setter",
typeof(void),
new[] { typeof(TField) },
targetType,
true
);
var setterIL = setterMethod.GetILGenerator();
setterIL.Emit(OpCodes.Ldarg_0);
setterIL.Emit(OpCodes.Stsfld, fieldInfo);
setterIL.Emit(OpCodes.Ret);
setter = (Action<TField>)setterMethod.CreateDelegate(typeof(Action<TField>));
#endif
}
}
else
{
// If no JIT is available, we can only use reflection.
getter = () => (TField)fieldInfo.GetValue(null);
if (fieldInfo.CanWrite())
{
setter = (value) => fieldInfo.SetValue(null, value);
}
}
}
}
public object GetValue(object target)
{
if (OptimizedReflection.safeMode)
{
OptimizedReflection.VerifyStaticTarget(targetType, target);
try
{
return GetValueUnsafe(target);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return GetValueUnsafe(target);
}
}
private object GetValueUnsafe(object target)
{
return getter.Invoke();
}
public void SetValue(object target, object value)
{
if (OptimizedReflection.safeMode)
{
OptimizedReflection.VerifyStaticTarget(targetType, target);
if (setter == null)
{
throw new TargetException($"The field '{targetType}.{fieldInfo.Name}' cannot be assigned.");
}
if (!typeof(TField).IsAssignableFrom(value))
{
throw new ArgumentException($"The provided value for '{targetType}.{fieldInfo.Name}' does not match the field type.\nProvided: {value?.GetType()?.ToString() ?? "null"}\nExpected: {typeof(TField)}");
}
try
{
SetValueUnsafe(target, value);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
SetValueUnsafe(target, value);
}
}
private void SetValueUnsafe(object target, object value)
{
setter.Invoke((TField)value);
}
}
}

View File

@@ -0,0 +1,19 @@
using System;
using System.Reflection;
namespace Unity.VisualScripting
{
public abstract class StaticFunctionInvokerBase<TResult> : StaticInvokerBase
{
protected StaticFunctionInvokerBase(MethodInfo methodInfo) : base(methodInfo)
{
if (OptimizedReflection.safeMode)
{
if (methodInfo.ReturnType != typeof(TResult))
{
throw new ArgumentException("Return type of method info doesn't match generic type.", nameof(methodInfo));
}
}
}
}
}

View File

@@ -0,0 +1,68 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class StaticFunctionInvoker<TResult> : StaticFunctionInvokerBase<TResult>
{
public StaticFunctionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Func<TResult> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 0)
{
throw new TargetParameterCountException();
}
return Invoke(target);
}
public override object Invoke(object target)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
try
{
return InvokeUnsafe(target);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target);
}
}
public object InvokeUnsafe(object target)
{
return invoke.Invoke();
}
protected override Type[] GetParameterTypes()
{
return Type.EmptyTypes;
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Func<TResult>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = () => ((Func<TResult>)methodInfo.CreateDelegate(typeof(Func<TResult>)))();
}
}
}

View File

@@ -0,0 +1,69 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class StaticFunctionInvoker<TParam0, TResult> : StaticFunctionInvokerBase<TResult>
{
public StaticFunctionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Func<TParam0, TResult> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 1)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0]);
}
public override object Invoke(object target, object arg0)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
try
{
return InvokeUnsafe(target, arg0);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0);
}
}
public object InvokeUnsafe(object target, object arg0)
{
return invoke.Invoke((TParam0)arg0);
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Func<TParam0, TResult>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = param0 => ((Func<TParam0, TResult>)methodInfo.CreateDelegate(typeof(Func<TParam0, TResult>)))(param0);
}
}
}

View File

@@ -0,0 +1,70 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class StaticFunctionInvoker<TParam0, TParam1, TResult> : StaticFunctionInvokerBase<TResult>
{
public StaticFunctionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Func<TParam0, TParam1, TResult> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 2)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0], args[1]);
}
public override object Invoke(object target, object arg0, object arg1)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
VerifyArgument<TParam1>(methodInfo, 1, arg1);
try
{
return InvokeUnsafe(target, arg0, arg1);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0, arg1);
}
}
public object InvokeUnsafe(object target, object arg0, object arg1)
{
return invoke.Invoke((TParam0)arg0, (TParam1)arg1);
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0), typeof(TParam1) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Func<TParam0, TParam1, TResult>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (param0, param1) => ((Func<TParam0, TParam1, TResult>)methodInfo.CreateDelegate(typeof(Func<TParam0, TParam1, TResult>)))(param0, param1);
}
}
}

View File

@@ -0,0 +1,71 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class StaticFunctionInvoker<TParam0, TParam1, TParam2, TResult> : StaticFunctionInvokerBase<TResult>
{
public StaticFunctionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Func<TParam0, TParam1, TParam2, TResult> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 3)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0], args[1], args[2]);
}
public override object Invoke(object target, object arg0, object arg1, object arg2)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
VerifyArgument<TParam1>(methodInfo, 1, arg1);
VerifyArgument<TParam2>(methodInfo, 2, arg2);
try
{
return InvokeUnsafe(target, arg0, arg1, arg2);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0, arg1, arg2);
}
}
public object InvokeUnsafe(object target, object arg0, object arg1, object arg2)
{
return invoke.Invoke((TParam0)arg0, (TParam1)arg1, (TParam2)arg2);
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0), typeof(TParam1), typeof(TParam2) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Func<TParam0, TParam1, TParam2, TResult>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (param0, param1, param2) => ((Func<TParam0, TParam1, TParam2, TResult>)methodInfo.CreateDelegate(typeof(Func<TParam0, TParam1, TParam2, TResult>)))(param0, param1, param2);
}
}
}

View File

@@ -0,0 +1,72 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class StaticFunctionInvoker<TParam0, TParam1, TParam2, TParam3, TResult> : StaticFunctionInvokerBase<TResult>
{
public StaticFunctionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Func<TParam0, TParam1, TParam2, TParam3, TResult> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 4)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0], args[1], args[2], args[3]);
}
public override object Invoke(object target, object arg0, object arg1, object arg2, object arg3)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
VerifyArgument<TParam1>(methodInfo, 1, arg1);
VerifyArgument<TParam2>(methodInfo, 2, arg2);
VerifyArgument<TParam3>(methodInfo, 3, arg3);
try
{
return InvokeUnsafe(target, arg0, arg1, arg2, arg3);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0, arg1, arg2, arg3);
}
}
public object InvokeUnsafe(object target, object arg0, object arg1, object arg2, object arg3)
{
return invoke.Invoke((TParam0)arg0, (TParam1)arg1, (TParam2)arg2, (TParam3)arg3);
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0), typeof(TParam1), typeof(TParam2), typeof(TParam3) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Func<TParam0, TParam1, TParam2, TParam3, TResult>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (param0, param1, param2, param3) => ((Func<TParam0, TParam1, TParam2, TParam3, TResult>)methodInfo.CreateDelegate(typeof(Func<TParam0, TParam1, TParam2, TParam3, TResult>)))(param0, param1, param2, param3);
}
}
}

View File

@@ -0,0 +1,73 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class StaticFunctionInvoker<TParam0, TParam1, TParam2, TParam3, TParam4, TResult> : StaticFunctionInvokerBase<TResult>
{
public StaticFunctionInvoker(MethodInfo methodInfo) : base(methodInfo) { }
private Func<TParam0, TParam1, TParam2, TParam3, TParam4, TResult> invoke;
public override object Invoke(object target, params object[] args)
{
if (args.Length != 5)
{
throw new TargetParameterCountException();
}
return Invoke(target, args[0], args[1], args[2], args[3], args[4]);
}
public override object Invoke(object target, object arg0, object arg1, object arg2, object arg3, object arg4)
{
if (OptimizedReflection.safeMode)
{
VerifyTarget(target);
VerifyArgument<TParam0>(methodInfo, 0, arg0);
VerifyArgument<TParam1>(methodInfo, 1, arg1);
VerifyArgument<TParam2>(methodInfo, 2, arg2);
VerifyArgument<TParam3>(methodInfo, 3, arg3);
VerifyArgument<TParam4>(methodInfo, 4, arg4);
try
{
return InvokeUnsafe(target, arg0, arg1, arg2, arg3, arg4);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return InvokeUnsafe(target, arg0, arg1, arg2, arg3, arg4);
}
}
public object InvokeUnsafe(object target, object arg0, object arg1, object arg2, object arg3, object arg4)
{
return invoke.Invoke((TParam0)arg0, (TParam1)arg1, (TParam2)arg2, (TParam3)arg3, (TParam4)arg4);
}
protected override Type[] GetParameterTypes()
{
return new[] { typeof(TParam0), typeof(TParam1), typeof(TParam2), typeof(TParam3), typeof(TParam4) };
}
protected override void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions)
{
invoke = Expression.Lambda<Func<TParam0, TParam1, TParam2, TParam3, TParam4, TResult>>(callExpression, parameterExpressions).Compile();
}
protected override void CreateDelegate()
{
invoke = (param0, param1, param2, param3, param4) => ((Func<TParam0, TParam1, TParam2, TParam3, TParam4, TResult>)methodInfo.CreateDelegate(typeof(Func<TParam0, TParam1, TParam2, TParam3, TParam4, TResult>)))(param0, param1, param2, param3, param4);
}
}
}

View File

@@ -0,0 +1,35 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public abstract class StaticInvokerBase : InvokerBase
{
protected StaticInvokerBase(MethodInfo methodInfo) : base(methodInfo)
{
if (OptimizedReflection.safeMode)
{
if (!methodInfo.IsStatic)
{
throw new ArgumentException("The method isn't static.", nameof(methodInfo));
}
}
}
protected sealed override void CompileExpression()
{
var parameterExpressions = GetParameterExpressions();
var callExpression = Expression.Call(methodInfo, parameterExpressions);
CompileExpression(callExpression, parameterExpressions);
}
protected abstract void CompileExpression(MethodCallExpression callExpression, ParameterExpression[] parameterExpressions);
protected override void VerifyTarget(object target)
{
OptimizedReflection.VerifyStaticTarget(targetType, target);
}
}
}

View File

@@ -0,0 +1,145 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Unity.VisualScripting
{
public class StaticPropertyAccessor<TProperty> : IOptimizedAccessor
{
public StaticPropertyAccessor(PropertyInfo propertyInfo)
{
if (OptimizedReflection.safeMode)
{
if (propertyInfo == null)
{
throw new ArgumentNullException(nameof(propertyInfo));
}
if (propertyInfo.PropertyType != typeof(TProperty))
{
throw new ArgumentException("The property type of the property info doesn't match the generic type.", nameof(propertyInfo));
}
if (!propertyInfo.IsStatic())
{
throw new ArgumentException("The property isn't static.", nameof(propertyInfo));
}
}
this.propertyInfo = propertyInfo;
targetType = propertyInfo.DeclaringType;
}
private readonly PropertyInfo propertyInfo;
private Func<TProperty> getter;
private Action<TProperty> setter;
private Type targetType;
public void Compile()
{
var getterInfo = propertyInfo.GetGetMethod(true);
var setterInfo = propertyInfo.GetSetMethod(true);
if (OptimizedReflection.useJit)
{
if (getterInfo != null)
{
var propertyExpression = Expression.Property(null, propertyInfo);
getter = Expression.Lambda<Func<TProperty>>(propertyExpression).Compile();
}
if (setterInfo != null)
{
setter = (Action<TProperty>)setterInfo.CreateDelegate(typeof(Action<TProperty>));
}
}
else
{
if (getterInfo != null)
{
getter = (Func<TProperty>)getterInfo.CreateDelegate(typeof(Func<TProperty>));
}
if (setterInfo != null)
{
setter = (Action<TProperty>)setterInfo.CreateDelegate(typeof(Action<TProperty>));
}
}
}
public object GetValue(object target)
{
if (OptimizedReflection.safeMode)
{
OptimizedReflection.VerifyStaticTarget(targetType, target);
if (getter == null)
{
throw new TargetException($"The property '{targetType}.{propertyInfo.Name}' has no get accessor.");
}
try
{
return GetValueUnsafe(target);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return GetValueUnsafe(target);
}
}
private object GetValueUnsafe(object target)
{
return getter.Invoke();
}
public void SetValue(object target, object value)
{
if (OptimizedReflection.safeMode)
{
OptimizedReflection.VerifyStaticTarget(targetType, target);
if (setter == null)
{
throw new TargetException($"The property '{targetType}.{propertyInfo.Name}' has no set accessor.");
}
if (!typeof(TProperty).IsAssignableFrom(value))
{
throw new ArgumentException($"The provided value for '{targetType}.{propertyInfo.Name}' does not match the property type.\nProvided: {value?.GetType()?.ToString() ?? "null"}\nExpected: {typeof(TProperty)}");
}
try
{
SetValueUnsafe(target, value);
}
catch (TargetInvocationException)
{
throw;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
SetValueUnsafe(target, value);
}
}
private void SetValueUnsafe(object target, object value)
{
setter.Invoke((TProperty)value);
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
namespace Unity.VisualScripting
{
// Allows us to migrate old serialized namespaces to new ones
// Ex usage: [assembly: RenamedAssembly("Bolt.Core", "Unity.VisualScripting.Core")]
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class RenamedAssemblyAttribute : Attribute
{
public RenamedAssemblyAttribute(string previousName, string newName)
{
this.previousName = previousName;
this.newName = newName;
}
public string previousName { get; }
public string newName { get; }
}
}

View File

@@ -0,0 +1,15 @@
using System;
namespace Unity.VisualScripting
{
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
public sealed class RenamedFromAttribute : Attribute
{
public RenamedFromAttribute(string previousName)
{
this.previousName = previousName;
}
public string previousName { get; }
}
}

View File

@@ -0,0 +1,20 @@
using System;
namespace Unity.VisualScripting
{
// Allows us to migrate old serialized namespaces to new ones
// Ex usage: [assembly: RenamedNamespace("Bolt", "Unity.VisualScripting")]
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class RenamedNamespaceAttribute : Attribute
{
public RenamedNamespaceAttribute(string previousName, string newName)
{
this.previousName = previousName;
this.newName = newName;
}
public string previousName { get; }
public string newName { get; }
}
}

View File

@@ -0,0 +1,460 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using Unity.VisualScripting.AssemblyQualifiedNameParser;
using UnityEngine;
using Exception = System.Exception;
using Unity.VisualScripting;
namespace Unity.VisualScripting
{
public static class RuntimeCodebase
{
private static readonly object @lock = new object();
private static readonly List<Type> _types = new List<Type>();
public static IEnumerable<Type> types => _types;
private static readonly List<Assembly> _assemblies = new List<Assembly>();
public static IEnumerable<Assembly> assemblies => _assemblies;
/* (disallowedAssemblies)
This is a hack to force our RuntimeCodebase to use the RenamedTypeLookup for certain types when we deserialize them.
When we migrate from asset store to package assemblies (With new names), we want to deserialize our types
to the new types with new namespaces that exist in our new assemblies
(Ex: Unity.VisualScripting.SuperUnit instead of Bolt.SuperUnit).
Problem arises because we're migrating via script. Deleting the old assembly files on the disk doesn't remove
them from our AppDomain, and we can't unload specific assemblies.
Reloading the whole AppDomain would reload the migration scripts too, which would re-trigger the whole
migration flow and be bad UX.
So to avoid this problem, we don't reload the AppDomain (old assemblies still loaded) but just avoid them when
trying to deserialize types temporarily. When we Domain Reload at the end, it's cleaned up.
Without this, we get deserialization errors on migration to do with trying to instantiate a new type from an
old interface type.
This shouldn't cause much of a perf difference for most use because all our types are cached anyway,
and logic to do with this sits beyond the cached types layer.
*/
public static HashSet<string> disallowedAssemblies = new HashSet<string>();
private static readonly Dictionary<string, Type> typeSerializations = new Dictionary<string, Type>();
private static Dictionary<string, Type> _renamedTypes = null;
private static Dictionary<string, string> _renamedNamespaces = null;
private static Dictionary<string, string> _renamedAssemblies = null;
private static readonly Dictionary<Type, Dictionary<string, string>> _renamedMembers = new Dictionary<Type, Dictionary<string, string>>();
static RuntimeCodebase()
{
lock (@lock)
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
_assemblies.Add(assembly);
foreach (var assemblyType in assembly.GetTypesSafely())
{
_types.Add(assemblyType);
}
}
}
}
#region Assembly Attributes
public static IEnumerable<Attribute> GetAssemblyAttributes(Type attributeType)
{
return GetAssemblyAttributes(attributeType, assemblies);
}
public static IEnumerable<Attribute> GetAssemblyAttributes(Type attributeType, IEnumerable<Assembly> assemblies)
{
Ensure.That(nameof(attributeType)).IsNotNull(attributeType);
Ensure.That(nameof(assemblies)).IsNotNull(assemblies);
foreach (var assembly in assemblies)
{
foreach (var attribute in assembly.GetCustomAttributes(attributeType))
{
if (attributeType.IsInstanceOfType(attribute))
{
yield return attribute;
}
}
}
}
public static IEnumerable<TAttribute> GetAssemblyAttributes<TAttribute>(IEnumerable<Assembly> assemblies) where TAttribute : Attribute
{
return GetAssemblyAttributes(typeof(TAttribute), assemblies).Cast<TAttribute>();
}
public static IEnumerable<TAttribute> GetAssemblyAttributes<TAttribute>() where TAttribute : Attribute
{
return GetAssemblyAttributes(typeof(TAttribute)).Cast<TAttribute>();
}
#endregion
#region Serialization
public static void PrewarmTypeDeserialization(Type type)
{
Ensure.That(nameof(type)).IsNotNull(type);
var serialization = SerializeType(type);
if (typeSerializations.ContainsKey(serialization))
{
// Some are duplicates, but almost always compiler generated stuff.
// Safe to ignore, and anyway what would we even do to deserialize them properly?
}
else
{
typeSerializations.Add(serialization, type);
}
}
public static string SerializeType(Type type)
{
Ensure.That(nameof(type)).IsNotNull(type);
return type?.FullName;
}
public static bool TryDeserializeType(string typeName, out Type type)
{
if (string.IsNullOrEmpty(typeName))
{
type = null;
return false;
}
lock (@lock)
{
if (!TryCachedTypeLookup(typeName, out type))
{
if (!TrySystemTypeLookup(typeName, out type))
{
if (!TryRenamedTypeLookup(typeName, out type))
{
return false;
}
}
typeSerializations.Add(typeName, type);
}
return true;
}
}
public static Type DeserializeType(string typeName)
{
if (!TryDeserializeType(typeName, out var type))
{
throw new SerializationException($"Unable to find type: '{typeName ?? "(null)"}'.");
}
return type;
}
public static void ClearCachedTypes()
{
typeSerializations.Clear();
}
private static bool TryCachedTypeLookup(string typeName, out Type type)
{
return typeSerializations.TryGetValue(typeName, out type);
}
private static bool TrySystemTypeLookup(string typeName, out Type type)
{
foreach (var assembly in _assemblies)
{
if (disallowedAssemblies.Contains(assembly.GetName().Name))
continue;
type = assembly.GetType(typeName);
if (type != null)
{
return true;
}
}
type = null;
return false;
}
private static bool TrySystemTypeLookup(TypeName typeName, out Type type)
{
if (disallowedAssemblies.Contains(typeName.AssemblyName))
{
type = null;
return false;
}
// Can't retrieve an array with the ToLooseString format so use the type Name and compare Assemblies
if (typeName.IsArray)
{
foreach (var assembly in _assemblies.Where(a => typeName.AssemblyName == a.GetName().Name))
{
type = assembly.GetType(typeName.Name);
if (type != null)
{
return true;
}
}
type = null;
return false;
}
return TrySystemTypeLookup(typeName.ToLooseString(), out type);
}
private static bool TryRenamedTypeLookup(string previousTypeName, out Type type)
{
// Try for an exact match in our renamed types dictionary.
// That should work for every non-generic type.
if (renamedTypes.TryGetValue(previousTypeName, out var newType))
{
type = newType;
return true;
}
// If we can't get an exact match, we'll try parsing the previous type name,
// replacing all the renamed types we can find, then reconstructing it.
else
{
var parsedTypeName = TypeName.Parse(previousTypeName);
foreach (var renamedType in renamedTypes)
{
parsedTypeName.ReplaceName(renamedType.Key, renamedType.Value);
}
foreach (var renamedNamespace in renamedNamespaces)
{
parsedTypeName.ReplaceNamespace(renamedNamespace.Key, renamedNamespace.Value);
}
foreach (var renamedAssembly in renamedAssemblies)
{
parsedTypeName.ReplaceAssembly(renamedAssembly.Key, renamedAssembly.Value);
}
// Run the system lookup
if (TrySystemTypeLookup(parsedTypeName, out type))
{
return true;
}
type = null;
return false;
}
}
#endregion
#region Renaming
// Can't use AttributeUtility here, because the caching system will
// try to load all attributes of the type for efficiency, which is
// not allowed on the serialization thread because some of Unity's
// attribute constructors use Unity API methods (ugh!).
public static Dictionary<string, string> renamedNamespaces
{
get
{
if (_renamedNamespaces == null)
{
_renamedNamespaces = FetchRenamedNamespaces();
}
return _renamedNamespaces;
}
}
public static Dictionary<string, string> renamedAssemblies
{
get
{
if (_renamedAssemblies == null)
{
_renamedAssemblies = FetchRenamedAssemblies();
}
return _renamedAssemblies;
}
}
public static Dictionary<string, Type> renamedTypes
{
get
{
if (_renamedTypes == null)
{
// Fetch only on demand because attribute lookups are expensive
_renamedTypes = FetchRenamedTypes();
}
return _renamedTypes;
}
}
public static Dictionary<string, string> RenamedMembers(Type type)
{
Dictionary<string, string> renamedMembers;
if (!_renamedMembers.TryGetValue(type, out renamedMembers))
{
renamedMembers = FetchRenamedMembers(type);
_renamedMembers.Add(type, renamedMembers);
}
return renamedMembers;
}
private static Dictionary<string, string> FetchRenamedMembers(Type type)
{
Ensure.That(nameof(type)).IsNotNull(type);
var renamedMembers = new Dictionary<string, string>();
var members = type.GetExtendedMembers(Member.SupportedBindingFlags);
foreach (var member in members)
{
IEnumerable<RenamedFromAttribute> renamedFromAttributes;
try
{
renamedFromAttributes = Attribute.GetCustomAttributes(member, typeof(RenamedFromAttribute), false).Cast<RenamedFromAttribute>();
}
catch (Exception ex)
{
Debug.LogWarning($"Failed to fetch RenamedFrom attributes for member '{member}':\n{ex}");
continue;
}
var newMemberName = member.Name;
foreach (var renamedFromAttribute in renamedFromAttributes)
{
var previousMemberName = renamedFromAttribute.previousName;
if (renamedMembers.ContainsKey(previousMemberName))
{
Debug.LogWarning($"Multiple members on '{type}' indicate having been renamed from '{previousMemberName}'.\nIgnoring renamed attributes for '{member}'.");
continue;
}
renamedMembers.Add(previousMemberName, newMemberName);
}
}
return renamedMembers;
}
private static Dictionary<string, string> FetchRenamedNamespaces()
{
var renamedNamespaces = new Dictionary<string, string>();
foreach (var renamedNamespaceAttribute in GetAssemblyAttributes<RenamedNamespaceAttribute>())
{
var previousNamespaceName = renamedNamespaceAttribute.previousName;
var newNamespaceName = renamedNamespaceAttribute.newName;
if (renamedNamespaces.ContainsKey(previousNamespaceName))
{
Debug.LogWarning($"Multiple new names have been provided for namespace '{previousNamespaceName}'.\nIgnoring new name '{newNamespaceName}'.");
continue;
}
renamedNamespaces.Add(previousNamespaceName, newNamespaceName);
}
return renamedNamespaces;
}
private static Dictionary<string, string> FetchRenamedAssemblies()
{
var renamedAssemblies = new Dictionary<string, string>();
foreach (var renamedAssemblyAttribute in GetAssemblyAttributes<RenamedAssemblyAttribute>())
{
var previousAssemblyName = renamedAssemblyAttribute.previousName;
var newAssemblyName = renamedAssemblyAttribute.newName;
if (renamedAssemblies.ContainsKey(previousAssemblyName))
{
Debug.LogWarning($"Multiple new names have been provided for assembly '{previousAssemblyName}'.\nIgnoring new name '{newAssemblyName}'.");
continue;
}
renamedAssemblies.Add(previousAssemblyName, newAssemblyName);
}
return renamedAssemblies;
}
private static Dictionary<string, Type> FetchRenamedTypes()
{
var renamedTypes = new Dictionary<string, Type>();
foreach (var assembly in assemblies)
{
foreach (var type in assembly.GetTypesSafely())
{
IEnumerable<RenamedFromAttribute> renamedFromAttributes;
try
{
renamedFromAttributes = Attribute.GetCustomAttributes(type, typeof(RenamedFromAttribute), false).Cast<RenamedFromAttribute>();
}
catch (Exception ex)
{
Debug.LogWarning($"Failed to fetch RenamedFrom attributes for type '{type}':\n{ex}");
continue;
}
var newTypeName = type.FullName;
foreach (var renamedFromAttribute in renamedFromAttributes)
{
var previousTypeName = renamedFromAttribute.previousName;
if (renamedTypes.ContainsKey(previousTypeName))
{
Debug.LogWarning($"Multiple types indicate having been renamed from '{previousTypeName}'.\nIgnoring renamed attributes for '{type}'.");
continue;
}
renamedTypes.Add(previousTypeName, type);
}
}
}
return renamedTypes;
}
#endregion
}
}

View File

@@ -0,0 +1,331 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
namespace Unity.VisualScripting
{
/// <summary>
/// Filters the list of types displayed in the inspector drawer.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public sealed class TypeFilter : Attribute, ICloneable
{
public TypeFilter(TypesMatching matching, IEnumerable<Type> types)
{
Ensure.That(nameof(types)).IsNotNull(types);
Matching = matching;
this.types = new HashSet<Type>(types);
Value = true;
Reference = true;
Classes = true;
Interfaces = true;
Structs = true;
Enums = true;
Public = true;
NonPublic = false;
Abstract = true;
Generic = true;
OpenConstructedGeneric = false;
Static = true;
Sealed = true;
Nested = true;
Primitives = true;
Object = true;
NonSerializable = true;
Obsolete = false;
}
public TypeFilter(TypesMatching matching, params Type[] types) : this(matching, (IEnumerable<Type>)types) { }
public TypeFilter(IEnumerable<Type> types) : this(TypesMatching.ConvertibleToAny, types) { }
public TypeFilter(params Type[] types) : this(TypesMatching.ConvertibleToAny, types) { }
private readonly HashSet<Type> types;
public TypesMatching Matching { get; set; }
public HashSet<Type> Types => types;
public bool Value { get; set; }
public bool Reference { get; set; }
public bool Classes { get; set; }
public bool Interfaces { get; set; }
public bool Structs { get; set; }
public bool Enums { get; set; }
public bool Public { get; set; }
public bool NonPublic { get; set; }
public bool Abstract { get; set; }
public bool Generic { get; set; }
public bool OpenConstructedGeneric { get; set; }
public bool Static { get; set; }
public bool Sealed { get; set; }
public bool Nested { get; set; }
public bool Primitives { get; set; }
public bool Object { get; set; }
public bool NonSerializable { get; set; }
public bool Obsolete { get; set; }
public bool ExpectsBoolean => Types.Count == 1 && Types.Single() == typeof(bool);
object ICloneable.Clone()
{
return Clone();
}
public TypeFilter Clone()
{
return new TypeFilter(Matching, Types.ToArray())
{
Value = Value,
Reference = Reference,
Classes = Classes,
Interfaces = Interfaces,
Structs = Structs,
Enums = Enums,
Public = Public,
NonPublic = NonPublic,
Abstract = Abstract,
Generic = Generic,
OpenConstructedGeneric = OpenConstructedGeneric,
Static = Static,
Sealed = Sealed,
Nested = Nested,
Primitives = Primitives,
Object = Object,
NonSerializable = NonSerializable,
Obsolete = Obsolete
};
}
public override bool Equals(object obj)
{
var other = obj as TypeFilter;
if (other == null)
{
return false;
}
return
Matching == other.Matching &&
types.SetEquals(other.types) &&
Value == other.Value &&
Reference == other.Reference &&
Classes == other.Classes &&
Interfaces == other.Interfaces &&
Structs == other.Structs &&
Enums == other.Enums &&
Public == other.Public &&
NonPublic == other.NonPublic &&
Abstract == other.Abstract &&
Generic == other.Generic &&
OpenConstructedGeneric == other.OpenConstructedGeneric &&
Static == other.Static &&
Sealed == other.Sealed &&
Nested == other.Nested &&
Primitives == other.Primitives &&
Object == other.Object &&
NonSerializable == other.NonSerializable &&
Obsolete == other.Obsolete;
}
public override int GetHashCode()
{
unchecked
{
var hash = 17;
hash = hash * 23 + Matching.GetHashCode();
foreach (var type in types)
{
if (type != null)
{
hash = hash * 23 + type.GetHashCode();
}
}
hash = hash * 23 + Value.GetHashCode();
hash = hash * 23 + Reference.GetHashCode();
hash = hash * 23 + Classes.GetHashCode();
hash = hash * 23 + Interfaces.GetHashCode();
hash = hash * 23 + Structs.GetHashCode();
hash = hash * 23 + Enums.GetHashCode();
hash = hash * 23 + Public.GetHashCode();
hash = hash * 23 + NonPublic.GetHashCode();
hash = hash * 23 + Abstract.GetHashCode();
hash = hash * 23 + Generic.GetHashCode();
hash = hash * 23 + OpenConstructedGeneric.GetHashCode();
hash = hash * 23 + Static.GetHashCode();
hash = hash * 23 + Sealed.GetHashCode();
hash = hash * 23 + Nested.GetHashCode();
hash = hash * 23 + Primitives.GetHashCode();
hash = hash * 23 + Object.GetHashCode();
hash = hash * 23 + NonSerializable.GetHashCode();
hash = hash * 23 + Obsolete.GetHashCode();
return hash;
}
}
public bool ValidateType(Type type)
{
Ensure.That(nameof(type)).IsNotNull(type);
if (!Generic && type.IsGenericType)
{
return false;
}
if (!OpenConstructedGeneric && type.ContainsGenericParameters)
{
return false;
}
if (!Value && type.IsValueType)
{
return false;
}
if (!Reference && !type.IsValueType)
{
return false;
}
if (!Classes && type.IsClass)
{
return false;
}
if (!Interfaces && type.IsInterface)
{
return false;
}
if (!Structs && (type.IsValueType && !type.IsEnum && !type.IsPrimitive))
{
return false;
}
if (!Enums && type.IsEnum)
{
return false;
}
if (!Public && type.IsVisible)
{
return false;
}
if (!NonPublic && !type.IsVisible)
{
return false;
}
if (!Abstract && type.IsAbstract())
{
return false;
}
if (!Static && type.IsStatic())
{
return false;
}
if (!Sealed && type.IsSealed)
{
return false;
}
if (!Nested && type.IsNested)
{
return false;
}
if (!Primitives && type.IsPrimitive)
{
return false;
}
if (!Object && type == typeof(object))
{
return false;
}
if (!NonSerializable && !type.IsSerializable)
{
return false;
}
if (type.IsSpecialName || type.HasAttribute<CompilerGeneratedAttribute>())
{
return false;
}
if (!Obsolete && type.HasAttribute<ObsoleteAttribute>())
{
return false;
}
var valid = true;
if (Types.Count > 0)
{
valid = Matching == TypesMatching.AssignableToAll;
foreach (var allowedType in Types)
{
if (Matching == TypesMatching.Any)
{
if (type == allowedType)
{
valid = true;
break;
}
}
else if (Matching == TypesMatching.ConvertibleToAny)
{
if (type.IsConvertibleTo(allowedType, true))
{
valid = true;
break;
}
}
else if (Matching == TypesMatching.AssignableToAll)
{
valid &= allowedType.IsAssignableFrom(type);
if (!valid)
{
break;
}
}
else
{
throw new UnexpectedEnumValueException<TypesMatching>(Matching);
}
}
}
return valid;
}
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine($"Matching: {Matching}");
sb.AppendLine($"Types: {types.ToCommaSeparatedString()}");
sb.AppendLine();
sb.AppendLine($"Value: {Value}");
sb.AppendLine($"Reference: {Reference}");
sb.AppendLine($"Classes: {Classes}");
sb.AppendLine($"Interfaces: {Interfaces}");
sb.AppendLine($"Structs: {Structs}");
sb.AppendLine($"Enums: {Enums}");
sb.AppendLine($"Public: {Public}");
sb.AppendLine($"NonPublic: {NonPublic}");
sb.AppendLine($"Abstract: {Abstract}");
sb.AppendLine($"Generic: {Generic}");
sb.AppendLine($"OpenConstructedGeneric: {OpenConstructedGeneric}");
sb.AppendLine($"Static: {Static}");
sb.AppendLine($"Sealed: {Sealed}");
sb.AppendLine($"Nested: {Nested}");
sb.AppendLine($"Primitives: {Primitives}");
sb.AppendLine($"Object: {Object}");
sb.AppendLine($"NonSerializable: {NonSerializable}");
sb.AppendLine($"Obsolete: {Obsolete}");
return sb.ToString();
}
public static TypeFilter Any => new TypeFilter();
}
}

View File

@@ -0,0 +1,459 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace Unity.VisualScripting
{
// Adapted from AssemblyQualifiedNameParser
public class TypeName
{
public string AssemblyDescription { get; private set; }
public string AssemblyName { get; private set; }
public string AssemblyVersion { get; private set; }
public string AssemblyCulture { get; private set; }
public string AssemblyPublicKeyToken { get; private set; }
public List<TypeName> GenericParameters { get; } = new List<TypeName>();
private readonly List<string> names = new List<string>();
private readonly List<int> genericarities = new List<int>();
public string Name { get; private set; }
public bool IsArray => Name.EndsWith("[]");
public string LastName => names[names.Count - 1];
public static TypeName Parse(string s)
{
var index = 0;
return new TypeName(s, ref index);
}
private enum ParseState
{
Name,
Array,
Generics,
Assembly
}
private TypeName(string s, ref int index)
{
try
{
var startIndex = index;
var nameStartIndex = startIndex;
var nameEndIndex = (int?)null;
var assemblyDescriptionStartIndex = (int?)null;
var assemblyDescriptionEndIndex = (int?)null;
var hasGroupingBracket = false;
var state = ParseState.Name;
for (; index < s.Length; ++index)
{
var currentCharacter = s[index];
var nextCharacter = index + 1 < s.Length ? s[index + 1] : (char?)null;
if (state == ParseState.Name)
{
if (currentCharacter == '[')
{
if (index == startIndex)
{
// Skip type grouping bracket
hasGroupingBracket = true;
nameStartIndex++;
}
else if (nextCharacter == ']' || nextCharacter == ',')
{
// Square bracket delimits an array
state = ParseState.Array;
}
else
{
// Square bracket delimits the generic argument list
nameEndIndex = index;
state = ParseState.Generics;
}
}
else if (currentCharacter == ']')
{
if (hasGroupingBracket)
{
// We finished the current grouping, break out
break;
}
}
else if (currentCharacter == ',')
{
// We're entering assembly description
state = ParseState.Assembly;
assemblyDescriptionStartIndex = index + 1;
if (nameEndIndex == null)
{
nameEndIndex = index;
}
}
}
else if (state == ParseState.Array)
{
if (currentCharacter == ']')
{
state = ParseState.Name;
}
}
else if (state == ParseState.Generics)
{
if (currentCharacter == ']')
{
state = ParseState.Name;
}
else if (currentCharacter == ',' || currentCharacter == ' ')
{
// Generic delimiters
}
else
{
GenericParameters.Add(new TypeName(s, ref index));
}
}
else if (state == ParseState.Assembly)
{
if (currentCharacter == ']')
{
if (hasGroupingBracket)
{
// We finished the current grouping, break out
assemblyDescriptionEndIndex = index;
break;
}
}
}
}
if (nameEndIndex == null)
{
nameEndIndex = s.Length;
}
if (assemblyDescriptionEndIndex == null)
{
assemblyDescriptionEndIndex = s.Length;
}
Name = s.Substring(nameStartIndex, nameEndIndex.Value - nameStartIndex);
if (Name.Contains('+'))
{
var nestedNames = Name.Split('+');
foreach (var nestedName in nestedNames)
{
nestedName.PartsAround('`', out var name, out var genericarity);
names.Add(name);
if (genericarity != null)
{
genericarities.Add(int.Parse(genericarity));
}
else
{
genericarities.Add(0);
}
}
}
else
{
Name.PartsAround('`', out var name, out var genericarity);
names.Add(name);
if (genericarity != null)
{
genericarities.Add(int.Parse(genericarity));
}
else
{
genericarities.Add(0);
}
}
if (assemblyDescriptionStartIndex != null)
{
AssemblyDescription = s.Substring(assemblyDescriptionStartIndex.Value, assemblyDescriptionEndIndex.Value - assemblyDescriptionStartIndex.Value);
var parts = AssemblyDescription.Split(',')
.Select(x => x.Trim())
.ToList();
AssemblyVersion = LookForPairThenRemove(parts, "Version");
AssemblyCulture = LookForPairThenRemove(parts, "Culture");
AssemblyPublicKeyToken = LookForPairThenRemove(parts, "PublicKeyToken");
if (parts.Count > 0)
{
AssemblyName = parts[0];
}
}
}
catch (Exception ex)
{
throw new FormatException($"Failed to parse type name: {s}", ex);
}
}
private static string LookForPairThenRemove(List<string> strings, string Name)
{
for (var istr = 0; istr < strings.Count; istr++)
{
var s = strings[istr];
var i = s.IndexOf(Name);
if (i == 0)
{
var i2 = s.IndexOf('=');
if (i2 > 0)
{
var ret = s.Substring(i2 + 1);
strings.RemoveAt(istr);
return ret;
}
}
}
return null;
}
public void ReplaceNamespace(string oldNamespace, string newNamespace)
{
if (names[0].StartsWith(oldNamespace + "."))
{
names[0] = newNamespace + "." + names[0].TrimStart(oldNamespace + ".");
}
foreach (var genericParameter in GenericParameters)
{
genericParameter.ReplaceNamespace(oldNamespace, newNamespace);
}
UpdateName();
}
public void ReplaceAssembly(string oldAssembly, string newAssembly)
{
if (AssemblyName != null && AssemblyName.StartsWith(oldAssembly))
{
AssemblyName = newAssembly + AssemblyName.TrimStart(oldAssembly);
}
foreach (var genericParameter in GenericParameters)
{
genericParameter.ReplaceAssembly(oldAssembly, newAssembly);
}
}
public void ReplaceName(string oldTypeName, Type newType)
{
ReplaceName(oldTypeName, newType.FullName, newType.Assembly?.GetName());
}
public void ReplaceName(string oldTypeName, string newTypeName, AssemblyName newAssemblyName = null)
{
for (var i = 0; i < names.Count; i++)
{
if (ToElementTypeName(names[i]) == oldTypeName)
{
names[i] = ToArrayOrType(names[i], newTypeName);
if (newAssemblyName != null)
{
SetAssemblyName(newAssemblyName);
}
}
}
foreach (var genericParameter in GenericParameters)
{
genericParameter.ReplaceName(oldTypeName, newTypeName, newAssemblyName);
}
UpdateName();
}
// We never compare Arrays but just ElementTypes, so remove the square brackets from the old type
static string ToElementTypeName(string s)
{
return s.EndsWith("[]") ? s.Replace("[]", string.Empty) : s;
}
// If the old type was an array, then set the new type as an array
static string ToArrayOrType(string oldType, string newType)
{
if (oldType.EndsWith("[]"))
{
newType += "[]";
}
return newType;
}
public void SetAssemblyName(AssemblyName newAssemblyName)
{
AssemblyDescription = newAssemblyName.ToString();
AssemblyName = newAssemblyName.Name;
AssemblyCulture = newAssemblyName.CultureName;
AssemblyVersion = newAssemblyName.Version.ToString();
AssemblyPublicKeyToken = newAssemblyName.GetPublicKeyToken()?.ToHexString() ?? "null";
}
private void UpdateName()
{
var sb = new StringBuilder();
for (var i = 0; i < names.Count; i++)
{
if (i != 0)
{
sb.Append('+');
}
sb.Append(names[i]);
if (genericarities[i] > 0)
{
sb.Append('`');
sb.Append(genericarities[i]);
}
}
Name = sb.ToString();
}
public string ToString(TypeNameDetail specification, TypeNameDetail genericsSpecification)
{
var sb = new StringBuilder();
sb.Append(Name);
if (GenericParameters.Count > 0)
{
sb.Append("[");
var isFirstParameter = true;
foreach (var genericParameter in GenericParameters)
{
if (!isFirstParameter)
{
sb.Append(",");
}
if (genericsSpecification != TypeNameDetail.Name)
{
sb.Append("[");
}
sb.Append(genericParameter.ToString(genericsSpecification, genericsSpecification));
if (genericsSpecification != TypeNameDetail.Name)
{
sb.Append("]");
}
isFirstParameter = false;
}
sb.Append("]");
}
if (specification == TypeNameDetail.Full)
{
if (!string.IsNullOrEmpty(AssemblyDescription))
{
sb.Append(", ");
sb.Append(AssemblyDescription);
}
}
else if (specification == TypeNameDetail.NameAndAssembly)
{
if (!string.IsNullOrEmpty(AssemblyName))
{
sb.Append(", ");
sb.Append(AssemblyName);
}
}
return sb.ToString();
}
public override string ToString()
{
return ToString(TypeNameDetail.Name, TypeNameDetail.Full);
}
public string ToLooseString()
{
// Removes the Culture, Token and VersionNumber
return ToString(TypeNameDetail.NameAndAssembly, TypeNameDetail.NameAndAssembly);
}
public static string Simplify(string typeName)
{
return Parse(typeName).ToLooseString();
}
public static string SimplifyFast(string typeName)
{
// This assumes type strings are written with ', Version=' first, which
// is standard for Type.AssemblyQualifiedName but not technically spec guaranteed.
// It is however incredibly faster than parsing the type name and re-outputting it.
while (true)
{
var startIndex = typeName.IndexOf(", Version=", StringComparison.Ordinal);
if (startIndex >= 0)
{
var endIndex = typeName.IndexOf(']', startIndex);
if (endIndex >= 0)
{
typeName = typeName.Remove(startIndex, endIndex - startIndex);
}
else
{
typeName = typeName.Substring(0, startIndex);
break;
}
}
else
{
break;
}
}
return typeName;
}
}
}

View File

@@ -0,0 +1,11 @@
namespace Unity.VisualScripting
{
public enum TypeNameDetail
{
Name,
NameAndAssembly,
Full
}
}

View File

@@ -0,0 +1,9 @@
namespace Unity.VisualScripting
{
public enum TypeQualifier
{
Name,
Namespace,
GlobalNamespace
}
}

View File

@@ -0,0 +1,824 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace Unity.VisualScripting
{
public static class TypeUtility
{
private static readonly HashSet<Type> _numericTypes = new HashSet<Type>
{
typeof(byte),
typeof(sbyte),
typeof(short),
typeof(ushort),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(float),
typeof(double),
typeof(decimal)
};
private static readonly HashSet<Type> _numericConstructTypes = new HashSet<Type>
{
typeof(Vector2),
typeof(Vector3),
typeof(Vector4),
typeof(Quaternion),
typeof(Matrix4x4),
typeof(Rect),
};
private static readonly HashSet<Type> typesWithShortStrings = new HashSet<Type>()
{
typeof(string),
typeof(Vector2),
typeof(Vector3),
typeof(Vector4)
};
public static bool IsBasic(this Type type)
{
Ensure.That(nameof(type)).IsNotNull(type);
if (type == typeof(string) || type == typeof(decimal))
{
return true;
}
if (type.IsEnum)
{
return true;
}
if (type.IsPrimitive)
{
if (type == typeof(IntPtr) || type == typeof(UIntPtr))
{
return false;
}
return true;
}
return false;
}
public static bool IsNumeric(this Type type)
{
Ensure.That(nameof(type)).IsNotNull(type);
return _numericTypes.Contains(type);
}
public static bool IsNumericConstruct(this Type type)
{
Ensure.That(nameof(type)).IsNotNull(type);
return _numericConstructTypes.Contains(type);
}
public static Namespace Namespace(this Type type)
{
return Unity.VisualScripting.Namespace.FromFullName(type.Namespace);
}
public static Func<object> Instantiator(this Type type, bool nonPublic = true)
{
var instantiator = type.Instantiator(nonPublic, Empty<Type>.array);
if (instantiator != null)
{
return () => instantiator.Invoke(Empty<object>.array);
}
return null;
}
public static Func<object[], object> Instantiator(this Type type, bool nonPublic = true, params Type[] parameterTypes)
{
// Unity objects cannot be instantiated via constructor
if (typeof(UnityObject).IsAssignableFrom(type))
{
return null;
}
// Value types don't have parameterless constructors at the IL level
// http://stackoverflow.com/questions/3751519/
if ((type.IsValueType || type.IsBasic()) && parameterTypes.Length == 0)
{
return (args) => type.PseudoDefault();
}
// Look for matching constructor
var constructor = type.GetConstructorAccepting(parameterTypes, nonPublic);
if (constructor != null)
{
return (args) => constructor.Invoke(args);
}
// Can't instantiate from given access and parameter types
return null;
}
public static object TryInstantiate(this Type type, bool nonPublic = true, params object[] args)
{
Ensure.That(nameof(type)).IsNotNull(type);
var instantiator = type.Instantiator(nonPublic, args.Select(arg => arg.GetType()).ToArray());
return instantiator?.Invoke(args);
}
public static object Instantiate(this Type type, bool nonPublic = true, params object[] args)
{
Ensure.That(nameof(type)).IsNotNull(type);
var parameterTypes = args.Select(arg => arg.GetType()).ToArray();
var instantiator = type.Instantiator(nonPublic, parameterTypes);
if (instantiator == null)
{
throw new ArgumentException($"Type {type} cannot be{(nonPublic ? "" : " publicly")} instantiated with the provided parameter types: {parameterTypes.ToCommaSeparatedString()}");
}
return instantiator(args);
}
public static object Default(this Type type)
{
Ensure.That(nameof(type)).IsNotNull(type);
if (type.IsReferenceType())
{
return null;
}
if (!defaultPrimitives.TryGetValue(type, out var defaultPrimitive))
{
defaultPrimitive = Activator.CreateInstance(type);
}
return defaultPrimitive;
}
public static object PseudoDefault(this Type type)
{
if (type == typeof(Color))
{
return Color.white;
}
else if (type == typeof(string))
{
return string.Empty;
}
else if (type.IsEnum)
{
// Support the [DefaultValue] attribute, fallback to zero-value
// https://stackoverflow.com/questions/529929
var values = Enum.GetValues(type);
if (values.Length == 0)
{
Debug.LogWarning($"Empty enum: {type}\nThis may cause problems with serialization.");
return Activator.CreateInstance(type);
}
var attribute = type.GetAttribute<DefaultValueAttribute>();
if (attribute != null)
{
return attribute.Value;
}
return values.GetValue(0);
}
return type.Default();
}
private static readonly Dictionary<Type, object> defaultPrimitives = new Dictionary<Type, object>()
{
{ typeof(int), default(int) },
{ typeof(uint), default(uint) },
{ typeof(long), default(long) },
{ typeof(ulong), default(ulong) },
{ typeof(short), default(short) },
{ typeof(ushort), default(ushort) },
{ typeof(byte), default(byte) },
{ typeof(sbyte), default(sbyte) },
{ typeof(float), default(float) },
{ typeof(double), default(double) },
{ typeof(decimal), default(decimal) },
{ typeof(Vector2), default(Vector2) },
{ typeof(Vector3), default(Vector3) },
{ typeof(Vector4), default(Vector4) },
};
public static bool IsStatic(this Type type)
{
return type.IsAbstract && type.IsSealed;
}
public static bool IsAbstract(this Type type)
{
// Do not return true for static types
return type.IsAbstract && !type.IsSealed;
}
public static bool IsConcrete(this Type type)
{
return !type.IsAbstract && !type.IsInterface && !type.ContainsGenericParameters;
}
public static IEnumerable<Type> GetInterfaces(this Type type, bool includeInherited)
{
if (includeInherited || type.BaseType == null)
{
return type.GetInterfaces();
}
else
{
return type.GetInterfaces().Except(type.BaseType.GetInterfaces());
}
}
public static IEnumerable<Type> BaseTypeAndInterfaces(this Type type, bool inheritedInterfaces = true)
{
var types = Enumerable.Empty<Type>();
if (type.BaseType != null)
{
types = types.Concat(type.BaseType.Yield());
}
types = types.Concat(type.GetInterfaces(inheritedInterfaces));
return types;
}
public static IEnumerable<Type> Hierarchy(this Type type)
{
var baseType = type.BaseType;
while (baseType != null)
{
yield return baseType;
foreach (var @interface in baseType.GetInterfaces(false))
{
yield return @interface;
}
baseType = baseType.BaseType;
}
}
public static IEnumerable<Type> AndBaseTypeAndInterfaces(this Type type)
{
return type.Yield().Concat(type.BaseTypeAndInterfaces());
}
public static IEnumerable<Type> AndInterfaces(this Type type)
{
return type.Yield().Concat(type.GetInterfaces());
}
public static IEnumerable<Type> AndHierarchy(this Type type)
{
return type.Yield().Concat(type.Hierarchy());
}
public static Type GetListElementType(Type listType, bool allowNonGeneric)
{
if (listType == null)
{
throw new ArgumentNullException(nameof(listType));
}
// http://stackoverflow.com/questions/4452590
if (listType.IsArray)
{
return listType.GetElementType();
}
else if (typeof(IList).IsAssignableFrom(listType))
{
var genericListInterface =
listType
.AndInterfaces()
.FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>));
if (genericListInterface == null)
{
if (allowNonGeneric)
{
return typeof(object);
}
else
{
return null;
}
}
return genericListInterface.GetGenericArguments()[0];
}
else
{
return null;
}
}
public static Type GetEnumerableElementType(Type enumerableType, bool allowNonGeneric)
{
if (enumerableType == null)
{
throw new ArgumentNullException(nameof(enumerableType));
}
// http://stackoverflow.com/a/12728562
if (typeof(IEnumerable).IsAssignableFrom(enumerableType))
{
var genericEnumerableInterface =
enumerableType
.AndInterfaces()
.FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
if (genericEnumerableInterface == null)
{
if (allowNonGeneric)
{
return typeof(object);
}
else
{
return null;
}
}
return genericEnumerableInterface.GetGenericArguments()[0];
}
else
{
return null;
}
}
public static Type GetDictionaryItemType(Type dictionaryType, bool allowNonGeneric, int genericArgumentIndex)
{
if (dictionaryType == null)
{
throw new ArgumentNullException(nameof(dictionaryType));
}
if (typeof(IDictionary).IsAssignableFrom(dictionaryType))
{
var genericDictionaryInterface =
dictionaryType
.AndInterfaces()
.FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>));
if (genericDictionaryInterface == null)
{
if (allowNonGeneric)
{
return typeof(object);
}
else
{
return null;
}
}
return genericDictionaryInterface.GetGenericArguments()[genericArgumentIndex];
}
else
{
return null;
}
}
public static Type GetDictionaryKeyType(Type dictionaryType, bool allowNonGeneric)
{
return GetDictionaryItemType(dictionaryType, allowNonGeneric, 0);
}
public static Type GetDictionaryValueType(Type dictionaryType, bool allowNonGeneric)
{
return GetDictionaryItemType(dictionaryType, allowNonGeneric, 1);
}
public static bool IsNullable(this Type type)
{
// http://stackoverflow.com/a/1770232
return type.IsReferenceType() || Nullable.GetUnderlyingType(type) != null;
}
public static bool IsReferenceType(this Type type)
{
return !type.IsValueType;
}
public static bool IsStruct(this Type type)
{
return type.IsValueType && !type.IsPrimitive && !type.IsEnum;
}
public static bool IsAssignableFrom(this Type type, object value)
{
if (value == null)
{
return type.IsNullable();
}
else
{
return type.IsInstanceOfType(value);
}
}
public static bool CanMakeGenericTypeVia(this Type openConstructedType, Type closedConstructedType)
{
Ensure.That(nameof(openConstructedType)).IsNotNull(openConstructedType);
Ensure.That(nameof(closedConstructedType)).IsNotNull(closedConstructedType);
if (openConstructedType == closedConstructedType)
{
return true;
}
if (openConstructedType.IsGenericParameter) // e.g.: T
{
// The open-constructed type is a generic parameter.
// First, check if all special attribute constraints are respected.
var constraintAttributes = openConstructedType.GenericParameterAttributes;
if (constraintAttributes != GenericParameterAttributes.None)
{
// e.g.: where T : struct
if (constraintAttributes.HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint) &&
!closedConstructedType.IsValueType)
{
return false;
}
// e.g.: where T : class
if (constraintAttributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint) &&
closedConstructedType.IsValueType)
{
return false;
}
// e.g.: where T : new()
if (constraintAttributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint) &&
closedConstructedType.GetConstructor(Type.EmptyTypes) == null)
{
return false;
}
}
// Then, check if all type constraints are respected.
// e.g.: where T : BaseType, IInterface1, IInterface2
foreach (var constraint in openConstructedType.GetGenericParameterConstraints())
{
if (!constraint.IsAssignableFrom(closedConstructedType))
{
return false;
}
}
return true;
}
else if (openConstructedType.ContainsGenericParameters)
{
// The open-constructed type is not a generic parameter but contains generic parameters.
// It could be either a generic type or an array.
if (openConstructedType.IsGenericType) // e.g. Generic<T1, int, T2>
{
// The open-constructed type is a generic type.
var openConstructedGenericDefinition = openConstructedType.GetGenericTypeDefinition(); // e.g.: Generic<,,>
var openConstructedGenericArguments = openConstructedType.GetGenericArguments(); // e.g.: { T1, int, T2 }
// Check a list of possible candidate closed-constructed types:
// - the closed-constructed type itself
// - its base type, if any (i.e.: if the closed-constructed type is not object)
// - its implemented interfaces
foreach (var inheritedClosedConstructedType in closedConstructedType.AndBaseTypeAndInterfaces())
{
if (inheritedClosedConstructedType.IsGenericType &&
inheritedClosedConstructedType.GetGenericTypeDefinition() == openConstructedGenericDefinition)
{
// The inherited closed-constructed type and the open-constructed type share the same generic definition.
var inheritedClosedConstructedGenericArguments = inheritedClosedConstructedType.GetGenericArguments(); // e.g.: { float, int, string }
// For each open-constructed generic argument, recursively check if it
// can be made into a closed-constructed type via the closed-constructed generic argument.
for (var i = 0; i < openConstructedGenericArguments.Length; i++)
{
if (!openConstructedGenericArguments[i].CanMakeGenericTypeVia(inheritedClosedConstructedGenericArguments[i])) // !T1.IsAssignableFromGeneric(float)
{
return false;
}
}
// The inherited closed-constructed type matches the generic definition of
// the open-constructed type and each of its type arguments are assignable to each equivalent type
// argument of the constraint.
return true;
}
}
// The open-constructed type contains generic parameters, but no
// inherited closed-constructed type has a matching generic definition.
return false;
}
else if (openConstructedType.IsArray) // e.g. T[]
{
// The open-constructed type is an array.
if (!closedConstructedType.IsArray ||
closedConstructedType.GetArrayRank() != openConstructedType.GetArrayRank())
{
// Fail if the closed-constructed type isn't an array of the same rank.
return false;
}
var openConstructedElementType = openConstructedType.GetElementType();
var closedConstructedElementType = closedConstructedType.GetElementType();
return openConstructedElementType.CanMakeGenericTypeVia(closedConstructedElementType);
}
else if (openConstructedType.IsByRef) // e.g. T&
{
// The open-constructed type is by ref.
if (!closedConstructedType.IsByRef)
{
// Fail if the closed-constructed type isn't also by ref.
return false;
}
var openConstructedElementType = openConstructedType.GetElementType();
var closedConstructedElementType = closedConstructedType.GetElementType();
return openConstructedElementType.CanMakeGenericTypeVia(closedConstructedElementType);
}
else
{
throw new NotImplementedException();
}
}
else
{
// The open-constructed type does not contain generic parameters,
// we can proceed to a regular closed-type check.
return openConstructedType.IsAssignableFrom(closedConstructedType);
}
}
public static Type MakeGenericTypeVia(this Type openConstructedType, Type closedConstructedType, Dictionary<Type, Type> resolvedGenericParameters, bool safe = true)
{
Ensure.That(nameof(openConstructedType)).IsNotNull(openConstructedType);
Ensure.That(nameof(closedConstructedType)).IsNotNull(closedConstructedType);
Ensure.That(nameof(resolvedGenericParameters)).IsNotNull(resolvedGenericParameters);
if (safe && !openConstructedType.CanMakeGenericTypeVia(closedConstructedType))
{
throw new GenericClosingException(openConstructedType, closedConstructedType);
}
if (openConstructedType == closedConstructedType)
{
return openConstructedType;
}
if (openConstructedType.IsGenericParameter) // e.g.: T
{
// The open-constructed type is a generic parameter.
// We can directly map it to the closed-constructed type.
// Because this is the lowest possible level of type resolution,
// we will add this entry to our list of resolved generic parameters
// in case we need it later (e.g. for resolving generic methods).
// Note that we allow an open-constructed type to "make" another
// open-constructed type, as long as the former respects all of
// the latter's constraints. Therefore, we will only add the resolved
// parameter to our dictionary if it actually is resolved.
if (!closedConstructedType.ContainsGenericParameters)
{
if (resolvedGenericParameters.ContainsKey(openConstructedType))
{
if (resolvedGenericParameters[openConstructedType] != closedConstructedType)
{
throw new InvalidOperationException("Nested generic parameters resolve to different values.");
}
}
else
{
resolvedGenericParameters.Add(openConstructedType, closedConstructedType);
}
}
return closedConstructedType;
}
else if (openConstructedType.ContainsGenericParameters) // e.g.: Generic<T1, int, T2>
{
// The open-constructed type is not a generic parameter but contains generic parameters.
// It could be either a generic type or an array.
if (openConstructedType.IsGenericType) // e.g. Generic<T1, int, T2>
{
// The open-constructed type is a generic type.
var openConstructedGenericDefinition = openConstructedType.GetGenericTypeDefinition(); // e.g.: Generic<,,>
var openConstructedGenericArguments = openConstructedType.GetGenericArguments(); // e.g.: { T1, int, T2 }
// Check a list of possible candidate closed-constructed types:
// - the closed-constructed type itself
// - its base type, if any (i.e.: if the closed-constructed type is not object)
// - its implemented interfaces
foreach (var inheritedCloseConstructedType in closedConstructedType.AndBaseTypeAndInterfaces())
{
if (inheritedCloseConstructedType.IsGenericType &&
inheritedCloseConstructedType.GetGenericTypeDefinition() == openConstructedGenericDefinition)
{
// The inherited closed-constructed type and the open-constructed type share the same generic definition.
var inheritedClosedConstructedGenericArguments = inheritedCloseConstructedType.GetGenericArguments(); // e.g.: { float, int, string }
// For each inherited open-constructed type generic argument, recursively resolve it
// via the equivalent closed-constructed type generic argument.
var closedConstructedGenericArguments = new Type[openConstructedGenericArguments.Length];
for (var j = 0; j < openConstructedGenericArguments.Length; j++)
{
closedConstructedGenericArguments[j] = MakeGenericTypeVia
(
openConstructedGenericArguments[j],
inheritedClosedConstructedGenericArguments[j],
resolvedGenericParameters,
safe: false // We recursively checked before, no need to do it again
);
// e.g.: Resolve(T1, float)
}
// Construct the final closed-constructed type from the resolved arguments
return openConstructedGenericDefinition.MakeGenericType(closedConstructedGenericArguments);
}
}
// The open-constructed type contains generic parameters, but no
// inherited closed-constructed type has a matching generic definition.
// This cannot happen in safe mode, but could in unsafe mode.
throw new GenericClosingException(openConstructedType, closedConstructedType);
}
else if (openConstructedType.IsArray) // e.g. T[]
{
var arrayRank = openConstructedType.GetArrayRank();
// The open-constructed type is an array.
if (!closedConstructedType.IsArray ||
closedConstructedType.GetArrayRank() != arrayRank)
{
// Fail if the closed-constructed type isn't an array of the same rank.
// This cannot happen in safe mode, but could in unsafe mode.
throw new GenericClosingException(openConstructedType, closedConstructedType);
}
var openConstructedElementType = openConstructedType.GetElementType();
var closedConstructedElementType = closedConstructedType.GetElementType();
return openConstructedElementType.MakeGenericTypeVia
(
closedConstructedElementType,
resolvedGenericParameters,
safe: false
).MakeArrayType(arrayRank);
}
else if (openConstructedType.IsByRef) // e.g. T&
{
// The open-constructed type is by ref.
if (!closedConstructedType.IsByRef)
{
// Fail if the closed-constructed type isn't also by ref.
// This cannot happen in safe mode, but could in unsafe mode.
throw new GenericClosingException(openConstructedType, closedConstructedType);
}
var openConstructedElementType = openConstructedType.GetElementType();
var closedConstructedElementType = closedConstructedType.GetElementType();
return openConstructedElementType.MakeGenericTypeVia
(
closedConstructedElementType,
resolvedGenericParameters,
safe: false
).MakeByRefType();
}
else
{
throw new NotImplementedException();
}
}
else
{
// The open-constructed type does not contain generic parameters,
// it is by definition already resolved.
return openConstructedType;
}
}
public static string ToShortString(this object o, int maxLength = 20)
{
var type = o?.GetType();
if (type == null || o.IsUnityNull())
{
return "Null";
}
else if (type == typeof(float))
{
return ((float)o).ToString("0.##");
}
else if (type == typeof(double))
{
return ((double)o).ToString("0.##");
}
else if (type == typeof(decimal))
{
return ((decimal)o).ToString("0.##");
}
else if (type.IsBasic() || typesWithShortStrings.Contains(type))
{
return o.ToString().Truncate(maxLength);
}
else if (typeof(UnityObject).IsAssignableFrom(type))
{
return ((UnityObject)o).name.Truncate(maxLength);
}
else
{
return null;
}
}
public static IEnumerable<Type> GetTypesSafely(this Assembly assembly)
{
Type[] types;
try
{
types = assembly.GetTypes();
}
catch (Exception ex)
{
Debug.LogWarning($"Failed to load types in assembly '{assembly}'.\n{ex}");
yield break;
}
foreach (var type in types)
{
// Apparently void can be returned somehow:
// http://support.ludiq.io/topics/483
if (type == typeof(void))
{
continue;
}
yield return type;
}
}
}
}

View File

@@ -0,0 +1,9 @@
namespace Unity.VisualScripting
{
public enum TypesMatching
{
ConvertibleToAny,
AssignableToAll,
Any
}
}