diff options
| author | tylermurphy534 <tylermurphy534@gmail.com> | 2022-11-06 15:12:42 -0500 |
|---|---|---|
| committer | tylermurphy534 <tylermurphy534@gmail.com> | 2022-11-06 15:12:42 -0500 |
| commit | eb84bb298d2b95aec7b2ae12cbf25ac64f25379a (patch) | |
| tree | efd616a157df06ab661c6d56651853431ac6b08b /VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions | |
| download | unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.tar.gz unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.tar.bz2 unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.zip | |
move to self host
Diffstat (limited to 'VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions')
22 files changed, 3793 insertions, 0 deletions
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/FieldInfoExtensions.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/FieldInfoExtensions.cs new file mode 100644 index 00000000..1787771f --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/FieldInfoExtensions.cs @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------- +// <copyright file="FieldInfoExtensions.cs" company="Sirenix IVS"> +// Copyright (c) 2018 Sirenix IVS +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// </copyright> +//----------------------------------------------------------------------- + +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.Reflection; + + /// <summary> + /// FieldInfo method extensions. + /// </summary> + public static class FieldInfoExtensions + { + /// <summary> + /// Determines whether the specified field is an alias. + /// </summary> + /// <param name="fieldInfo">The field to check.</param> + /// <returns> + /// <c>true</c> if the specified field is an alias; otherwise, <c>false</c>. + /// </returns> + public static bool IsAliasField(this FieldInfo fieldInfo) + { + return fieldInfo is MemberAliasFieldInfo; + } + + /// <summary> + /// Returns the original, backing field of an alias field if the field is an alias. + /// </summary> + /// <param name="fieldInfo">The field to check.</param> + /// /// <param name="throwOnNotAliased">if set to <c>true</c> an exception will be thrown if the field is not aliased.</param> + /// <returns></returns> + /// <exception cref="System.ArgumentException">The field was not aliased; this only occurs if throwOnNotAliased is true.</exception> + public static FieldInfo DeAliasField(this FieldInfo fieldInfo, bool throwOnNotAliased = false) + { + MemberAliasFieldInfo aliasFieldInfo = fieldInfo as MemberAliasFieldInfo; + + if (aliasFieldInfo != null) + { + while (aliasFieldInfo.AliasedField is MemberAliasFieldInfo) + { + aliasFieldInfo = aliasFieldInfo.AliasedField as MemberAliasFieldInfo; + } + + return aliasFieldInfo.AliasedField; + } + + if (throwOnNotAliased) + { + throw new ArgumentException("The field " + fieldInfo.GetNiceName() + " was not aliased."); + } + + return fieldInfo; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/FieldInfoExtensions.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/FieldInfoExtensions.cs.meta new file mode 100644 index 00000000..f3556584 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/FieldInfoExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 78ce67c0b3c1975c520a08d1ff9fd24e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/GarbageFreeIterators.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/GarbageFreeIterators.cs new file mode 100644 index 00000000..9c3653cb --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/GarbageFreeIterators.cs @@ -0,0 +1,330 @@ +//----------------------------------------------------------------------- +// <copyright file="GarbageFreeIterators.cs" company="Sirenix IVS"> +// Copyright (c) 2018 Sirenix IVS +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// </copyright> +//----------------------------------------------------------------------- +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.Collections.Generic; + + /// <summary> + /// Garbage free enumerator methods. + /// </summary> + public static class GarbageFreeIterators + { + /// <summary> + /// Garbage free enumerator for lists. + /// </summary> + public static ListIterator<T> GFIterator<T>(this List<T> list) + { + return new ListIterator<T>(list); + } + + /// <summary> + /// Garbage free enumerator for dictionaries. + /// </summary> + public static DictionaryIterator<T1, T2> GFIterator<T1, T2>(this Dictionary<T1, T2> dictionary) + { + return new DictionaryIterator<T1, T2>(dictionary); + } + + /// <summary> + /// Garbage free enumator for dictionary values. + /// </summary> + public static DictionaryValueIterator<T1, T2> GFValueIterator<T1, T2>(this Dictionary<T1, T2> dictionary) + { + return new DictionaryValueIterator<T1, T2>(dictionary); + } + + /// <summary> + /// Garbage free enumerator for hashsets. + /// </summary> + public static HashsetIterator<T> GFIterator<T>(this HashSet<T> hashset) + { + return new HashsetIterator<T>(hashset); + } + + /// <summary> + /// List iterator. + /// </summary> + public struct ListIterator<T> : IDisposable + { + private bool isNull; + private List<T> list; + private List<T>.Enumerator enumerator; + + /// <summary> + /// Creates a list iterator. + /// </summary> + public ListIterator(List<T> list) + { + this.isNull = list == null; + if (this.isNull) + { + this.list = null; + this.enumerator = new List<T>.Enumerator(); + } + else + { + this.list = list; + this.enumerator = this.list.GetEnumerator(); + } + } + + /// <summary> + /// Gets the enumerator. + /// </summary> + public ListIterator<T> GetEnumerator() + { + return this; + } + + /// <summary> + /// Gets the current value. + /// </summary> + public T Current + { + get + { + return this.enumerator.Current; + } + } + + /// <summary> + /// Moves to the next value. + /// </summary> + public bool MoveNext() + { + if (this.isNull) + { + return false; + } + return this.enumerator.MoveNext(); + } + + /// <summary> + /// Disposes the iterator. + /// </summary> + public void Dispose() + { + this.enumerator.Dispose(); + } + } + + /// <summary> + /// Hashset iterator. + /// </summary> + public struct HashsetIterator<T> : IDisposable + { + private bool isNull; + private HashSet<T> hashset; + private HashSet<T>.Enumerator enumerator; + + /// <summary> + /// Creates a hashset iterator. + /// </summary> + public HashsetIterator(HashSet<T> hashset) + { + this.isNull = hashset == null; + if (this.isNull) + { + this.hashset = null; + this.enumerator = new HashSet<T>.Enumerator(); + } + else + { + this.hashset = hashset; + this.enumerator = this.hashset.GetEnumerator(); + } + } + + /// <summary> + /// Gets the enumerator. + /// </summary> + public HashsetIterator<T> GetEnumerator() + { + return this; + } + + /// <summary> + /// Gets the current value. + /// </summary> + public T Current + { + get + { + return this.enumerator.Current; + } + } + + /// <summary> + /// Moves to the next value. + /// </summary> + public bool MoveNext() + { + if (this.isNull) + { + return false; + } + return this.enumerator.MoveNext(); + } + + /// <summary> + /// Disposes the iterator. + /// </summary> + public void Dispose() + { + this.enumerator.Dispose(); + } + } + + /// <summary> + /// Dictionary iterator. + /// </summary> + public struct DictionaryIterator<T1, T2> : IDisposable + { + private Dictionary<T1, T2> dictionary; + private Dictionary<T1, T2>.Enumerator enumerator; + private bool isNull; + + /// <summary> + /// Creates a dictionary iterator. + /// </summary> + public DictionaryIterator(Dictionary<T1, T2> dictionary) + { + this.isNull = dictionary == null; + + if (this.isNull) + { + this.dictionary = null; + this.enumerator = new Dictionary<T1, T2>.Enumerator(); + } + else + { + this.dictionary = dictionary; + this.enumerator = this.dictionary.GetEnumerator(); + } + } + + /// <summary> + /// Gets the enumerator. + /// </summary> + public DictionaryIterator<T1, T2> GetEnumerator() + { + return this; + } + + /// <summary> + /// Gets the current value. + /// </summary> + public KeyValuePair<T1, T2> Current + { + get + { + return this.enumerator.Current; + } + } + + /// <summary> + /// Moves to the next value. + /// </summary> + public bool MoveNext() + { + if (this.isNull) + { + return false; + } + return this.enumerator.MoveNext(); + } + + /// <summary> + /// Disposes the iterator. + /// </summary> + public void Dispose() + { + this.enumerator.Dispose(); + } + } + + /// <summary> + /// Dictionary value iterator. + /// </summary> + public struct DictionaryValueIterator<T1, T2> : IDisposable + { + private Dictionary<T1, T2> dictionary; + private Dictionary<T1, T2>.Enumerator enumerator; + private bool isNull; + + /// <summary> + /// Creates a dictionary value iterator. + /// </summary> + public DictionaryValueIterator(Dictionary<T1, T2> dictionary) + { + this.isNull = dictionary == null; + + if (this.isNull) + { + this.dictionary = null; + this.enumerator = new Dictionary<T1, T2>.Enumerator(); + } + else + { + this.dictionary = dictionary; + this.enumerator = this.dictionary.GetEnumerator(); + } + } + + /// <summary> + /// Gets the enumerator. + /// </summary> + public DictionaryValueIterator<T1, T2> GetEnumerator() + { + return this; + } + + /// <summary> + /// Gets the current value. + /// </summary> + public T2 Current + { + get + { + return this.enumerator.Current.Value; + } + } + + /// <summary> + /// Moves to the next value. + /// </summary> + public bool MoveNext() + { + if (this.isNull) + { + return false; + } + return this.enumerator.MoveNext(); + } + + /// <summary> + /// Disposes the iterator. + /// </summary> + public void Dispose() + { + this.enumerator.Dispose(); + } + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/GarbageFreeIterators.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/GarbageFreeIterators.cs.meta new file mode 100644 index 00000000..d47adba1 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/GarbageFreeIterators.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 068f5645a5c3f9ce36a580ec57e775d1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/LinqExtensions.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/LinqExtensions.cs new file mode 100644 index 00000000..29c8d1a5 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/LinqExtensions.cs @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------- +// <copyright file="LinqExtensions.cs" company="Sirenix IVS"> +// Copyright (c) 2018 Sirenix IVS +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// </copyright> +//----------------------------------------------------------------------- + +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.Collections.Generic; + + /// <summary> + /// Various LinQ extensions. + /// </summary> + public static class LinqExtensions + { + /// <summary> + /// Perform an action on each item. + /// </summary> + /// <param name="source">The source.</param> + /// <param name="action">The action to perform.</param> + public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Action<T> action) + { + foreach (var item in source) + { + action(item); + } + + return source; + } + + /// <summary> + /// Perform an action on each item. + /// </summary> + /// <param name="source">The source.</param> + /// <param name="action">The action to perform.</param> + public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Action<T, int> action) + { + int counter = 0; + + foreach (var item in source) + { + action(item, counter++); + } + + return source; + } + + /// <summary> + /// Add a collection to the end of another collection. + /// </summary> + /// <param name="source">The collection.</param> + /// <param name="append">The collection to append.</param> + public static IEnumerable<T> Append<T>(this IEnumerable<T> source, IEnumerable<T> append) + { + foreach (var item in source) + { + yield return item; + } + + foreach (var item in append) + { + yield return item; + } + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/LinqExtensions.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/LinqExtensions.cs.meta new file mode 100644 index 00000000..1afa9a89 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/LinqExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0f84614827ff91701149564447a3932b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/MemberInfoExtensions.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/MemberInfoExtensions.cs new file mode 100644 index 00000000..eea7bef9 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/MemberInfoExtensions.cs @@ -0,0 +1,254 @@ +//----------------------------------------------------------------------- +// <copyright file="MemberInfoExtensions.cs" company="Sirenix IVS"> +// Copyright (c) 2018 Sirenix IVS +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// </copyright> +//----------------------------------------------------------------------- +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using System.Globalization; + + /// <summary> + /// MemberInfo method extensions. + /// </summary> + public static class MemberInfoExtensions + { + /// <summary> + /// Returns true if the attribute whose type is specified by the generic argument is defined on this member + /// </summary> + public static bool IsDefined<T>(this ICustomAttributeProvider member, bool inherit) where T : Attribute + { + try + { + return member.IsDefined(typeof(T), inherit); + } + catch + { + return false; + } + } + + /// <summary> + /// Returns true if the attribute whose type is specified by the generic argument is defined on this member + /// </summary> + public static bool IsDefined<T>(this ICustomAttributeProvider member) where T : Attribute + { + return IsDefined<T>(member, false); + } + + /// <summary> + /// Returns the first found custom attribute of type T on this member + /// Returns null if none was found + /// </summary> + public static T GetAttribute<T>(this ICustomAttributeProvider member, bool inherit) where T : Attribute + { + var all = GetAttributes<T>(member, inherit).ToArray(); + return (all == null || all.Length == 0) ? null : all[0]; + } + + /// <summary> + /// Returns the first found non-inherited custom attribute of type T on this member + /// Returns null if none was found + /// </summary> + public static T GetAttribute<T>(this ICustomAttributeProvider member) where T : Attribute + { + return GetAttribute<T>(member, false); + } + + /// <summary> + /// Gets all attributes of the specified generic type. + /// </summary> + /// <param name="member">The member.</param> + public static IEnumerable<T> GetAttributes<T>(this ICustomAttributeProvider member) where T : Attribute + { + return GetAttributes<T>(member, false); + } + + /// <summary> + /// Gets all attributes of the specified generic type. + /// </summary> + /// <param name="member">The member.</param> + /// <param name="inherit">If true, specifies to also search the ancestors of element for custom attributes.</param> + public static IEnumerable<T> GetAttributes<T>(this ICustomAttributeProvider member, bool inherit) where T : Attribute + { + try + { + return member.GetCustomAttributes(typeof(T), inherit).Cast<T>(); + } + catch + { + return new T[0]; + } + } + + /// <summary> + /// Gets all attribute instances defined on a MemeberInfo. + /// </summary> + /// <param name="member">The member.</param> + public static Attribute[] GetAttributes(this ICustomAttributeProvider member) + { + try + { + return member.GetAttributes<Attribute>().ToArray(); + } + catch + { + return new Attribute[0]; + } + } + + /// <summary> + /// Gets all attribute instances on a MemberInfo. + /// </summary> + /// <param name="member">The member.</param> + /// <param name="inherit">If true, specifies to also search the ancestors of element for custom attributes.</param> + public static Attribute[] GetAttributes(this ICustomAttributeProvider member, bool inherit) + { + try + { + return member.GetAttributes<Attribute>(inherit).ToArray(); + } + catch + { + return new Attribute[0]; + } + } + + /// <summary> + /// If this member is a method, returns the full method name (name + params) otherwise the member name paskal splitted + /// </summary> + public static string GetNiceName(this MemberInfo member) + { + var method = member as MethodBase; + string result; + if (method != null) + { + result = method.GetFullName(); + } + else + { + result = member.Name; + } + + return result.ToTitleCase(); + } + + /// <summary> + /// Determines whether a FieldInfo, PropertyInfo or MethodInfo is static. + /// </summary> + /// <param name="member">The member.</param> + /// <returns> + /// <c>true</c> if the specified member is static; otherwise, <c>false</c>. + /// </returns> + /// <exception cref="System.NotSupportedException"></exception> + public static bool IsStatic(this MemberInfo member) + { + var field = member as FieldInfo; + if (field != null) + { + return field.IsStatic; + } + + var property = member as PropertyInfo; + if (property != null) + { + return property.CanRead ? property.GetGetMethod(true).IsStatic : property.GetSetMethod(true).IsStatic; + } + + var method = member as MethodBase; + if (method != null) + { + return method.IsStatic; + } + + var @event = member as EventInfo; + if (@event != null) + { + return @event.GetRaiseMethod(true).IsStatic; + } + + var type = member as Type; + if (type != null) + { + return type.IsSealed && type.IsAbstract; + } + + string message = string.Format( + CultureInfo.InvariantCulture, + "Unable to determine IsStatic for member {0}.{1}" + + "MemberType was {2} but only fields, properties and methods are supported.", + member.DeclaringType.FullName, + member.Name, + member.GetType().FullName); + + throw new NotSupportedException(message); + } + + /// <summary> + /// Determines whether the specified member is an alias. + /// </summary> + /// <param name="memberInfo">The member to check.</param> + /// <returns> + /// <c>true</c> if the specified member is an alias; otherwise, <c>false</c>. + /// </returns> + public static bool IsAlias(this MemberInfo memberInfo) + { + return memberInfo is MemberAliasFieldInfo + || memberInfo is MemberAliasPropertyInfo + || memberInfo is MemberAliasMethodInfo; + } + + /// <summary> + /// Returns the original, backing member of an alias member if the member is an alias. + /// </summary> + /// <param name="memberInfo">The member to check.</param> + /// /// <param name="throwOnNotAliased">if set to <c>true</c> an exception will be thrown if the member is not aliased.</param> + /// <returns></returns> + /// <exception cref="System.ArgumentException">The member was not aliased; this only occurs if throwOnNotAliased is true.</exception> + public static MemberInfo DeAlias(this MemberInfo memberInfo, bool throwOnNotAliased = false) + { + MemberAliasFieldInfo aliasFieldInfo = memberInfo as MemberAliasFieldInfo; + + if (aliasFieldInfo != null) + { + return aliasFieldInfo.AliasedField; + } + + MemberAliasPropertyInfo aliasPropertyInfo = memberInfo as MemberAliasPropertyInfo; + + if (aliasPropertyInfo != null) + { + return aliasPropertyInfo.AliasedProperty; + } + + MemberAliasMethodInfo aliasMethodInfo = memberInfo as MemberAliasMethodInfo; + + if (aliasMethodInfo != null) + { + return aliasMethodInfo.AliasedMethod; + } + + if (throwOnNotAliased) + { + throw new ArgumentException("The member " + memberInfo.GetNiceName() + " was not aliased."); + } + + return memberInfo; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/MemberInfoExtensions.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/MemberInfoExtensions.cs.meta new file mode 100644 index 00000000..683ed7a3 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/MemberInfoExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 62088a9c188c943eb4035de16eb6ec32 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/MethodInfoExtensions.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/MethodInfoExtensions.cs new file mode 100644 index 00000000..91eba202 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/MethodInfoExtensions.cs @@ -0,0 +1,139 @@ +//----------------------------------------------------------------------- +// <copyright file="MethodInfoExtensions.cs" company="Sirenix IVS"> +// Copyright (c) 2018 Sirenix IVS +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// </copyright> +//----------------------------------------------------------------------- + +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.Linq; + using System.Reflection; + using System.Runtime.CompilerServices; + using System.Text; + + /// <summary> + /// Various extensions for MethodInfo. + /// </summary> + public static class MethodInfoExtensions + { + /// <summary> + /// Returns the specified method's full name "methodName(argType1 arg1, argType2 arg2, etc)" + /// Uses the specified gauntlet to replaces type names, ex: "int" instead of "Int32" + /// </summary> + public static string GetFullName(this MethodBase method, string extensionMethodPrefix) + { + var builder = new StringBuilder(); + bool isExtensionMethod = method.IsExtensionMethod(); + + if (isExtensionMethod) + { + builder.Append(extensionMethodPrefix); + } + + builder.Append(method.Name); + builder.Append("("); + builder.Append(method.GetParamsNames()); + builder.Append(")"); + return builder.ToString(); + } + + /// <summary> + /// Returns a string representing the passed method parameters names. Ex "int num, float damage, Transform target" + /// </summary> + public static string GetParamsNames(this MethodBase method) + { + ParameterInfo[] pinfos = method.IsExtensionMethod() ? method.GetParameters().Skip(1).ToArray() : method.GetParameters(); + var builder = new StringBuilder(); + + for (int i = 0, len = pinfos.Length; i < len; i++) + { + var param = pinfos[i]; + var paramTypeName = param.ParameterType.GetNiceName(); + builder.Append(paramTypeName); + builder.Append(" "); + builder.Append(param.Name); + + if (i < len - 1) + { + builder.Append(", "); + } + } + + return builder.ToString(); + } + + /// <summary> + /// Returns the specified method's full name. + /// </summary> + public static string GetFullName(this MethodBase method) + { + return GetFullName(method, "[ext] "); + } + + /// <summary> + /// Tests if a method is an extension method. + /// </summary> + public static bool IsExtensionMethod(this MethodBase method) + { + var type = method.DeclaringType; + return type.IsSealed && + !type.IsGenericType && + !type.IsNested && + method.IsDefined(typeof(ExtensionAttribute), false); + } + + /// <summary> + /// Determines whether the specified method is an alias. + /// </summary> + /// <param name="methodInfo">The method to check.</param> + /// <returns> + /// <c>true</c> if the specified method is an alias; otherwise, <c>false</c>. + /// </returns> + public static bool IsAliasMethod(this MethodInfo methodInfo) + { + return methodInfo is MemberAliasMethodInfo; + } + + /// <summary> + /// Returns the original, backing method of an alias method if the method is an alias. + /// </summary> + /// <param name="methodInfo">The method to check.</param> + /// /// <param name="throwOnNotAliased">if set to <c>true</c> an exception will be thrown if the method is not aliased.</param> + /// <returns></returns> + /// <exception cref="System.ArgumentException">The method was not aliased; this only occurs if throwOnNotAliased is true.</exception> + public static MethodInfo DeAliasMethod(this MethodInfo methodInfo, bool throwOnNotAliased = false) + { + MemberAliasMethodInfo aliasMethodInfo = methodInfo as MemberAliasMethodInfo; + + if (aliasMethodInfo != null) + { + while (aliasMethodInfo.AliasedMethod is MemberAliasMethodInfo) + { + aliasMethodInfo = aliasMethodInfo.AliasedMethod as MemberAliasMethodInfo; + } + + return aliasMethodInfo.AliasedMethod; + } + + if (throwOnNotAliased) + { + throw new ArgumentException("The method " + methodInfo.GetNiceName() + " was not aliased."); + } + + return methodInfo; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/MethodInfoExtensions.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/MethodInfoExtensions.cs.meta new file mode 100644 index 00000000..0c7f3001 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/MethodInfoExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 63a9a0384a6fe66fb04f82f325895b30 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/Operator.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/Operator.cs new file mode 100644 index 00000000..1d9dd605 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/Operator.cs @@ -0,0 +1,126 @@ +//----------------------------------------------------------------------- +// <copyright file="Operator.cs" company="Sirenix IVS"> +// Copyright (c) 2018 Sirenix IVS +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// </copyright> +//----------------------------------------------------------------------- +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + /// <summary> + /// Determines the type of operator. + /// </summary> + /// <seealso cref="TypeExtensions" /> + public enum Operator + { + /// <summary> + /// The == operator. + /// </summary> + Equality, + + /// <summary> + /// The != operator. + /// </summary> + Inequality, + + /// <summary> + /// The + operator. + /// </summary> + Addition, + + /// <summary> + /// The - operator. + /// </summary> + Subtraction, + + /// <summary> + /// The * operator. + /// </summary> + Multiply, + + /// <summary> + /// The / operator. + /// </summary> + Division, + + /// <summary> + /// The < operator. + /// </summary> + LessThan, + + /// <summary> + /// The > operator. + /// </summary> + GreaterThan, + + /// <summary> + /// The <= operator. + /// </summary> + LessThanOrEqual, + + /// <summary> + /// The >= operator. + /// </summary> + GreaterThanOrEqual, + + /// <summary> + /// The % operator. + /// </summary> + Modulus, + + /// <summary> + /// The >> operator. + /// </summary> + RightShift, + + /// <summary> + /// The << operator. + /// </summary> + LeftShift, + + /// <summary> + /// The & operator. + /// </summary> + BitwiseAnd, + + /// <summary> + /// The | operator. + /// </summary> + BitwiseOr, + + /// <summary> + /// The ^ operator. + /// </summary> + ExclusiveOr, + + /// <summary> + /// The ~ operator. + /// </summary> + BitwiseComplement, + + /// <summary> + /// The && operator. + /// </summary> + LogicalAnd, + + /// <summary> + /// The || operator. + /// </summary> + LogicalOr, + + /// <summary> + /// The ! operator. + /// </summary> + LogicalNot, + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/Operator.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/Operator.cs.meta new file mode 100644 index 00000000..b3e287b3 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/Operator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1df9513f03131466eecad22d1b19c4d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/PathUtilities.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/PathUtilities.cs new file mode 100644 index 00000000..7bcdfecf --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/PathUtilities.cs @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------- +// <copyright file="PathUtilities.cs" company="Sirenix IVS"> +// Copyright (c) 2018 Sirenix IVS +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// </copyright> +//----------------------------------------------------------------------- +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.IO; + using System.Text; + + /// <summary> + /// DirectoryInfo method extensions. + /// </summary> + public static class PathUtilities + { + /// <summary> + /// Determines whether the directory has a given directory in its hierarchy of children. + /// </summary> + /// <param name="parentDir">The parent directory.</param> + /// <param name="subDir">The sub directory.</param> + public static bool HasSubDirectory(this DirectoryInfo parentDir, DirectoryInfo subDir) + { + var parentDirName = parentDir.FullName.TrimEnd('\\', '/'); + + while (subDir != null) + { + if (subDir.FullName.TrimEnd('\\', '/') == parentDirName) + { + return true; + } + else + { + subDir = subDir.Parent; + } + } + + return false; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/PathUtilities.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/PathUtilities.cs.meta new file mode 100644 index 00000000..c50e1780 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/PathUtilities.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: da8aea12015a2df5402c9e2d4f1cec5c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/PropertyInfoExtensions.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/PropertyInfoExtensions.cs new file mode 100644 index 00000000..09b175c7 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/PropertyInfoExtensions.cs @@ -0,0 +1,106 @@ +//----------------------------------------------------------------------- +// <copyright file="PropertyInfoExtensions.cs" company="Sirenix IVS"> +// Copyright (c) 2018 Sirenix IVS +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// </copyright> +//----------------------------------------------------------------------- + +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.Reflection; + + /// <summary> + /// PropertyInfo method extensions. + /// </summary> + public static class PropertyInfoExtensions + { + /// <summary> + /// Determines whether a property is an auto property with a usable getter and setter. + /// </summary> + public static bool IsAutoProperty(this PropertyInfo propInfo, bool allowVirtual = false) + { + if (!(propInfo.CanWrite && propInfo.CanRead)) + { + return false; + } + + if (!allowVirtual) + { + var getter = propInfo.GetGetMethod(true); + var setter = propInfo.GetSetMethod(true); + + if ((getter != null && (getter.IsAbstract || getter.IsVirtual)) || (setter != null && (setter.IsAbstract || setter.IsVirtual))) + { + return false; + } + } + + var flag = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; + string compilerGeneratedName = "<" + propInfo.Name + ">"; + var fields = propInfo.DeclaringType.GetFields(flag); + + for (int i = 0; i < fields.Length; i++) + { + if (fields[i].Name.Contains(compilerGeneratedName)) + { + return true; + } + } + + return false; + } + + /// <summary> + /// Determines whether the specified property is an alias. + /// </summary> + /// <param name="propertyInfo">The property to check.</param> + /// <returns> + /// <c>true</c> if the specified property is an alias; otherwise, <c>false</c>. + /// </returns> + public static bool IsAliasProperty(this PropertyInfo propertyInfo) + { + return propertyInfo is MemberAliasPropertyInfo; + } + + /// <summary> + /// Returns the original, backing property of an alias property if the property is an alias. + /// </summary> + /// <param name="propertyInfo">The property to check.</param> + /// /// <param name="throwOnNotAliased">if set to <c>true</c> an exception will be thrown if the property is not aliased.</param> + /// <returns></returns> + /// <exception cref="System.ArgumentException">The property was not aliased; this only occurs if throwOnNotAliased is true.</exception> + public static PropertyInfo DeAliasProperty(this PropertyInfo propertyInfo, bool throwOnNotAliased = false) + { + MemberAliasPropertyInfo aliasPropertyInfo = propertyInfo as MemberAliasPropertyInfo; + + if (aliasPropertyInfo != null) + { + while (aliasPropertyInfo.AliasedProperty is MemberAliasPropertyInfo) + { + aliasPropertyInfo = aliasPropertyInfo.AliasedProperty as MemberAliasPropertyInfo; + } + + return aliasPropertyInfo.AliasedProperty; + } + + if (throwOnNotAliased) + { + throw new ArgumentException("The property " + propertyInfo.GetNiceName() + " was not aliased."); + } + + return propertyInfo; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/PropertyInfoExtensions.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/PropertyInfoExtensions.cs.meta new file mode 100644 index 00000000..1c704a93 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/PropertyInfoExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7f13450d6fd82372ffa7ee075a8eb4c9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/StringExtensions.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/StringExtensions.cs new file mode 100644 index 00000000..0bb70b20 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/StringExtensions.cs @@ -0,0 +1,80 @@ +//----------------------------------------------------------------------- +// <copyright file="StringExtensions.cs" company="Sirenix IVS"> +// Copyright (c) 2018 Sirenix IVS +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// </copyright> +//----------------------------------------------------------------------- + +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.Globalization; + using System.Text; + + /// <summary> + /// String method extensions. + /// </summary> + public static class StringExtensions + { + /// <summary> + /// Eg MY_INT_VALUE => MyIntValue + /// </summary> + public static string ToTitleCase(this string input) + { + var builder = new StringBuilder(); + for (int i = 0; i < input.Length; i++) + { + var current = input[i]; + if (current == '_' && i + 1 < input.Length) + { + var next = input[i + 1]; + if (char.IsLower(next)) + { + next = char.ToUpper(next, CultureInfo.InvariantCulture); + } + + builder.Append(next); + i++; + } + else + { + builder.Append(current); + } + } + + return builder.ToString(); + } + + /// <summary> + /// Returns true if this string is null, empty, or contains only whitespace. + /// </summary> + /// <param name="str">The string to check.</param> + /// <returns><c>true</c> if this string is null, empty, or contains only whitespace; otherwise, <c>false</c>.</returns> + public static bool IsNullOrWhitespace(this string str) + { + if (!string.IsNullOrEmpty(str)) + { + for (int i = 0; i < str.Length; i++) + { + if (char.IsWhiteSpace(str[i]) == false) + { + return false; + } + } + } + + return true; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/StringExtensions.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/StringExtensions.cs.meta new file mode 100644 index 00000000..53d6b4a6 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/StringExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b554cbd9469011b544a2d92ae85a3b60 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/TypeExtensions.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/TypeExtensions.cs new file mode 100644 index 00000000..b797a1f4 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/TypeExtensions.cs @@ -0,0 +1,2368 @@ +//----------------------------------------------------------------------- +// <copyright file="TypeExtensions.cs" company="Sirenix IVS"> +// Copyright (c) 2018 Sirenix IVS +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// </copyright> +//----------------------------------------------------------------------- + +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using System.Reflection; + using System.Text; + using UnityEngine; + + /// <summary> + /// Type method extensions. + /// </summary> + public static class TypeExtensions + { + private static readonly Func<float, float, bool> FloatEqualityComparerFunc = FloatEqualityComparer; + private static readonly Func<double, double, bool> DoubleEqualityComparerFunc = DoubleEqualityComparer; + private static readonly Func<Quaternion, Quaternion, bool> QuaternionEqualityComparerFunc = QuaternionEqualityComparer; + + private static readonly object GenericConstraintsSatisfaction_LOCK = new object(); + private static readonly Dictionary<Type, Type> GenericConstraintsSatisfactionInferredParameters = new Dictionary<Type, Type>(); + private static readonly Dictionary<Type, Type> GenericConstraintsSatisfactionResolvedMap = new Dictionary<Type, Type>(); + private static readonly HashSet<Type> GenericConstraintsSatisfactionProcessedParams = new HashSet<Type>(); + + private static readonly Type GenericListInterface = typeof(IList<>); + private static readonly Type GenericCollectionInterface = typeof(ICollection<>); + + private static readonly object WeaklyTypedTypeCastDelegates_LOCK = new object(); + private static readonly object StronglyTypedTypeCastDelegates_LOCK = new object(); + private static readonly DoubleLookupDictionary<Type, Type, Func<object, object>> WeaklyTypedTypeCastDelegates = new DoubleLookupDictionary<Type, Type, Func<object, object>>(); + private static readonly DoubleLookupDictionary<Type, Type, Delegate> StronglyTypedTypeCastDelegates = new DoubleLookupDictionary<Type, Type, Delegate>(); + + private static readonly Type[] TwoLengthTypeArray_Cached = new Type[2]; + + private static readonly Stack<Type> GenericArgumentsContainsTypes_ArgsToCheckCached = new Stack<Type>(); + + private static HashSet<string> ReservedCSharpKeywords = new HashSet<string>() + { + "abstract", + "as", + "base", + "bool", + "break", + "byte", + "case", + "catch", + "char", + "checked", + "class", + "const", + "continue", + "decimal", + "default", + "delegate", + "do", + "double", + "else", + "enum", + "event", + "explicit", + "extern", + "false", + "finally", + "fixed", + "float", + "for", + "foreach", + "goto", + "if", + "implicit", + "in", + "int", + "interface", + "internal", + "is", + "lock", + "long", + "namespace", + "new", + "null", + "object", + "operator", + "out", + "override", + "params", + "private", + "protected", + "public", + "readonly", + "ref", + "return", + "sbyte", + "sealed", + "short", + "sizeof", + "stackalloc", + "static", + "string", + "struct", + "switch", + "this", + "throw", + "true", + "try", + "typeof", + "uint", + "ulong", + "unchecked", + "unsafe", + "ushort", + "using", + "static", + "void", + "volatile", + "while", + "in", + "get", + "set", + "var", + //"async", // Identifiers can be named async and await + //"await", + }; + + /// <summary> + /// Type name alias lookup. + /// TypeNameAlternatives["Single"] will give you "float", "UInt16" will give you "ushort", "Boolean[]" will give you "bool[]" etc.. + /// </summary> + public static readonly Dictionary<string, string> TypeNameAlternatives = new Dictionary<string, string>() + { + { "Single", "float" }, + { "Double", "double" }, + { "SByte", "sbyte" }, + { "Int16", "short" }, + { "Int32", "int" }, + { "Int64", "long" }, + { "Byte", "byte" }, + { "UInt16", "ushort" }, + { "UInt32", "uint" }, + { "UInt64", "ulong" }, + { "Decimal", "decimal" }, + { "String", "string" }, + { "Char", "char" }, + { "Boolean", "bool" }, + { "Single[]", "float[]" }, + { "Double[]", "double[]" }, + { "SByte[]", "sbyte[]" }, + { "Int16[]", "short[]" }, + { "Int32[]", "int[]" }, + { "Int64[]", "long[]" }, + { "Byte[]", "byte[]" }, + { "UInt16[]", "ushort[]" }, + { "UInt32[]", "uint[]" }, + { "UInt64[]", "ulong[]" }, + { "Decimal[]", "decimal[]" }, + { "String[]", "string[]" }, + { "Char[]", "char[]" }, + { "Boolean[]", "bool[]" }, + }; + + private static readonly object CachedNiceNames_LOCK = new object(); + private static readonly Dictionary<Type, string> CachedNiceNames = new Dictionary<Type, string>(); + + private static string GetCachedNiceName(Type type) + { + string result; + lock (CachedNiceNames_LOCK) + { + if (!CachedNiceNames.TryGetValue(type, out result)) + { + result = CreateNiceName(type); + CachedNiceNames.Add(type, result); + } + } + return result; + } + + private static string CreateNiceName(Type type) + { + if (type.IsArray) + { + int rank = type.GetArrayRank(); + return type.GetElementType().GetNiceName() + (rank == 1 ? "[]" : "[,]"); + } + + if (type.InheritsFrom(typeof(Nullable<>))) + { + return type.GetGenericArguments()[0].GetNiceName() + "?"; + } + + if (type.IsByRef) + { + return "ref " + type.GetElementType().GetNiceName(); + } + + if (type.IsGenericParameter || !type.IsGenericType) + { + return TypeNameGauntlet(type); + } + + var builder = new StringBuilder(); + var name = type.Name; + var index = name.IndexOf("`"); + + if (index != -1) + { + builder.Append(name.Substring(0, index)); + } + else + { + builder.Append(name); + } + + builder.Append('<'); + var args = type.GetGenericArguments(); + + for (int i = 0; i < args.Length; i++) + { + var arg = args[i]; + + if (i != 0) + { + builder.Append(", "); + } + + builder.Append(GetNiceName(arg)); + } + + builder.Append('>'); + return builder.ToString(); + } + + private static readonly Type VoidPointerType = typeof(void).MakePointerType(); + + private static readonly Dictionary<Type, HashSet<Type>> PrimitiveImplicitCasts = new Dictionary<Type, HashSet<Type>>() + { + { typeof(Int64), new HashSet<Type>() { typeof(Single), typeof(Double), typeof(Decimal) } }, + { typeof(Int32), new HashSet<Type>() { typeof(Int64), typeof(Single), typeof(Double), typeof(Decimal) } }, + { typeof(Int16), new HashSet<Type>() { typeof(Int32), typeof(Int64), typeof(Single), typeof(Double), typeof(Decimal) } }, + { typeof(SByte), new HashSet<Type>() { typeof(Int16), typeof(Int32), typeof(Int64), typeof(Single), typeof(Double), typeof(Decimal) } }, + { typeof(UInt64), new HashSet<Type>() { typeof(Single), typeof(Double), typeof(Decimal) } }, + { typeof(UInt32), new HashSet<Type>() { typeof(Int64), typeof(UInt64), typeof(Single), typeof(Double), typeof(Decimal) } }, + { typeof(UInt16), new HashSet<Type>() { typeof(Int32), typeof(UInt32), typeof(Int64), typeof(UInt64), typeof(Single), typeof(Double), typeof(Decimal) } }, + { typeof(Byte), new HashSet<Type>() { typeof(Int16), typeof(UInt16), typeof(Int32), typeof(UInt32), typeof(Int64), typeof(UInt64), typeof(Single), typeof(Double), typeof(Decimal) } }, + { typeof(Char), new HashSet<Type>() { typeof(UInt16), typeof(Int32), typeof(UInt32), typeof(Int64), typeof(UInt64), typeof(Single), typeof(Double), typeof(Decimal) } }, + { typeof(Boolean), new HashSet<Type>() { } }, + { typeof(Decimal), new HashSet<Type>() { } }, + { typeof(Single), new HashSet<Type>() { typeof(Double) } }, + { typeof(Double), new HashSet<Type>() { } }, + { typeof(IntPtr), new HashSet<Type>() { } }, + { typeof(UIntPtr), new HashSet<Type>() { } }, + { VoidPointerType, new HashSet<Type>() { } }, + }; + + private static readonly HashSet<Type> ExplicitCastIntegrals = new HashSet<Type>() + { + { typeof(Int64) }, + { typeof(Int32) }, + { typeof(Int16) }, + { typeof(SByte) }, + { typeof(UInt64) }, + { typeof(UInt32) }, + { typeof(UInt16) }, + { typeof(Byte) }, + { typeof(Char) }, + { typeof(Decimal) }, + { typeof(Single) }, + { typeof(Double) }, + { typeof(IntPtr) }, + { typeof(UIntPtr) } + }; + + internal static bool HasCastDefined(this Type from, Type to, bool requireImplicitCast) + { + if (from.IsEnum) + { + return Enum.GetUnderlyingType(from).IsCastableTo(to); + } + + if (to.IsEnum) + { + return Enum.GetUnderlyingType(to).IsCastableTo(from); + } + + if ((from.IsPrimitive || from == VoidPointerType) && (to.IsPrimitive || to == VoidPointerType)) + { + if (requireImplicitCast) + { + return PrimitiveImplicitCasts[from].Contains(to); + } + else + { + if (from == typeof(IntPtr)) + { + if (to == typeof(UIntPtr)) + { + return false; + } + else if (to == VoidPointerType) + { + return true; + } + } + else if (from == typeof(UIntPtr)) + { + if (to == typeof(IntPtr)) + { + return false; + } + else if (to == VoidPointerType) + { + return true; + } + } + + return ExplicitCastIntegrals.Contains(from) && ExplicitCastIntegrals.Contains(to); + } + } + + return from.GetCastMethod(to, requireImplicitCast) != null; + } + + /// <summary> + /// Checks whether a given string is a valid CSharp identifier name. This also checks full type names including namespaces. + /// </summary> + /// <param name="identifier">The identifier to check.</param> + public static bool IsValidIdentifier(string identifier) + { + if (identifier == null || identifier.Length == 0) + { + return false; + } + + int dotIndex = identifier.IndexOf('.'); + + if (dotIndex >= 0) + { + string[] identifiers = identifier.Split('.'); + + for (int i = 0; i < identifiers.Length; i++) + { + if (!IsValidIdentifier(identifiers[i])) + { + return false; + } + } + + return true; + } + + if (ReservedCSharpKeywords.Contains(identifier)) + { + return false; + } + + if (!IsValidIdentifierStartCharacter(identifier[0])) + { + return false; + } + + for (int i = 1; i < identifier.Length; i++) + { + if (!IsValidIdentifierPartCharacter(identifier[i])) + { + return false; + } + } + + return true; + } + + private static bool IsValidIdentifierStartCharacter(char c) + { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '@' || char.IsLetter(c); + } + + private static bool IsValidIdentifierPartCharacter(char c) + { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9') || char.IsLetter(c); + } + + /// <summary> + /// Determines whether a type can be casted to another type. + /// </summary> + /// <param name="from">From.</param> + /// <param name="to">To.</param> + /// <param name="requireImplicitCast">if set to <c>true</c> an implicit or explicit operator must be defined on the given type.</param> + public static bool IsCastableTo(this Type from, Type to, bool requireImplicitCast = false) + { + if (from == null) + { + throw new ArgumentNullException("from"); + } + + if (to == null) + { + throw new ArgumentNullException("to"); + } + + if (from == to) + { + return true; + } + + return to.IsAssignableFrom(from) || from.HasCastDefined(to, requireImplicitCast); + } + + /// <summary> + /// If a type can be casted to another type, this provides a function to manually convert the type. + /// </summary> + /// <param name="from">From.</param> + /// <param name="to">To.</param> + /// <param name="requireImplicitCast">if set to <c>true</c> an implicit or explicit operator must be defined on the given type.</param> + public static Func<object, object> GetCastMethodDelegate(this Type from, Type to, bool requireImplicitCast = false) + { + Func<object, object> result; + + lock (WeaklyTypedTypeCastDelegates_LOCK) + { + if (WeaklyTypedTypeCastDelegates.TryGetInnerValue(from, to, out result) == false) + { + var method = GetCastMethod(from, to, requireImplicitCast); + + if (method != null) + { + result = (obj) => method.Invoke(null, new object[] { obj }); + } + + WeaklyTypedTypeCastDelegates.AddInner(from, to, result); + } + } + + return result; + } + + /// <summary> + /// If a type can be casted to another type, this provides a function to manually convert the type. + /// </summary> + /// <param name="requireImplicitCast">if set to <c>true</c> an implicit or explicit operator must be defined on the given type.</param> + public static Func<TFrom, TTo> GetCastMethodDelegate<TFrom, TTo>(bool requireImplicitCast = false) + { + Delegate del; + + lock (StronglyTypedTypeCastDelegates_LOCK) + { + if (StronglyTypedTypeCastDelegates.TryGetInnerValue(typeof(TFrom), typeof(TTo), out del) == false) + { + var method = GetCastMethod(typeof(TFrom), typeof(TTo), requireImplicitCast); + + if (method != null) + { + del = Delegate.CreateDelegate(typeof(Func<TFrom, TTo>), method); + } + + StronglyTypedTypeCastDelegates.AddInner(typeof(TFrom), typeof(TTo), del); + } + } + + return (Func<TFrom, TTo>)del; + } + + /// <summary> + /// If a type can be casted to another type, this provides the method info of the method in charge of converting the type. + /// </summary> + /// <param name="from">From.</param> + /// <param name="to">To.</param> + /// <param name="requireImplicitCast">if set to <c>true</c> an implicit or explicit operator must be defined on the given type.</param> + public static MethodInfo GetCastMethod(this Type from, Type to, bool requireImplicitCast = false) + { + var fromMethods = from.GetAllMembers<MethodInfo>(BindingFlags.Public | BindingFlags.Static); + + foreach (var method in fromMethods) + { + if ((method.Name == "op_Implicit" || (requireImplicitCast == false && method.Name == "op_Explicit")) && to.IsAssignableFrom(method.ReturnType)) + { + return method; + } + } + + var toMethods = to.GetAllMembers<MethodInfo>(BindingFlags.Public | BindingFlags.Static); + + foreach (var method in toMethods) + { + if ((method.Name == "op_Implicit" || (requireImplicitCast == false && method.Name == "op_Explicit")) && method.GetParameters()[0].ParameterType.IsAssignableFrom(from)) + { + return method; + } + } + + return null; + } + + private static bool FloatEqualityComparer(float a, float b) + { + if (float.IsNaN(a) && float.IsNaN(b)) return true; + return a == b; + } + + private static bool DoubleEqualityComparer(double a, double b) + { + if (double.IsNaN(a) && double.IsNaN(b)) return true; + return a == b; + } + + private static bool QuaternionEqualityComparer(Quaternion a, Quaternion b) + { + return a.x == b.x && a.y == b.y && a.z == b.z && a.w == b.w; + } + + /// <summary> + /// Gets an equality comparer delegate used to compare the equality of values of a given type. In order, this will be: + /// + /// 1. The == operator, if one is defined on the type. + /// 2. A delegate that uses <see cref="IEquatable{T}"/>, if the type implements that interface. + /// 3. .NET's own <see cref="EqualityComparer{T}.Default"/> + /// </summary> + /// <remarks> + /// <para>Note that in the special case of the type <see cref="UnityEngine.Quaternion"/>, a special equality comparer is returned that only checks whether all the Quaternion components are equal.</para> + /// <para>This is because, by default, Quaternion's equality operator is broken when operating on invalid quaternions; "default(Quaternion) == default(Quaternion)" evaluates to false, and this causes a multitude of problems.</para> + /// <para>Special delegates are also returned for float and double, that consider float.NaN to be equal to float.NaN, and double.NaN to be equal to double.NaN.</para> + /// </remarks> + public static Func<T, T, bool> GetEqualityComparerDelegate<T>() + { + if (typeof(T) == typeof(float)) + return (Func<T, T, bool>)(object)FloatEqualityComparerFunc; + else if (typeof(T) == typeof(double)) + return (Func<T, T, bool>)(object)DoubleEqualityComparerFunc; + else if (typeof(T) == typeof(Quaternion)) + return (Func<T, T, bool>)(object)QuaternionEqualityComparerFunc; + + Func<T, T, bool> result = null; + MethodInfo equalityMethod; + + if (typeof(IEquatable<T>).IsAssignableFrom(typeof(T))) + { + if (typeof(T).IsValueType) + { + result = (a, b) => + { + return ((IEquatable<T>)a).Equals(b); + }; + } + else + { + result = (a, b) => + { + if (object.ReferenceEquals(a, b)) + { + return true; + } + else if (object.ReferenceEquals(a, null)) + { + return false; + } + else + { + return ((IEquatable<T>)a).Equals(b); + } + }; + } + } + else + { + Type currentType = typeof(T); + + while (currentType != null && currentType != typeof(object)) + { + equalityMethod = currentType.GetOperatorMethod(Operator.Equality, currentType, currentType); + + if (equalityMethod != null) + { + result = (Func<T, T, bool>)Delegate.CreateDelegate(typeof(Func<T, T, bool>), equalityMethod, true); + break; + } + + currentType = currentType.BaseType; + } + } + + if (result == null) + { + var comparer = EqualityComparer<T>.Default; + result = comparer.Equals; + } + + return result; + } + + /// <summary> + /// Gets the first attribute of type T. Returns null in the no attribute of type T was found. + /// </summary> + /// <param name="type">The type.</param> + /// <param name="inherit">If true, specifies to also search the ancestors of element for custom attributes.</param> + public static T GetAttribute<T>(this Type type, bool inherit) where T : Attribute + { + var attrs = type.GetCustomAttributes(typeof(T), inherit); + + if (attrs.Length == 0) + { + return null; + } + else + { + return (T)attrs[0]; + } + } + + /// <summary> + /// Determines whether a type implements or inherits from another type. + /// </summary> + /// <param name="type">The type.</param> + /// <param name="to">To.</param> + public static bool ImplementsOrInherits(this Type type, Type to) + { + return to.IsAssignableFrom(type); + } + + /// <summary> + /// Determines whether a type implements an open generic interface or class such as IList<> or List<>. + /// </summary> + /// <param name="candidateType">Type of the candidate.</param> + /// <param name="openGenericType">Type of the open generic type.</param> + /// <returns></returns> + public static bool ImplementsOpenGenericType(this Type candidateType, Type openGenericType) + { + if (openGenericType.IsInterface) return candidateType.ImplementsOpenGenericInterface(openGenericType); + else return candidateType.ImplementsOpenGenericClass(openGenericType); + } + + /// <summary> + /// Determines whether a type implements an open generic interface such as IList<>. + /// </summary> + /// <param name="candidateType">Type of the candidate.</param> + /// <param name="openGenericInterfaceType">Type of the open generic interface.</param> + /// <exception cref="System.ArgumentNullException"></exception> + /// <exception cref="System.ArgumentException">Type " + openGenericInterfaceType.Name + " is not a generic type definition and an interface.</exception> + public static bool ImplementsOpenGenericInterface(this Type candidateType, Type openGenericInterfaceType) + { + if (candidateType == openGenericInterfaceType) + return true; + + if (candidateType.IsGenericType && candidateType.GetGenericTypeDefinition() == openGenericInterfaceType) + return true; + + var interfaces = candidateType.GetInterfaces(); + + for (int i = 0; i < interfaces.Length; i++) + { + if (interfaces[i].ImplementsOpenGenericInterface(openGenericInterfaceType)) + return true; + } + + return false; + } + + /// <summary> + /// Determines whether a type implements an open generic class such as List<>. + /// </summary> + /// <param name="candidateType">Type of the candidate.</param> + /// <param name="openGenericType">Type of the open generic interface.</param> + public static bool ImplementsOpenGenericClass(this Type candidateType, Type openGenericType) + { + if (candidateType.IsGenericType && candidateType.GetGenericTypeDefinition() == openGenericType) + return true; + + var baseType = candidateType.BaseType; + + if (baseType != null && baseType.ImplementsOpenGenericClass(openGenericType)) + return true; + + return false; + } + + /// <summary> + /// Gets the generic arguments of an inherited open generic class or interface. + /// </summary> + /// <param name="candidateType">Type of the candidate.</param> + /// <param name="openGenericType">The open generic type to get the arguments of.</param> + public static Type[] GetArgumentsOfInheritedOpenGenericType(this Type candidateType, Type openGenericType) + { + if (openGenericType.IsInterface) return candidateType.GetArgumentsOfInheritedOpenGenericInterface(openGenericType); + else return candidateType.GetArgumentsOfInheritedOpenGenericClass(openGenericType); + } + + /// <summary> + /// Gets the generic arguments of an inherited open generic class. + /// </summary> + /// <param name="candidateType">Type of the candidate.</param> + /// <param name="openGenericType">Type of the open generic class.</param> + public static Type[] GetArgumentsOfInheritedOpenGenericClass(this Type candidateType, Type openGenericType) + { + if (candidateType.IsGenericType && candidateType.GetGenericTypeDefinition() == openGenericType) + return candidateType.GetGenericArguments(); + + var baseType = candidateType.BaseType; + + if (baseType != null) + return baseType.GetArgumentsOfInheritedOpenGenericClass(openGenericType); + + return null; + } + + /// <summary> + /// Gets the generic arguments of an inherited open generic interface. + /// </summary> + /// <param name="candidateType">Type of the candidate.</param> + /// <param name="openGenericInterfaceType">Type of the open generic interface.</param> + public static Type[] GetArgumentsOfInheritedOpenGenericInterface(this Type candidateType, Type openGenericInterfaceType) + { + // This if clause fixes an "error" in newer .NET Runtimes where enum arrays + // implement interfaces like IList<int>, which will be matched on by Odin + // before the IList<TheEnum> interface and cause a lot of issues because + // you can't actually use an enum array as if it was an IList<int>. + if ((openGenericInterfaceType == GenericListInterface || openGenericInterfaceType == GenericCollectionInterface) && candidateType.IsArray) + { + return new Type[] { candidateType.GetElementType() }; + } + + if (candidateType == openGenericInterfaceType) + return candidateType.GetGenericArguments(); + + if (candidateType.IsGenericType && candidateType.GetGenericTypeDefinition() == openGenericInterfaceType) + return candidateType.GetGenericArguments(); + + var interfaces = candidateType.GetInterfaces(); + + for (int i = 0; i < interfaces.Length; i++) + { + var @interface = interfaces[i]; + if (!@interface.IsGenericType) continue; + + var result = @interface.GetArgumentsOfInheritedOpenGenericInterface(openGenericInterfaceType); + + if (result != null) + return result; + } + + return null; + } + + /// <summary> + /// Gets the MethodInfo of a specific operator kind, with the given left and right operands. This overload is *far* faster than any of the other GetOperatorMethod implementations, and should be used whenever possible. + /// </summary> + public static MethodInfo GetOperatorMethod(this Type type, Operator op, Type leftOperand, Type rightOperand) + { + string methodName; + + switch (op) + { + case Operator.Equality: + methodName = "op_Equality"; + break; + + case Operator.Inequality: + methodName = "op_Inequality"; + break; + + case Operator.Addition: + methodName = "op_Addition"; + break; + + case Operator.Subtraction: + methodName = "op_Subtraction"; + break; + + case Operator.Multiply: + methodName = "op_Multiply"; + break; + + case Operator.Division: + methodName = "op_Division"; + break; + + case Operator.LessThan: + methodName = "op_LessThan"; + break; + + case Operator.GreaterThan: + methodName = "op_GreaterThan"; + break; + + case Operator.LessThanOrEqual: + methodName = "op_LessThanOrEqual"; + break; + + case Operator.GreaterThanOrEqual: + methodName = "op_GreaterThanOrEqual"; + break; + + case Operator.Modulus: + methodName = "op_Modulus"; + break; + + case Operator.RightShift: + methodName = "op_RightShift"; + break; + + case Operator.LeftShift: + methodName = "op_LeftShift"; + break; + + case Operator.BitwiseAnd: + methodName = "op_BitwiseAnd"; + break; + + case Operator.BitwiseOr: + methodName = "op_BitwiseOr"; + break; + + case Operator.ExclusiveOr: + methodName = "op_ExclusiveOr"; + break; + + case Operator.BitwiseComplement: + methodName = "op_OnesComplement"; + break; + + case Operator.LogicalNot: + methodName = "op_LogicalNot"; + break; + + case Operator.LogicalAnd: + case Operator.LogicalOr: + return null; // Not overridable + + default: + throw new NotImplementedException(); + } + + var types = TwoLengthTypeArray_Cached; + + lock (types) + { + types[0] = leftOperand; + types[1] = rightOperand; + + try + { + var result = type.GetMethod(methodName, Flags.StaticAnyVisibility, null, types, null); + + if (result != null && result.ReturnType != typeof(bool)) return null; + + return result; + } + catch (AmbiguousMatchException) + { + // We fallback to manual resolution + var methods = type.GetMethods(Flags.StaticAnyVisibility); + + for (int i = 0; i < methods.Length; i++) + { + var method = methods[i]; + if (method.Name != methodName) continue; + if (method.ReturnType != typeof(bool)) continue; + var parameters = method.GetParameters(); + if (parameters.Length != 2) continue; + if (!parameters[0].ParameterType.IsAssignableFrom(leftOperand)) continue; + if (!parameters[1].ParameterType.IsAssignableFrom(rightOperand)) continue; + + return method; + } + + return null; + } + } + } + + /// <summary> + /// Gets the MethodInfo of a specific operator type. + /// </summary> + public static MethodInfo GetOperatorMethod(this Type type, Operator op) + { + string methodName; + + switch (op) + { + case Operator.Equality: + methodName = "op_Equality"; + break; + + case Operator.Inequality: + methodName = "op_Inequality"; + break; + + case Operator.Addition: + methodName = "op_Addition"; + break; + + case Operator.Subtraction: + methodName = "op_Subtraction"; + break; + + case Operator.Multiply: + methodName = "op_Multiply"; + break; + + case Operator.Division: + methodName = "op_Division"; + break; + + case Operator.LessThan: + methodName = "op_LessThan"; + break; + + case Operator.GreaterThan: + methodName = "op_GreaterThan"; + break; + + case Operator.LessThanOrEqual: + methodName = "op_LessThanOrEqual"; + break; + + case Operator.GreaterThanOrEqual: + methodName = "op_GreaterThanOrEqual"; + break; + + case Operator.Modulus: + methodName = "op_Modulus"; + break; + + case Operator.RightShift: + methodName = "op_RightShift"; + break; + + case Operator.LeftShift: + methodName = "op_LeftShift"; + break; + + case Operator.BitwiseAnd: + methodName = "op_BitwiseAnd"; + break; + + case Operator.BitwiseOr: + methodName = "op_BitwiseOr"; + break; + + case Operator.ExclusiveOr: + methodName = "op_ExclusiveOr"; + break; + + case Operator.BitwiseComplement: + methodName = "op_OnesComplement"; + break; + + case Operator.LogicalNot: + methodName = "op_LogicalNot"; + break; + + case Operator.LogicalAnd: + case Operator.LogicalOr: + return null; // Not overridable + + default: + throw new NotImplementedException(); + } + + return type.GetAllMembers<MethodInfo>(Flags.StaticAnyVisibility).FirstOrDefault(m => m.Name == methodName); + } + + /// <summary> + /// Gets the MethodInfo of a specific operator type. + /// </summary> + public static MethodInfo[] GetOperatorMethods(this Type type, Operator op) + { + string methodName; + + switch (op) + { + // TODO: Add Divide and other names for other .Net versions + case Operator.Equality: + methodName = "op_Equality"; + break; + + case Operator.Inequality: + methodName = "op_Inequality"; + break; + + case Operator.Addition: + methodName = "op_Addition"; + break; + + case Operator.Subtraction: + methodName = "op_Subtraction"; + break; + + case Operator.Multiply: + methodName = "op_Multiply"; + break; + + case Operator.Division: + methodName = "op_Division"; + break; + + case Operator.LessThan: + methodName = "op_LessThan"; + break; + + case Operator.GreaterThan: + methodName = "op_GreaterThan"; + break; + + case Operator.LessThanOrEqual: + methodName = "op_LessThanOrEqual"; + break; + + case Operator.GreaterThanOrEqual: + methodName = "op_GreaterThanOrEqual"; + break; + + case Operator.Modulus: + methodName = "op_Modulus"; + break; + + case Operator.RightShift: + methodName = "op_RightShift"; + break; + + case Operator.LeftShift: + methodName = "op_LeftShift"; + break; + + case Operator.BitwiseAnd: + methodName = "op_BitwiseAnd"; + break; + + case Operator.BitwiseOr: + methodName = "op_BitwiseOr"; + break; + + case Operator.ExclusiveOr: + methodName = "op_ExclusiveOr"; + break; + + case Operator.BitwiseComplement: + methodName = "op_OnesComplement"; + break; + + case Operator.LogicalNot: + methodName = "op_LogicalNot"; + break; + + case Operator.LogicalAnd: + case Operator.LogicalOr: + return null; // Not overridable + + default: + throw new NotImplementedException(); + } + + return type.GetAllMembers<MethodInfo>(Flags.StaticAnyVisibility).Where(x => x.Name == methodName).ToArray(); + } + + /// <summary> + /// Gets all members from a given type, including members from all base types if the <see cref="BindingFlags.DeclaredOnly"/> flag isn't set. + /// </summary> + public static IEnumerable<MemberInfo> GetAllMembers(this Type type, BindingFlags flags = BindingFlags.Default) + { + Type currentType = type; + + if ((flags & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly) + { + foreach (var member in currentType.GetMembers(flags)) + { + yield return member; + } + } + else + { + flags |= BindingFlags.DeclaredOnly; + + do + { + foreach (var member in currentType.GetMembers(flags)) + { + yield return member; + } + + currentType = currentType.BaseType; + } + while (currentType != null); + } + } + + /// <summary> + /// Gets all members from a given type, including members from all base types. + /// </summary> + public static IEnumerable<MemberInfo> GetAllMembers(this Type type, string name, BindingFlags flags = BindingFlags.Default) + { + foreach (var member in type.GetAllMembers(flags)) + { + if (member.Name != name) continue; + yield return member; + } + } + + /// <summary> + /// Gets all members of a specific type from a type, including members from all base types, if the <see cref="BindingFlags.DeclaredOnly"/> flag isn't set. + /// </summary> + public static IEnumerable<T> GetAllMembers<T>(this Type type, BindingFlags flags = BindingFlags.Default) where T : MemberInfo + { + if (type == null) throw new ArgumentNullException("type"); + if (type == typeof(object)) yield break; + + Type currentType = type; + + if ((flags & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly) + { + foreach (var member in currentType.GetMembers(flags)) + { + var found = member as T; + + if (found != null) + { + yield return found; + } + } + } + else + { + flags |= BindingFlags.DeclaredOnly; + + do + { + foreach (var member in currentType.GetMembers(flags)) + { + var found = member as T; + + if (found != null) + { + yield return found; + } + } + + currentType = currentType.BaseType; + } + while (currentType != null); + } + } + + /// <summary> + /// Gets the generic type definition of an open generic base type. + /// </summary> + public static Type GetGenericBaseType(this Type type, Type baseType) + { + int count; + return GetGenericBaseType(type, baseType, out count); + } + + /// <summary> + /// Gets the generic type definition of an open generic base type. + /// </summary> + public static Type GetGenericBaseType(this Type type, Type baseType, out int depthCount) + { + if (type == null) + { + throw new ArgumentNullException("type"); + } + + if (baseType == null) + { + throw new ArgumentNullException("baseType"); + } + + if (baseType.IsGenericType == false) + { + throw new ArgumentException("Type " + baseType.Name + " is not a generic type."); + } + + if (type.InheritsFrom(baseType) == false) + { + throw new ArgumentException("Type " + type.Name + " does not inherit from " + baseType.Name + "."); + } + + var t = type; + + depthCount = 0; + while (t != null && (t.IsGenericType == false || t.GetGenericTypeDefinition() != baseType)) + { + depthCount++; + t = t.BaseType; + } + + if (t == null) + { + throw new ArgumentException(type.Name + " is assignable from " + baseType.Name + ", but base type was not found?"); + } + + return t; + } + + /// <summary> + /// Returns a lazy enumerable of all the base types of this type including interfaces and classes + /// </summary> + public static IEnumerable<Type> GetBaseTypes(this Type type, bool includeSelf = false) + { + var result = GetBaseClasses(type, includeSelf).Concat(type.GetInterfaces()); + if (includeSelf && type.IsInterface) + { + result.Concat(new Type[] { type }); + } + return result; + } + + /// <summary> + /// Returns a lazy enumerable of all the base classes of this type + /// </summary> + public static IEnumerable<Type> GetBaseClasses(this Type type, bool includeSelf = false) + { + if (type == null || type.BaseType == null) + { + yield break; + } + + if (includeSelf) + { + yield return type; + } + + var current = type.BaseType; + + while (current != null) + { + yield return current; + current = current.BaseType; + } + } + + /// <summary> + /// Used to filter out unwanted type names. Ex "int" instead of "Int32" + /// </summary> + private static string TypeNameGauntlet(this Type type) + { + string typeName = type.Name; + + string altTypeName = string.Empty; + + if (TypeNameAlternatives.TryGetValue(typeName, out altTypeName)) + { + typeName = altTypeName; + } + + return typeName; + } + + /// <summary> + /// Returns a nicely formatted name of a type. + /// </summary> + public static string GetNiceName(this Type type) + { + if (type.IsNested && type.IsGenericParameter == false) + { + return type.DeclaringType.GetNiceName() + "." + GetCachedNiceName(type); + } + + return GetCachedNiceName(type); + } + + /// <summary> + /// Returns a nicely formatted full name of a type. + /// </summary> + public static string GetNiceFullName(this Type type) + { + string result; + + if (type.IsNested && type.IsGenericParameter == false) + { + return type.DeclaringType.GetNiceFullName() + "." + GetCachedNiceName(type); + } + + result = GetCachedNiceName(type); + + if (type.Namespace != null) + { + result = type.Namespace + "." + result; + } + + return result; + } + + /// <summary> + /// Gets the name of the compilable nice. + /// </summary> + /// <param name="type">The type.</param> + public static string GetCompilableNiceName(this Type type) + { + return type.GetNiceName().Replace('<', '_').Replace('>', '_').TrimEnd('_'); + } + + /// <summary> + /// Gets the full name of the compilable nice. + /// </summary> + /// <param name="type">The type.</param> + public static string GetCompilableNiceFullName(this Type type) + { + return type.GetNiceFullName().Replace('<', '_').Replace('>', '_').TrimEnd('_'); + } + + /// <summary> + /// Returns the first found custom attribute of type T on this type + /// Returns null if none was found + /// </summary> + public static T GetCustomAttribute<T>(this Type type, bool inherit) where T : Attribute + { + var attrs = type.GetCustomAttributes(typeof(T), inherit); + if (attrs.Length == 0) return null; + return attrs[0] as T; + } + + /// <summary> + /// Returns the first found non-inherited custom attribute of type T on this type + /// Returns null if none was found + /// </summary> + public static T GetCustomAttribute<T>(this Type type) where T : Attribute + { + return GetCustomAttribute<T>(type, false); + } + + /// <summary> + /// Gets all attributes of type T. + /// </summary> + /// <param name="type">The type.</param> + public static IEnumerable<T> GetCustomAttributes<T>(this Type type) where T : Attribute + { + return GetCustomAttributes<T>(type, false); + } + + /// <summary> + /// Gets all attributes of type T. + /// </summary> + /// <param name="type">The type</param> + /// <param name="inherit">If true, specifies to also search the ancestors of element for custom attributes.</param> + public static IEnumerable<T> GetCustomAttributes<T>(this Type type, bool inherit) where T : Attribute + { + var attrs = type.GetCustomAttributes(typeof(T), inherit); + + for (int i = 0; i < attrs.Length; i++) + { + yield return attrs[i] as T; + } + } + + /// <summary> + /// Returns true if the attribute whose type is specified by the generic argument is defined on this type + /// </summary> + public static bool IsDefined<T>(this Type type) where T : Attribute + { + return type.IsDefined(typeof(T), false); + } + + /// <summary> + /// Returns true if the attribute whose type is specified by the generic argument is defined on this type + /// </summary> + public static bool IsDefined<T>(this Type type, bool inherit) where T : Attribute + { + return type.IsDefined(typeof(T), inherit); + } + + /// <summary> + /// Determines whether a type inherits or implements another type. Also include support for open generic base types such as List<>. + /// </summary> + /// <param name="type"></param> + public static bool InheritsFrom<TBase>(this Type type) + { + return type.InheritsFrom(typeof(TBase)); + } + + /// <summary> + /// Determines whether a type inherits or implements another type. Also include support for open generic base types such as List<>. + /// </summary> + /// <param name="type"></param> + /// <param name="baseType"></param> + public static bool InheritsFrom(this Type type, Type baseType) + { + if (baseType.IsAssignableFrom(type)) + { + return true; + } + + if (type.IsInterface && baseType.IsInterface == false) + { + return false; + } + + if (baseType.IsInterface) + { + return type.GetInterfaces().Contains(baseType); + } + + var t = type; + while (t != null) + { + if (t == baseType) + { + return true; + } + + if (baseType.IsGenericTypeDefinition && t.IsGenericType && t.GetGenericTypeDefinition() == baseType) + { + return true; + } + + t = t.BaseType; + } + + return false; + } + + /// <summary> + /// Gets the number of base types between given type and baseType. + /// </summary> + public static int GetInheritanceDistance(this Type type, Type baseType) + { + Type lowerType; + Type higherType; + + if (type.IsAssignableFrom(baseType)) + { + higherType = type; + lowerType = baseType; + } + else if (baseType.IsAssignableFrom(type)) + { + higherType = baseType; + lowerType = type; + } + else + { + throw new ArgumentException("Cannot assign types '" + type.GetNiceName() + "' and '" + baseType.GetNiceName() + "' to each other."); + } + + Type currentType = lowerType; + int count = 0; + + if (higherType.IsInterface) + { + while (currentType != null && currentType != typeof(object)) + { + count++; + currentType = currentType.BaseType; + + var interfaces = currentType.GetInterfaces(); + + for (int i = 0; i < interfaces.Length; i++) + { + if (interfaces[i] == higherType) + { + currentType = null; + break; + } + } + } + } + else + { + while (currentType != higherType && currentType != null && currentType != typeof(object)) + { + count++; + currentType = currentType.BaseType; + } + } + + return count; + } + + /// <summary> + /// Determines whether a method has the specified parameter types. + /// </summary> + public static bool HasParamaters(this MethodInfo methodInfo, IList<Type> paramTypes, bool inherit = true) + { + var methodParams = methodInfo.GetParameters(); + if (methodParams.Length == paramTypes.Count) + { + for (int i = 0; i < methodParams.Length; i++) + { + if (inherit && paramTypes[i].InheritsFrom(methodParams[i].ParameterType) == false) + { + return false; + } + else if (methodParams[i].ParameterType != paramTypes[i]) + { + return false; + } + } + return true; + } + return false; + } + + /// <summary> + /// FieldInfo will return the fieldType, propertyInfo the PropertyType, MethodInfo the return type and EventInfo will return the EventHandlerType. + /// </summary> + /// <param name="memberInfo">The MemberInfo.</param> + public static Type GetReturnType(this MemberInfo memberInfo) + { + var fieldInfo = memberInfo as FieldInfo; + if (fieldInfo != null) + { + return fieldInfo.FieldType; + } + + var propertyInfo = memberInfo as PropertyInfo; + if (propertyInfo != null) + { + return propertyInfo.PropertyType; + } + + var methodInfo = memberInfo as MethodInfo; + if (methodInfo != null) + { + return methodInfo.ReturnType; + } + + var eventInfo = memberInfo as EventInfo; + if (eventInfo != null) + { + return eventInfo.EventHandlerType; + } + return null; + } + + /// <summary> + /// Gets the value contained in a given <see cref="MemberInfo"/>. Currently only <see cref="FieldInfo"/> and <see cref="PropertyInfo"/> is supported. + /// </summary> + /// <param name="member">The <see cref="MemberInfo"/> to get the value of.</param> + /// <param name="obj">The instance to get the value from.</param> + /// <returns>The value contained in the given <see cref="MemberInfo"/>.</returns> + /// <exception cref="System.ArgumentException">Can't get the value of the given <see cref="MemberInfo"/> type.</exception> + public static object GetMemberValue(this MemberInfo member, object obj) + { + if (member is FieldInfo) + { + return (member as FieldInfo).GetValue(obj); + } + else if (member is PropertyInfo) + { + return (member as PropertyInfo).GetGetMethod(true).Invoke(obj, null); + } + else + { + throw new ArgumentException("Can't get the value of a " + member.GetType().Name); + } + } + + /// <summary> + /// Sets the value of a given MemberInfo. Currently only <see cref="FieldInfo"/> and <see cref="PropertyInfo"/> is supported. + /// </summary> + /// <param name="member">The <see cref="MemberInfo"/> to set the value of.</param> + /// <param name="obj">The object to set the value on.</param> + /// <param name="value">The value to set.</param> + /// <exception cref="System.ArgumentException"> + /// Property has no setter + /// or + /// Can't set the value of the given <see cref="MemberInfo"/> type. + /// </exception> + public static void SetMemberValue(this MemberInfo member, object obj, object value) + { + if (member is FieldInfo) + { + (member as FieldInfo).SetValue(obj, value); + } + else if (member is PropertyInfo) + { + var method = (member as PropertyInfo).GetSetMethod(true); + + if (method != null) + { + method.Invoke(obj, new object[] { value }); + } + else + { + throw new ArgumentException("Property " + member.Name + " has no setter"); + } + } + else + { + throw new ArgumentException("Can't set the value of a " + member.GetType().Name); + } + } + + /// <summary> + /// Tries to infer a set of valid generic parameters for a generic type definition, given a subset of known parameters. + /// </summary> + /// <param name="genericTypeDefinition">The generic type definition to attempt to infer parameters for.</param> + /// <param name="inferredParams">The inferred parameters, if inferral was successful.</param> + /// <param name="knownParameters">The known parameters to infer from.</param> + /// <returns>True if the parameters could be inferred, otherwise, false.</returns> + /// <exception cref="System.ArgumentNullException"> + /// genericTypeDefinition is null + /// or + /// knownParameters is null + /// </exception> + /// <exception cref="System.ArgumentException">The genericTypeDefinition parameter must be a generic type definition.</exception> + public static bool TryInferGenericParameters(this Type genericTypeDefinition, out Type[] inferredParams, params Type[] knownParameters) + { + if (genericTypeDefinition == null) + { + throw new ArgumentNullException("genericTypeDefinition"); + } + + if (knownParameters == null) + { + throw new ArgumentNullException("knownParameters"); + } + + if (!genericTypeDefinition.IsGenericType) + { + throw new ArgumentException("The genericTypeDefinition parameter must be a generic type."); + } + + lock (GenericConstraintsSatisfaction_LOCK) + { + Dictionary<Type, Type> matches = GenericConstraintsSatisfactionInferredParameters; + matches.Clear(); + + Type[] definitions = genericTypeDefinition.GetGenericArguments(); + + if (!genericTypeDefinition.IsGenericTypeDefinition) + { + Type[] constructedParameters = definitions; + genericTypeDefinition = genericTypeDefinition.GetGenericTypeDefinition(); + definitions = genericTypeDefinition.GetGenericArguments(); + + int unknownCount = 0; + + for (int i = 0; i < constructedParameters.Length; i++) + { + if (!constructedParameters[i].IsGenericParameter && (!constructedParameters[i].IsGenericType || constructedParameters[i].IsFullyConstructedGenericType())) + { + matches[definitions[i]] = constructedParameters[i]; + } + else + { + unknownCount++; + } + } + + if (unknownCount == knownParameters.Length) + { + int count = 0; + + for (int i = 0; i < constructedParameters.Length; i++) + { + if (constructedParameters[i].IsGenericParameter) + { + constructedParameters[i] = knownParameters[count++]; + } + } + + if (genericTypeDefinition.AreGenericConstraintsSatisfiedBy(constructedParameters)) + { + inferredParams = constructedParameters; + return true; + } + } + } + + if (definitions.Length == knownParameters.Length && genericTypeDefinition.AreGenericConstraintsSatisfiedBy(knownParameters)) + { + inferredParams = knownParameters; + return true; + } + + foreach (var type in definitions) + { + if (matches.ContainsKey(type)) continue; + + var constraints = type.GetGenericParameterConstraints(); + + foreach (var constraint in constraints) + { + foreach (var parameter in knownParameters) + { + if (!constraint.IsGenericType) + { + continue; + } + + Type constraintDefinition = constraint.GetGenericTypeDefinition(); + + var constraintParams = constraint.GetGenericArguments(); + Type[] paramParams; + + if (parameter.IsGenericType && constraintDefinition == parameter.GetGenericTypeDefinition()) + { + paramParams = parameter.GetGenericArguments(); + } + else if (constraintDefinition.IsInterface && parameter.ImplementsOpenGenericInterface(constraintDefinition)) + { + paramParams = parameter.GetArgumentsOfInheritedOpenGenericInterface(constraintDefinition); + } + else if (constraintDefinition.IsClass && parameter.ImplementsOpenGenericClass(constraintDefinition)) + { + paramParams = parameter.GetArgumentsOfInheritedOpenGenericClass(constraintDefinition); + } + else + { + continue; + } + + matches[type] = parameter; + + for (int i = 0; i < constraintParams.Length; i++) + { + if (constraintParams[i].IsGenericParameter) + { + matches[constraintParams[i]] = paramParams[i]; + } + } + } + } + } + + if (matches.Count == definitions.Length) + { + inferredParams = new Type[matches.Count]; + + for (int i = 0; i < definitions.Length; i++) + { + inferredParams[i] = matches[definitions[i]]; + } + + if (AreGenericConstraintsSatisfiedBy(genericTypeDefinition, inferredParams)) + { + return true; + } + } + + inferredParams = null; + return false; + } + } + /// <summary> + /// <para>Checks whether an array of types satisfy the constraints of a given generic type definition.</para> + /// <para>If this method returns true, the given parameters can be safely used with <see cref="Type.MakeGenericType(Type[])"/> with the given generic type definition.</para> + /// </summary> + /// <param name="genericType">The generic type definition to check.</param> + /// <param name="parameters">The parameters to check validity for.</param> + /// <exception cref="System.ArgumentNullException"> + /// genericType is null + /// or + /// types is null + /// </exception> + /// <exception cref="System.ArgumentException">The genericType parameter must be a generic type definition.</exception> + public static bool AreGenericConstraintsSatisfiedBy(this Type genericType, params Type[] parameters) + { + if (genericType == null) + { + throw new ArgumentNullException("genericType"); + } + + if (parameters == null) + { + throw new ArgumentNullException("parameters"); + } + + if (!genericType.IsGenericType) + { + throw new ArgumentException("The genericTypeDefinition parameter must be a generic type."); + } + + return AreGenericConstraintsSatisfiedBy(genericType.GetGenericArguments(), parameters); + } + + /// <summary> + /// <para>Checks whether an array of types satisfy the constraints of a given generic method definition.</para> + /// <para>If this method returns true, the given parameters can be safely used with <see cref="MethodInfo.MakeGenericMethod(Type[])"/> with the given generic method definition.</para> + /// </summary> + /// <param name="genericType">The generic method definition to check.</param> + /// <param name="parameters">The parameters to check validity for.</param> + /// <exception cref="System.ArgumentNullException"> + /// genericType is null + /// or + /// types is null + /// </exception> + /// <exception cref="System.ArgumentException">The genericMethod parameter must be a generic method definition.</exception> + public static bool AreGenericConstraintsSatisfiedBy(this MethodBase genericMethod, params Type[] parameters) + { + if (genericMethod == null) + { + throw new ArgumentNullException("genericMethod"); + } + + if (parameters == null) + { + throw new ArgumentNullException("parameters"); + } + + if (!genericMethod.IsGenericMethod) + { + throw new ArgumentException("The genericMethod parameter must be a generic method."); + } + + return AreGenericConstraintsSatisfiedBy(genericMethod.GetGenericArguments(), parameters); + } + + public static bool AreGenericConstraintsSatisfiedBy(Type[] definitions, Type[] parameters) + { + if (definitions.Length != parameters.Length) + { + return false; + } + + lock (GenericConstraintsSatisfaction_LOCK) + { + Dictionary<Type, Type> resolvedMap = GenericConstraintsSatisfactionResolvedMap; + resolvedMap.Clear(); + + for (int i = 0; i < definitions.Length; i++) + { + Type definition = definitions[i]; + Type parameter = parameters[i]; + + if (!definition.GenericParameterIsFulfilledBy(parameter, resolvedMap)) + { + return false; + } + } + + return true; + } + } + + public static bool GenericParameterIsFulfilledBy(this Type genericParameterDefinition, Type parameterType) + { + lock (GenericConstraintsSatisfaction_LOCK) + { + GenericConstraintsSatisfactionResolvedMap.Clear(); + return genericParameterDefinition.GenericParameterIsFulfilledBy(parameterType, GenericConstraintsSatisfactionResolvedMap); + } + } + + /// <summary> + /// Before calling this method we must ALWAYS hold a lock on the GenericConstraintsSatisfaction_LOCK object, as that is an implicit assumption it works with. + /// </summary> + private static bool GenericParameterIsFulfilledBy(this Type genericParameterDefinition, Type parameterType, Dictionary<Type, Type> resolvedMap, HashSet<Type> processedParams = null) + { + if (genericParameterDefinition == null) + { + throw new ArgumentNullException("genericParameterDefinition"); + } + + if (parameterType == null) + { + throw new ArgumentNullException("parameterType"); + } + + if (resolvedMap == null) + { + throw new ArgumentNullException("resolvedMap"); + } + + if (genericParameterDefinition.IsGenericParameter == false && genericParameterDefinition == parameterType) + { + return true; + } + + if (genericParameterDefinition.IsGenericParameter == false) + { + return false; + } + + if (processedParams == null) + { + processedParams = GenericConstraintsSatisfactionProcessedParams; // This is safe because we are currently holding the lock + processedParams.Clear(); + } + + processedParams.Add(genericParameterDefinition); + + // First, check up on the special constraint flags + GenericParameterAttributes specialConstraints = genericParameterDefinition.GenericParameterAttributes; + + if (specialConstraints != GenericParameterAttributes.None) + { + // Struct constraint (must not be nullable) + if ((specialConstraints & GenericParameterAttributes.NotNullableValueTypeConstraint) == GenericParameterAttributes.NotNullableValueTypeConstraint) + { + if (!parameterType.IsValueType || (parameterType.IsGenericType && parameterType.GetGenericTypeDefinition() == typeof(Nullable<>))) + { + return false; + } + } + // Class constraint + else if ((specialConstraints & GenericParameterAttributes.ReferenceTypeConstraint) == GenericParameterAttributes.ReferenceTypeConstraint) + { + if (parameterType.IsValueType) + { + return false; + } + } + + // Must have a public parameterless constructor + if ((specialConstraints & GenericParameterAttributes.DefaultConstructorConstraint) == GenericParameterAttributes.DefaultConstructorConstraint) + { + if (parameterType.IsAbstract || (!parameterType.IsValueType && parameterType.GetConstructor(Type.EmptyTypes) == null)) + { + return false; + } + } + } + + // If this parameter has already been resolved to a type, check if that resolved type is assignable with the argument type + if (resolvedMap.ContainsKey(genericParameterDefinition)) + { + if (!parameterType.IsAssignableFrom(resolvedMap[genericParameterDefinition])) + { + return false; + } + } + + // Then, check up on the actual type constraints, of which there can be three kinds: + // Type inheritance, Interface implementation and fulfillment of another generic parameter. + Type[] constraints = genericParameterDefinition.GetGenericParameterConstraints(); + + for (int i = 0; i < constraints.Length; i++) + { + Type constraint = constraints[i]; + + // Replace resolved constraint parameters with their resolved types + if (constraint.IsGenericParameter && resolvedMap.ContainsKey(constraint)) + { + constraint = resolvedMap[constraint]; + } + + if (constraint.IsGenericParameter) + { + if (!constraint.GenericParameterIsFulfilledBy(parameterType, resolvedMap, processedParams)) + { + return false; + } + } + else if (constraint.IsClass || constraint.IsInterface || constraint.IsValueType) + { + if (constraint.IsGenericType) + { + Type constraintDefinition = constraint.GetGenericTypeDefinition(); + + Type[] constraintParams = constraint.GetGenericArguments(); + Type[] paramParams; + + if (parameterType.IsGenericType && constraintDefinition == parameterType.GetGenericTypeDefinition()) + { + paramParams = parameterType.GetGenericArguments(); + } + else + { + if (constraintDefinition.IsClass) + { + if (parameterType.ImplementsOpenGenericClass(constraintDefinition)) + { + paramParams = parameterType.GetArgumentsOfInheritedOpenGenericClass(constraintDefinition); + } + else + { + return false; + } + } + else + { + if (parameterType.ImplementsOpenGenericInterface(constraintDefinition)) + { + paramParams = parameterType.GetArgumentsOfInheritedOpenGenericInterface(constraintDefinition); + } + else + { + return false; + } + } + } + + for (int j = 0; j < constraintParams.Length; j++) + { + var c = constraintParams[j]; + var p = paramParams[j]; + + // Replace resolved constraint parameters with their resolved types + if (c.IsGenericParameter && resolvedMap.ContainsKey(c)) + { + c = resolvedMap[c]; + } + + if (c.IsGenericParameter) + { + if (!processedParams.Contains(c) && !GenericParameterIsFulfilledBy(c, p, resolvedMap, processedParams)) + { + return false; + } + } + else if (c != p && !c.IsAssignableFrom(p)) + { + return false; + } + } + } + else if (!constraint.IsAssignableFrom(parameterType)) + { + return false; + } + } + else + { + throw new Exception("Unknown parameter constraint type! " + constraint.GetNiceName()); + } + } + + resolvedMap[genericParameterDefinition] = parameterType; + return true; + } + + /// <summary> + /// Not yet documented. + /// </summary> + public static string GetGenericConstraintsString(this Type type, bool useFullTypeNames = false) + { + if (type == null) + { + throw new ArgumentNullException("type"); + } + + if (!type.IsGenericTypeDefinition) + { + throw new ArgumentException("Type '" + type.GetNiceName() + "' is not a generic type definition!"); + } + + var parameters = type.GetGenericArguments(); + var strings = new string[parameters.Length]; + + for (int i = 0; i < parameters.Length; i++) + { + strings[i] = parameters[i].GetGenericParameterConstraintsString(useFullTypeNames); + } + + return string.Join(" ", strings); + } + + /// <summary> + /// Formats a string with the specified generic parameter constraints on any given type. Example output: <c>where T : class</c> + /// </summary> + public static string GetGenericParameterConstraintsString(this Type type, bool useFullTypeNames = false) + { + if (type == null) + { + throw new ArgumentNullException("type"); + } + + if (!type.IsGenericParameter) + { + throw new ArgumentException("Type '" + type.GetNiceName() + "' is not a generic parameter!"); + } + + StringBuilder sb = new StringBuilder(); + + bool started = false; + + var specialConstraints = type.GenericParameterAttributes; + + // Struct constraint (must not be nullable) + if ((specialConstraints & GenericParameterAttributes.NotNullableValueTypeConstraint) == GenericParameterAttributes.NotNullableValueTypeConstraint) + { + sb.Append("where ") + .Append(type.Name) + .Append(" : struct"); + + started = true; + } + // Class constraint + else if ((specialConstraints & GenericParameterAttributes.ReferenceTypeConstraint) == GenericParameterAttributes.ReferenceTypeConstraint) + { + sb.Append("where ") + .Append(type.Name) + .Append(" : class"); + + started = true; + } + + // Must have a public parameterless constructor + if ((specialConstraints & GenericParameterAttributes.DefaultConstructorConstraint) == GenericParameterAttributes.DefaultConstructorConstraint) + { + if (started) + { + sb.Append(", new()"); + } + else + { + sb.Append("where ") + .Append(type.Name) + .Append(" : new()"); + + started = true; + } + } + + // Then add type constraints + var constraints = type.GetGenericParameterConstraints(); + + if (constraints.Length > 0) + { + for (int j = 0; j < constraints.Length; j++) + { + var constraint = constraints[j]; + + if (started) + { + sb.Append(", "); + + if (useFullTypeNames) + sb.Append(constraint.GetNiceFullName()); + else + sb.Append(constraint.GetNiceName()); + } + else + { + sb.Append("where ") + .Append(type.Name) + .Append(" : "); + + if (useFullTypeNames) + sb.Append(constraint.GetNiceFullName()); + else + sb.Append(constraint.GetNiceName()); + + started = true; + } + } + } + + return sb.ToString(); + } + + /// <summary> + /// Determines whether a generic type contains the specified generic argument constraints. + /// </summary> + /// <param name="type">The type.</param> + /// <param name="types">The generic argument types.</param> + public static bool GenericArgumentsContainsTypes(this Type type, params Type[] types) + { + if (type == null) + { + throw new ArgumentNullException("type"); + } + + if (type.IsGenericType == false) + { + return false; + } + + bool[] typesSeen = new bool[types.Length]; + var args = type.GetGenericArguments(); + + var argsToCheck = GenericArgumentsContainsTypes_ArgsToCheckCached; + + lock (argsToCheck) + { + argsToCheck.Clear(); + + for (int i = 0; i < args.Length; i++) + { + argsToCheck.Push(args[i]); + } + + while (argsToCheck.Count > 0) + { + var arg = argsToCheck.Pop(); + + // Check if it's one of the types we're looking for, and if so, mark that as seen + for (int i = 0; i < types.Length; i++) + { + Type lookingForType = types[i]; + + if (lookingForType == arg) + { + typesSeen[i] = true; + } + else if (lookingForType.IsGenericTypeDefinition && arg.IsGenericType && !arg.IsGenericTypeDefinition && arg.GetGenericTypeDefinition() == lookingForType) + { + typesSeen[i] = true; + } + } + + // Check if all types we're looking for have been seen + { + bool allSeen = true; + + for (int i = 0; i < typesSeen.Length; i++) + { + if (typesSeen[i] == false) + { + allSeen = false; + break; + } + } + + if (allSeen) + { + return true; + } + } + + // If argument is a generic type, we have to also check its arguments + if (arg.IsGenericType) + { + foreach (var innerArg in arg.GetGenericArguments()) + { + argsToCheck.Push(innerArg); + } + } + } + } + + return false; + } + + /// <summary> + /// Determines whether a type is a fully constructed generic type. + /// </summary> + public static bool IsFullyConstructedGenericType(this Type type) + { + if (type == null) + { + throw new ArgumentNullException("type"); + } + + if (type.IsGenericTypeDefinition) + { + return false; + } + + if (type.HasElementType) + { + var element = type.GetElementType(); + if (element.IsGenericParameter || element.IsFullyConstructedGenericType() == false) + { + return false; + } + } + + var args = type.GetGenericArguments(); + + for (int i = 0; i < args.Length; i++) + { + var arg = args[i]; + + if (arg.IsGenericParameter) + { + return false; + } + else if (!arg.IsFullyConstructedGenericType()) + { + return false; + } + } + + return !type.IsGenericTypeDefinition; + + //if (type.IsGenericType == false || type.IsGenericTypeDefinition) + //{ + // return false; + //} + + //var args = type.GetGenericArguments(); + + //for (int i = 0; i < args.Length; i++) + //{ + // var arg = args[i]; + + // if (arg.IsGenericParameter) + // { + // return false; + // } + // else if (arg.IsGenericType && !arg.IsFullyConstructedGenericType()) + // { + // return false; + // } + //} + + //return true; + } + + /// <summary> + /// Determines whether a type is nullable by ensuring the type is neither a PrimitiveType, ValueType or an Enum. + /// </summary> + public static bool IsNullableType(this Type type) + { + return !(type.IsPrimitive || type.IsValueType || type.IsEnum); + } + + /// <summary> + /// Gets the enum bitmask in a ulong. + /// </summary> + /// <exception cref="System.ArgumentException">enumType</exception> + public static ulong GetEnumBitmask(object value, Type enumType) + { + if (!enumType.IsEnum) + { + throw new ArgumentException("enumType"); + } + + ulong selectedValue; + + try + { + selectedValue = Convert.ToUInt64(value, CultureInfo.InvariantCulture); + } + catch (OverflowException) + { + unchecked + { + selectedValue = (ulong)Convert.ToInt64(value, CultureInfo.InvariantCulture); + } + } + + return selectedValue; + } + + public static Type[] SafeGetTypes(this Assembly assembly) + { + try + { + return assembly.GetTypes(); + } + catch + { + return Type.EmptyTypes; + } + } + + public static bool SafeIsDefined(this Assembly assembly, Type attribute, bool inherit) + { + try + { + return assembly.IsDefined(attribute, inherit); + } + catch + { + return false; + } + } + + public static object[] SafeGetCustomAttributes(this Assembly assembly, Type type, bool inherit) + { + try + { + return assembly.GetCustomAttributes(type, inherit); + } + catch + { + return new object[0]; + } + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/TypeExtensions.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/TypeExtensions.cs.meta new file mode 100644 index 00000000..b7222210 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/TypeExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a6a172cef14a88c7fb714df37bbecedb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/UnityExtensions.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/UnityExtensions.cs new file mode 100644 index 00000000..bfca1b53 --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/UnityExtensions.cs @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------- +// <copyright file="UnityExtensions.cs" company="Sirenix IVS"> +// Copyright (c) 2018 Sirenix IVS +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// </copyright> +//----------------------------------------------------------------------- + +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.Reflection; + + /// <summary> + /// Extends various Unity classes. + /// </summary> + public static class UnityExtensions + { + private static readonly ValueGetter<UnityEngine.Object, IntPtr> UnityObjectCachedPtrFieldGetter; + + static UnityExtensions() + { + var field = typeof(UnityEngine.Object).GetField("m_CachedPtr", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + + if (field != null) + { + UnityObjectCachedPtrFieldGetter = EmitUtilities.CreateInstanceFieldGetter<UnityEngine.Object, IntPtr>(field); + } + } + + /// <summary> + /// Determines whether a Unity object is null or "fake null", + /// without ever calling Unity's own equality operators. + /// This method is useful for checking if a Unity object is + /// null, destroyed or missing at times when it is not allowed + /// to call Unity's own equality operators, for example when + /// not running on the main thread. + /// </summary> + /// <param name="obj">The Unity object to check.</param> + /// <returns>True if the object is null, missing or destroyed; otherwise false.</returns> + public static bool SafeIsUnityNull(this UnityEngine.Object obj) + { + if (object.ReferenceEquals(obj, null)) + { + return true; + } + + if (UnityObjectCachedPtrFieldGetter == null) + { + throw new NotSupportedException("Could not find the field 'm_CachedPtr' in the class UnityEngine.Object; cannot perform a special null check."); + } + + IntPtr ptr = UnityObjectCachedPtrFieldGetter(ref obj); + return ptr == IntPtr.Zero; + } + } +}
\ No newline at end of file diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/UnityExtensions.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/UnityExtensions.cs.meta new file mode 100644 index 00000000..ad7fea6b --- /dev/null +++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/UnityExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eb77f5278e425e91b71e186df29a5f16 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: |