summaryrefslogtreecommitdiff
path: root/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities
diff options
context:
space:
mode:
Diffstat (limited to 'VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities')
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions.meta8
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/FieldInfoExtensions.cs70
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/FieldInfoExtensions.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/GarbageFreeIterators.cs330
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/GarbageFreeIterators.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/LinqExtensions.cs79
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/LinqExtensions.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/MemberInfoExtensions.cs254
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/MemberInfoExtensions.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/MethodInfoExtensions.cs139
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/MethodInfoExtensions.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/Operator.cs126
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/Operator.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/PathUtilities.cs53
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/PathUtilities.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/PropertyInfoExtensions.cs106
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/PropertyInfoExtensions.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/StringExtensions.cs80
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/StringExtensions.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/TypeExtensions.cs2368
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/TypeExtensions.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/UnityExtensions.cs67
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions/UnityExtensions.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc.meta8
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs289
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Cache.cs254
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Cache.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/DoubleLookupDictionary.cs181
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/DoubleLookupDictionary.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/EmitUtilities.cs1772
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/EmitUtilities.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/FastTypeComparer.cs44
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/FastTypeComparer.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Flags.cs108
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Flags.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ICacheNotificationReceiver.cs36
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ICacheNotificationReceiver.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ImmutableList.cs605
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ImmutableList.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasFieldInfo.cs179
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasFieldInfo.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasMethodInfo.cs194
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasMethodInfo.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasPropertyInfo.cs237
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasPropertyInfo.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ReferenceEqualityComparer.cs57
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ReferenceEqualityComparer.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnityVersion.cs81
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnityVersion.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnsafeUtilities.cs408
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnsafeUtilities.cs.meta11
52 files changed, 8408 insertions, 0 deletions
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions.meta
new file mode 100644
index 00000000..a6221dea
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Extensions.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 56c5d7ad3a7da184bb15ac88016146d8
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
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 &lt; operator.
+ /// </summary>
+ LessThan,
+
+ /// <summary>
+ /// The &gt; operator.
+ /// </summary>
+ GreaterThan,
+
+ /// <summary>
+ /// The &lt;= operator.
+ /// </summary>
+ LessThanOrEqual,
+
+ /// <summary>
+ /// The &gt;= operator.
+ /// </summary>
+ GreaterThanOrEqual,
+
+ /// <summary>
+ /// The % operator.
+ /// </summary>
+ Modulus,
+
+ /// <summary>
+ /// The &gt;&gt; operator.
+ /// </summary>
+ RightShift,
+
+ /// <summary>
+ /// The &lt;&lt; operator.
+ /// </summary>
+ LeftShift,
+
+ /// <summary>
+ /// The &amp; operator.
+ /// </summary>
+ BitwiseAnd,
+
+ /// <summary>
+ /// The | operator.
+ /// </summary>
+ BitwiseOr,
+
+ /// <summary>
+ /// The ^ operator.
+ /// </summary>
+ ExclusiveOr,
+
+ /// <summary>
+ /// The ~ operator.
+ /// </summary>
+ BitwiseComplement,
+
+ /// <summary>
+ /// The &amp;&amp; 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&lt;&gt; or List&lt;&gt;.
+ /// </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&lt;&gt;.
+ /// </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&lt;&gt;.
+ /// </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&lt;&gt;.
+ /// </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&lt;&gt;.
+ /// </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:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc.meta
new file mode 100644
index 00000000..983f77cf
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 9633fa76046a40746ad43ef055712a32
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs
new file mode 100644
index 00000000..08a22475
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs
@@ -0,0 +1,289 @@
+//-----------------------------------------------------------------------
+// <copyright file="AssemblyImportSettingsUtilities.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>
+//-----------------------------------------------------------------------
+
+#if UNITY_EDITOR
+
+namespace VRC.Udon.Serialization.OdinSerializer.Utilities.Editor
+{
+ using System;
+ using System.IO;
+ using System.Linq;
+ using System.Reflection;
+ using UnityEditor;
+
+ /// <summary>
+ /// Defines how an assembly's import settings should be configured.
+ /// </summary>
+ public enum OdinAssemblyImportSettings
+ {
+ /// <summary>
+ /// Include the assembly in the build, but not in the editor.
+ /// </summary>
+ IncludeInBuildOnly,
+ /// <summary>
+ /// Include the assembly in the editor, but not in the build.
+ /// </summary>
+ IncludeInEditorOnly,
+ /// <summary>
+ /// Include the assembly in both the build and in the editor.
+ /// </summary>
+ IncludeInAll,
+ /// <summary>
+ /// Exclude the assembly from both the build and from the editor.
+ /// </summary>
+ ExcludeFromAll,
+ }
+
+ /// <summary>
+ /// Utility for correctly setting import on OdinSerializer assemblies based on platform and scripting backend.
+ /// </summary>
+ public static class AssemblyImportSettingsUtilities
+ {
+ private static MethodInfo getPropertyIntMethod;
+ private static MethodInfo getScriptingBackendMethod;
+ private static MethodInfo getApiCompatibilityLevelMethod;
+ private static MethodInfo apiCompatibilityLevelProperty;
+
+ /// <summary>
+ /// All valid Unity BuildTarget platforms.
+ /// </summary>
+ public static readonly ImmutableList<BuildTarget> Platforms;
+
+ /// <summary>
+ /// All valid Unity BuildTarget platforms that support Just In Time compilation.
+ /// </summary>
+ public static readonly ImmutableList<BuildTarget> JITPlatforms;
+
+ /// <summary>
+ /// All scripting backends that support JIT.
+ /// </summary>
+ public static readonly ImmutableList<ScriptingImplementation> JITScriptingBackends;
+
+ /// <summary>
+ /// All API compatibility levels that support JIT.
+ /// </summary>
+ public static readonly ImmutableList<ApiCompatibilityLevel> JITApiCompatibilityLevels;
+
+ static AssemblyImportSettingsUtilities()
+ {
+ // Different methods required for getting the current scripting backend from different versions of the Unity Editor.
+ getPropertyIntMethod = typeof(PlayerSettings).GetMethod("GetPropertyInt", Flags.StaticPublic, null, new Type[] { typeof(string), typeof(BuildTargetGroup) }, null);
+ getScriptingBackendMethod = typeof(PlayerSettings).GetMethod("GetScriptingBackend", Flags.StaticPublic, null, new Type[] { typeof(BuildTargetGroup) }, null);
+
+ // Diffferent methods required for getting the current api level from different versions of the Unity Editor.
+ getApiCompatibilityLevelMethod = typeof(PlayerSettings).GetMethod("GetApiCompatibilityLevel", Flags.StaticPublic, null, new Type[] { typeof(BuildTargetGroup) }, null);
+ var apiLevelProperty = typeof(PlayerSettings).GetProperty("apiCompatibilityLevel", Flags.StaticPublic);
+ apiCompatibilityLevelProperty = apiLevelProperty != null ? apiLevelProperty.GetGetMethod() : null;
+
+ // All valid BuildTarget values.
+ Platforms = new ImmutableList<BuildTarget>(Enum.GetValues(typeof(BuildTarget))
+ .Cast<BuildTarget>()
+ .Where(t => t >= 0 && typeof(BuildTarget).GetMember(t.ToString())[0].IsDefined(typeof(ObsoleteAttribute), false) == false)
+ .ToArray());
+
+ // All BuildTarget values that support JIT.
+ JITPlatforms = new ImmutableList<BuildTarget>(Platforms
+ .Where(i => i.ToString().StartsWith("StandaloneOSX")) // Unity 2017.3 replaced StandaloneOSXIntel, StandaloneOSXIntel64 and StandaloneOSXUniversal with StandaloneOSX.
+ .Append(new BuildTarget[]
+ {
+ BuildTarget.StandaloneWindows,
+ BuildTarget.StandaloneWindows64,
+ BuildTarget.StandaloneLinux,
+ BuildTarget.StandaloneLinux64,
+ BuildTarget.StandaloneLinuxUniversal,
+ BuildTarget.Android
+ })
+ .ToArray());
+
+ // All scripting backends that support JIT.
+ JITScriptingBackends = new ImmutableList<ScriptingImplementation>(new ScriptingImplementation[]
+ {
+ ScriptingImplementation.Mono2x,
+ });
+
+ // Names of all api levels that support JIT.
+ string[] jitApiNames = new string[]
+ {
+ "NET_2_0",
+ "NET_2_0_Subset",
+ "NET_4_6",
+ "NET_Web", // TODO: Does NET_Web support JIT stuff?
+ "NET_Micro" // TODO: Does NET_Micro support JIT stuff?
+ };
+
+ var apiLevelNames = Enum.GetNames(typeof(ApiCompatibilityLevel));
+
+ JITApiCompatibilityLevels = new ImmutableList<ApiCompatibilityLevel>(jitApiNames
+ .Where(x => apiLevelNames.Contains(x))
+ .Select(x => (ApiCompatibilityLevel)Enum.Parse(typeof(ApiCompatibilityLevel), x))
+ .ToArray());
+ }
+
+ /// <summary>
+ /// Set the import settings on the assembly.
+ /// </summary>
+ /// <param name="assemblyFilePath">The path to the assembly to configure import settings from.</param>
+ /// <param name="importSettings">The import settings to configure for the assembly at the path.</param>
+ public static void SetAssemblyImportSettings(BuildTarget platform, string assemblyFilePath, OdinAssemblyImportSettings importSettings)
+ {
+ bool includeInBuild = false;
+ bool includeInEditor = false;
+
+ switch (importSettings)
+ {
+ case OdinAssemblyImportSettings.IncludeInAll:
+ includeInBuild = true;
+ includeInEditor = true;
+ break;
+
+ case OdinAssemblyImportSettings.IncludeInBuildOnly:
+ includeInBuild = true;
+ break;
+
+ case OdinAssemblyImportSettings.IncludeInEditorOnly:
+ includeInEditor = true;
+ break;
+
+ case OdinAssemblyImportSettings.ExcludeFromAll:
+ break;
+ }
+
+ SetAssemblyImportSettings(platform, assemblyFilePath, includeInBuild, includeInEditor);
+ }
+
+ /// <summary>
+ /// Set the import settings on the assembly.
+ /// </summary>
+ /// <param name="assemblyFilePath">The path to the assembly to configure import settings from.</param>
+ /// <param name="includeInBuild">Indicates if the assembly should be included in the build.</param>
+ /// <param name="includeInEditor">Indicates if the assembly should be included in the Unity editor.</param>
+ public static void SetAssemblyImportSettings(BuildTarget platform, string assemblyFilePath, bool includeInBuild, bool includeInEditor)
+ {
+ if (File.Exists(assemblyFilePath) == false)
+ {
+ throw new FileNotFoundException(assemblyFilePath);
+ }
+
+ var importer = (PluginImporter)AssetImporter.GetAtPath(assemblyFilePath);
+ if (importer == null)
+ {
+ throw new InvalidOperationException("Failed to get PluginImporter for " + assemblyFilePath);
+ }
+
+ bool updateImportSettings =
+ importer.GetCompatibleWithAnyPlatform() // If the 'any platform' flag is true, then reapply settings no matter what to ensure that everything is correct.
+ //|| Platforms.Any(p => importer.GetCompatibleWithPlatform(p) != includeInBuild)
+ || importer.GetCompatibleWithPlatform(platform) != includeInBuild
+ || importer.GetCompatibleWithEditor() != includeInEditor;
+
+ // Apply new import settings if necessary.
+ if (updateImportSettings)
+ {
+ importer.SetCompatibleWithAnyPlatform(false);
+ //Platforms.ForEach(p => importer.SetCompatibleWithPlatform(p, includeInBuild));
+ importer.SetCompatibleWithPlatform(platform, includeInBuild);
+ importer.SetCompatibleWithEditor(includeInEditor);
+
+ importer.SaveAndReimport();
+ }
+ }
+
+ /// <summary>
+ /// Gets the current scripting backend for the build from the Unity editor. This method is Unity version independent.
+ /// </summary>
+ /// <returns></returns>
+ public static ScriptingImplementation GetCurrentScriptingBackend()
+ {
+ var buildGroup = EditorUserBuildSettings.selectedBuildTargetGroup;
+
+ if (getScriptingBackendMethod != null)
+ {
+ return (ScriptingImplementation)getScriptingBackendMethod.Invoke(null, new object[] { buildGroup });
+ }
+ else if (getPropertyIntMethod != null)
+ {
+ return (ScriptingImplementation)getPropertyIntMethod.Invoke(null, new object[] { "ScriptingBackend", buildGroup });
+ }
+
+ throw new InvalidOperationException("Was unable to get the current scripting backend!");
+ }
+
+ /// <summary>
+ /// Gets the current API compatibility level from the Unity Editor. This method is Unity version independent.
+ /// </summary>
+ /// <returns></returns>
+ public static ApiCompatibilityLevel GetCurrentApiCompatibilityLevel()
+ {
+ if (getApiCompatibilityLevelMethod != null)
+ {
+ var buildGroup = EditorUserBuildSettings.selectedBuildTargetGroup;
+ return (ApiCompatibilityLevel)getApiCompatibilityLevelMethod.Invoke(null, new object[] { buildGroup });
+ }
+ else if (apiCompatibilityLevelProperty != null)
+ {
+ return (ApiCompatibilityLevel)apiCompatibilityLevelProperty.Invoke(null, null);
+ }
+
+ throw new InvalidOperationException("Was unable to get the current api compatibility level!");
+ }
+
+ /// <summary>
+ /// Gets a value that indicates if the specified platform supports JIT.
+ /// </summary>
+ /// <param name="platform">The platform to test.</param>
+ /// <returns><c>true</c> if the platform supports JIT; otherwise <c>false</c>.</returns>
+ public static bool PlatformSupportsJIT(BuildTarget platform)
+ {
+ return JITPlatforms.Contains(platform);
+ }
+
+ /// <summary>
+ /// Gets a value that indicates if the specified scripting backend supports JIT.
+ /// </summary>
+ /// <param name="backend">The backend to test.</param>
+ /// <returns><c>true</c> if the backend supports JIT; otherwise <c>false</c>.</returns>
+ public static bool ScriptingBackendSupportsJIT(ScriptingImplementation backend)
+ {
+ return JITScriptingBackends.Contains(backend);
+ }
+
+ /// <summary>
+ /// Gets a value that indicates if the specified api level supports JIT.
+ /// </summary>
+ /// <param name="apiLevel">The api level to test.</param>
+ /// <returns><c>true</c> if the api level supports JIT; otherwise <c>false</c>.</returns>
+ public static bool ApiCompatibilityLevelSupportsJIT(ApiCompatibilityLevel apiLevel)
+ {
+ return JITApiCompatibilityLevels.Contains(apiLevel);
+ }
+
+ /// <summary>
+ /// Gets a value that indicates if the specified build settings supports JIT.
+ /// </summary>
+ /// <param name="platform">The platform build setting.</param>
+ /// <param name="backend">The scripting backend build settting.</param>
+ /// <param name="apiLevel">The api level build setting.</param>
+ /// <returns><c>true</c> if the build settings supports JIT; otherwise <c>false</c>.</returns>
+ public static bool IsJITSupported(BuildTarget platform, ScriptingImplementation backend, ApiCompatibilityLevel apiLevel)
+ {
+ return PlatformSupportsJIT(platform) && ScriptingBackendSupportsJIT(backend) && ApiCompatibilityLevelSupportsJIT(apiLevel);
+ }
+ }
+}
+
+#endif \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs.meta
new file mode 100644
index 00000000..623f5053
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 787c97af872124f748a4a9b366f325d3
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Cache.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Cache.cs
new file mode 100644
index 00000000..f99535a4
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Cache.cs
@@ -0,0 +1,254 @@
+//-----------------------------------------------------------------------
+// <copyright file="Cache.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.Threading;
+
+ public interface ICache : IDisposable
+ {
+ object Value { get; }
+ }
+
+ /// <summary>
+ /// Provides an easy way of claiming and freeing cached values of any non-abstract reference type with a public parameterless constructor.
+ /// <para />
+ /// Cached types which implement the <see cref="ICacheNotificationReceiver"/> interface will receive notifications when they are claimed and freed.
+ /// <para />
+ /// Only one thread should be holding a given cache instance at a time if <see cref="ICacheNotificationReceiver"/> is implemented, since the invocation of
+ /// <see cref="ICacheNotificationReceiver.OnFreed()"/> is not thread safe, IE, weird stuff might happen if multiple different threads are trying to free
+ /// the same cache instance at the same time. This will practically never happen unless you're doing really strange stuff, but the case is documented here.
+ /// </summary>
+ /// <typeparam name="T">The type which is cached.</typeparam>
+ /// <seealso cref="System.IDisposable" />
+ public sealed class Cache<T> : ICache where T : class, new()
+ {
+ private static readonly bool IsNotificationReceiver = typeof(ICacheNotificationReceiver).IsAssignableFrom(typeof(T));
+ private static object[] FreeValues = new object[4];
+
+ private bool isFree;
+
+ private static volatile int THREAD_LOCK_TOKEN = 0;
+
+ private static int maxCacheSize = 5;
+
+ /// <summary>
+ /// Gets or sets the maximum size of the cache. This value can never go beneath 1.
+ /// </summary>
+ /// <value>
+ /// The maximum size of the cache.
+ /// </value>
+ public static int MaxCacheSize
+ {
+ get
+ {
+ return Cache<T>.maxCacheSize;
+ }
+
+ set
+ {
+ Cache<T>.maxCacheSize = Math.Max(1, value);
+ }
+ }
+
+ private Cache()
+ {
+ this.Value = new T();
+ this.isFree = false;
+ }
+
+ /// <summary>
+ /// The cached value.
+ /// </summary>
+ public T Value;
+
+ /// <summary>
+ /// Gets a value indicating whether this cached value is free.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this cached value is free; otherwise, <c>false</c>.
+ /// </value>
+ public bool IsFree { get { return this.isFree; } }
+
+ object ICache.Value { get { return this.Value; } }
+
+ /// <summary>
+ /// Claims a cached value of type <see cref="T"/>.
+ /// </summary>
+ /// <returns>A cached value of type <see cref="T"/>.</returns>
+ public static Cache<T> Claim()
+ {
+ Cache<T> result = null;
+
+ // Very, very simple spinlock implementation
+ // this lock will almost never be contested
+ // and it will never be held for more than
+ // an instant; therefore, we want to avoid paying
+ // the lock(object) statement's semaphore
+ // overhead.
+ while (true)
+ {
+ if (Interlocked.CompareExchange(ref THREAD_LOCK_TOKEN, 1, 0) == 0)
+ {
+ break;
+ }
+ }
+
+ // We now hold the lock
+ var freeValues = FreeValues;
+ var length = freeValues.Length;
+
+ for (int i = 0; i < length; i++)
+ {
+ result = (Cache<T>)freeValues[i];
+ if (!object.ReferenceEquals(result, null))
+ {
+ freeValues[i] = null;
+ result.isFree = false;
+ break;
+ }
+ }
+
+ // Release the lock
+ THREAD_LOCK_TOKEN = 0;
+
+ if (result == null)
+ {
+ result = new Cache<T>();
+ }
+
+ if (IsNotificationReceiver)
+ {
+ (result.Value as ICacheNotificationReceiver).OnClaimed();
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// Releases a cached value.
+ /// </summary>
+ /// <param name="cache">The cached value to release.</param>
+ /// <exception cref="System.ArgumentNullException">The cached value to release is null.</exception>
+ public static void Release(Cache<T> cache)
+ {
+ if (cache == null)
+ {
+ throw new ArgumentNullException("cache");
+ }
+
+ if (cache.isFree) return;
+
+ // No need to call this method inside the lock, which might do heavy work
+ // there is a thread safety hole here, actually - if several different threads
+ // are trying to free the same cache instance, OnFreed might be called several
+ // times concurrently for the same cached value.
+ if (IsNotificationReceiver)
+ {
+ (cache.Value as ICacheNotificationReceiver).OnFreed();
+ }
+
+ while (true)
+ {
+ if (Interlocked.CompareExchange(ref THREAD_LOCK_TOKEN, 1, 0) == 0)
+ {
+ break;
+ }
+ }
+
+ // We now hold the lock
+
+ if (cache.isFree)
+ {
+ // Release the lock and leave - job's done already
+ THREAD_LOCK_TOKEN = 0;
+ return;
+ }
+
+
+ cache.isFree = true;
+
+ var freeValues = FreeValues;
+ var length = freeValues.Length;
+
+ bool added = false;
+
+ for (int i = 0; i < length; i++)
+ {
+ if (object.ReferenceEquals(freeValues[i], null))
+ {
+ freeValues[i] = cache;
+ added = true;
+ break;
+ }
+ }
+
+ if (!added && length < MaxCacheSize)
+ {
+ var newArr = new object[length * 2];
+
+ for (int i = 0; i < length; i++)
+ {
+ newArr[i] = freeValues[i];
+ }
+
+ newArr[length] = cache;
+
+ FreeValues = newArr;
+ }
+
+ // Release the lock
+ THREAD_LOCK_TOKEN = 0;
+
+ }
+
+ /// <summary>
+ /// Performs an implicit conversion from <see cref="Cache{T}"/> to <see cref="T"/>.
+ /// </summary>
+ /// <param name="cache">The cache to convert.</param>
+ /// <returns>
+ /// The result of the conversion.
+ /// </returns>
+ public static implicit operator T(Cache<T> cache)
+ {
+ if (cache == null)
+ {
+ return default(T);
+ }
+
+ return cache.Value;
+ }
+
+ /// <summary>
+ /// Releases this cached value.
+ /// </summary>
+ public void Release()
+ {
+ Release(this);
+ }
+
+ /// <summary>
+ /// Releases this cached value.
+ /// </summary>
+ void IDisposable.Dispose()
+ {
+ Cache<T>.Release(this);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Cache.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Cache.cs.meta
new file mode 100644
index 00000000..bffcf76b
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Cache.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 146b6bd1e3b0f0926205abf839ec9e6f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/DoubleLookupDictionary.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/DoubleLookupDictionary.cs
new file mode 100644
index 00000000..6706be1d
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/DoubleLookupDictionary.cs
@@ -0,0 +1,181 @@
+//-----------------------------------------------------------------------
+// <copyright file="DoubleLookupDictionary.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>
+ /// Not yet documented.
+ /// </summary>
+ [Serializable]
+ public class DoubleLookupDictionary<TFirstKey, TSecondKey, TValue> : Dictionary<TFirstKey, Dictionary<TSecondKey, TValue>>
+ {
+ private readonly IEqualityComparer<TSecondKey> secondKeyComparer;
+
+ public DoubleLookupDictionary()
+ {
+ this.secondKeyComparer = EqualityComparer<TSecondKey>.Default;
+ }
+
+ public DoubleLookupDictionary(IEqualityComparer<TFirstKey> firstKeyComparer, IEqualityComparer<TSecondKey> secondKeyComparer)
+ : base(firstKeyComparer)
+ {
+ this.secondKeyComparer = secondKeyComparer;
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public new Dictionary<TSecondKey, TValue> this[TFirstKey firstKey]
+ {
+ get
+ {
+ Dictionary<TSecondKey, TValue> innerDict;
+
+ if (!this.TryGetValue(firstKey, out innerDict))
+ {
+ innerDict = new Dictionary<TSecondKey, TValue>(this.secondKeyComparer);
+ this.Add(firstKey, innerDict);
+ }
+
+ return innerDict;
+ }
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public int InnerCount(TFirstKey firstKey)
+ {
+ Dictionary<TSecondKey, TValue> innerDict;
+
+ if (this.TryGetValue(firstKey, out innerDict))
+ {
+ return innerDict.Count;
+ }
+
+ return 0;
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public int TotalInnerCount()
+ {
+ int count = 0;
+
+ if (this.Count > 0)
+ {
+ foreach (var innerDict in this.Values)
+ {
+ count += innerDict.Count;
+ }
+ }
+
+ return count;
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public bool ContainsKeys(TFirstKey firstKey, TSecondKey secondKey)
+ {
+ Dictionary<TSecondKey, TValue> innerDict;
+
+ return this.TryGetValue(firstKey, out innerDict) && innerDict.ContainsKey(secondKey);
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public bool TryGetInnerValue(TFirstKey firstKey, TSecondKey secondKey, out TValue value)
+ {
+ Dictionary<TSecondKey, TValue> innerDict;
+
+ if (this.TryGetValue(firstKey, out innerDict) && innerDict.TryGetValue(secondKey, out value))
+ {
+ return true;
+ }
+
+ value = default(TValue);
+ return false;
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public TValue AddInner(TFirstKey firstKey, TSecondKey secondKey, TValue value)
+ {
+ if (this.ContainsKeys(firstKey, secondKey))
+ {
+ throw new ArgumentException("An element with the same keys already exists in the " + this.GetType().GetNiceName() + ".");
+ }
+
+ return this[firstKey][secondKey] = value;
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public bool RemoveInner(TFirstKey firstKey, TSecondKey secondKey)
+ {
+ Dictionary<TSecondKey, TValue> innerDict;
+
+ if (this.TryGetValue(firstKey, out innerDict))
+ {
+ bool removed = innerDict.Remove(secondKey);
+
+ if (innerDict.Count == 0)
+ {
+ this.Remove(firstKey);
+ }
+
+ return removed;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public void RemoveWhere(Func<TValue, bool> predicate)
+ {
+ List<TFirstKey> toRemoveBufferFirstKey = new List<TFirstKey>();
+ List<TSecondKey> toRemoveBufferSecondKey = new List<TSecondKey>();
+
+ foreach (var outerDictionary in this.GFIterator())
+ {
+ foreach (var innerKeyPair in outerDictionary.Value.GFIterator())
+ {
+ if (predicate(innerKeyPair.Value))
+ {
+ toRemoveBufferFirstKey.Add(outerDictionary.Key);
+ toRemoveBufferSecondKey.Add(innerKeyPair.Key);
+ }
+ }
+ }
+
+ for (int i = 0; i < toRemoveBufferFirstKey.Count; i++)
+ {
+ this.RemoveInner(toRemoveBufferFirstKey[i], toRemoveBufferSecondKey[i]);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/DoubleLookupDictionary.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/DoubleLookupDictionary.cs.meta
new file mode 100644
index 00000000..b2fe8e13
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/DoubleLookupDictionary.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1bd625694c606aab0cb7895da4911c6a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/EmitUtilities.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/EmitUtilities.cs
new file mode 100644
index 00000000..8ddc781b
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/EmitUtilities.cs
@@ -0,0 +1,1772 @@
+//-----------------------------------------------------------------------
+// <copyright file="EmitUtilities.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>
+//-----------------------------------------------------------------------
+
+// #if NET_STANDARD_2_0
+// #error Odin Inspector is incapable of compiling source code against the .NET Standard 2.0 API surface. You can change the API Compatibility Level in the Player settings.
+// #endif
+
+#if (UNITY_EDITOR || UNITY_STANDALONE) && !ENABLE_IL2CPP && NET_4_6
+#define CAN_EMIT
+#endif
+
+namespace VRC.Udon.Serialization.OdinSerializer.Utilities
+{
+ using System;
+ using System.Reflection;
+
+#if CAN_EMIT
+
+ using System.Reflection.Emit;
+
+#endif
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public delegate object WeakValueGetter(ref object instance);
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public delegate void WeakValueSetter(ref object instance, object value);
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public delegate FieldType WeakValueGetter<FieldType>(ref object instance);
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public delegate void WeakValueSetter<FieldType>(ref object instance, FieldType value);
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public delegate FieldType ValueGetter<InstanceType, FieldType>(ref InstanceType instance);
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public delegate void ValueSetter<InstanceType, FieldType>(ref InstanceType instance, FieldType value);
+
+ /// <summary>
+ /// Provides utilities for using the <see cref="System.Reflection.Emit"/> namespace.
+ /// <para />
+ /// This class is due for refactoring. Use at your own peril.
+ /// </summary>
+ public static class EmitUtilities
+ {
+ /// <summary>
+ /// Gets a value indicating whether emitting is supported on the current platform.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if the current platform can emit; otherwise, <c>false</c>.
+ /// </value>
+ public static bool CanEmit
+ {
+ get
+ {
+#if CAN_EMIT
+ return true;
+#else
+ return false;
+#endif
+ }
+ }
+
+ /// <summary>
+ /// Creates a delegate which gets the value of a field. If emitting is not supported on the current platform, the delegate will use reflection to get the value.
+ /// </summary>
+ /// <typeparam name="FieldType">The type of the field to get a value from.</typeparam>
+ /// <param name="fieldInfo">The <see cref="FieldInfo"/> instance describing the field to create a getter for.</param>
+ /// <returns>A delegate which gets the value of the given field.</returns>
+ /// <exception cref="System.ArgumentNullException">The fieldInfo parameter is null.</exception>
+ public static Func<FieldType> CreateStaticFieldGetter<FieldType>(FieldInfo fieldInfo)
+ {
+ if (fieldInfo == null)
+ {
+ throw new ArgumentNullException("fieldInfo");
+ }
+
+ if (!fieldInfo.IsStatic)
+ {
+ throw new ArgumentException("Field must be static.");
+ }
+
+ fieldInfo = fieldInfo.DeAliasField();
+
+ if (fieldInfo.IsLiteral)
+ {
+ FieldType value = (FieldType)fieldInfo.GetValue(null);
+ return () => value;
+ }
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return delegate ()
+ {
+ return (FieldType)fieldInfo.GetValue(null);
+ };
+#else
+ string methodName = fieldInfo.ReflectedType.FullName + ".get_" + fieldInfo.Name;
+
+ DynamicMethod getterMethod = new DynamicMethod(methodName, typeof(FieldType), new Type[0], true);
+ ILGenerator gen = getterMethod.GetILGenerator();
+
+ gen.Emit(OpCodes.Ldsfld, fieldInfo);
+ gen.Emit(OpCodes.Ret);
+
+ return (Func<FieldType>)getterMethod.CreateDelegate(typeof(Func<FieldType>));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a delegate which gets the value of a field. If emitting is not supported on the current platform, the delegate will use reflection to get the value.
+ /// </summary>
+ /// <param name="fieldInfo">The <see cref="FieldInfo"/> instance describing the field to create a getter for.</param>
+ /// <returns>A delegate which gets the value of the given field.</returns>
+ /// <exception cref="System.ArgumentNullException">The fieldInfo parameter is null.</exception>
+ public static Func<object> CreateWeakStaticFieldGetter(FieldInfo fieldInfo)
+ {
+ if (fieldInfo == null)
+ {
+ throw new ArgumentNullException("fieldInfo");
+ }
+
+ if (!fieldInfo.IsStatic)
+ {
+ throw new ArgumentException("Field must be static.");
+ }
+
+ fieldInfo = fieldInfo.DeAliasField();
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return delegate ()
+ {
+ return fieldInfo.GetValue(null);
+ };
+#else
+ string methodName = fieldInfo.ReflectedType.FullName + ".get_" + fieldInfo.Name;
+
+ DynamicMethod getterMethod = new DynamicMethod(methodName, typeof(object), new Type[0], true);
+ ILGenerator gen = getterMethod.GetILGenerator();
+
+ gen.Emit(OpCodes.Ldsfld, fieldInfo);
+
+ if (fieldInfo.FieldType.IsValueType)
+ {
+ gen.Emit(OpCodes.Box, fieldInfo.FieldType);
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (Func<object>)getterMethod.CreateDelegate(typeof(Func<object>));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a delegate which sets the value of a field. If emitting is not supported on the current platform, the delegate will use reflection to set the value.
+ /// </summary>
+ /// <typeparam name="FieldType">The type of the field to set a value to.</typeparam>
+ /// <param name="fieldInfo">The <see cref="FieldInfo"/> instance describing the field to create a setter for.</param>
+ /// <returns>A delegate which sets the value of the given field.</returns>
+ /// <exception cref="System.ArgumentNullException">The fieldInfo parameter is null.</exception>
+ public static Action<FieldType> CreateStaticFieldSetter<FieldType>(FieldInfo fieldInfo)
+ {
+ if (fieldInfo == null)
+ {
+ throw new ArgumentNullException("fieldInfo");
+ }
+
+ if (!fieldInfo.IsStatic)
+ {
+ throw new ArgumentException("Field must be static.");
+ }
+
+ fieldInfo = fieldInfo.DeAliasField();
+
+ if (fieldInfo.IsLiteral)
+ {
+ throw new ArgumentException("Field cannot be constant.");
+ }
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return delegate (FieldType value)
+ {
+ fieldInfo.SetValue(null, value);
+ };
+#else
+ string methodName = fieldInfo.ReflectedType.FullName + ".set_" + fieldInfo.Name;
+
+ DynamicMethod setterMethod = new DynamicMethod(methodName, null, new Type[] { typeof(FieldType) }, true);
+ ILGenerator gen = setterMethod.GetILGenerator();
+
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Stsfld, fieldInfo);
+ gen.Emit(OpCodes.Ret);
+
+ return (Action<FieldType>)setterMethod.CreateDelegate(typeof(Action<FieldType>));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a delegate which sets the value of a field. If emitting is not supported on the current platform, the delegate will use reflection to set the value.
+ /// </summary>
+ /// <param name="fieldInfo">The <see cref="FieldInfo"/> instance describing the field to create a setter for.</param>
+ /// <returns>A delegate which sets the value of the given field.</returns>
+ /// <exception cref="System.ArgumentNullException">The fieldInfo parameter is null.</exception>
+ public static Action<object> CreateWeakStaticFieldSetter(FieldInfo fieldInfo)
+ {
+ if (fieldInfo == null)
+ {
+ throw new ArgumentNullException("fieldInfo");
+ }
+
+ if (!fieldInfo.IsStatic)
+ {
+ throw new ArgumentException("Field must be static.");
+ }
+
+ fieldInfo = fieldInfo.DeAliasField();
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return delegate (object value)
+ {
+ fieldInfo.SetValue(null, value);
+ };
+#else
+ string methodName = fieldInfo.ReflectedType.FullName + ".set_" + fieldInfo.Name;
+
+ DynamicMethod setterMethod = new DynamicMethod(methodName, null, new Type[] { typeof(object) }, true);
+ ILGenerator gen = setterMethod.GetILGenerator();
+
+ gen.Emit(OpCodes.Ldarg_0);
+
+ if (fieldInfo.FieldType.IsValueType)
+ {
+ gen.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Castclass, fieldInfo.FieldType);
+ }
+
+ gen.Emit(OpCodes.Stsfld, fieldInfo);
+ gen.Emit(OpCodes.Ret);
+
+ return (Action<object>)setterMethod.CreateDelegate(typeof(Action<object>));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a delegate which gets the value of a field. If emitting is not supported on the current platform, the delegate will use reflection to get the value.
+ /// </summary>
+ /// <typeparam name="InstanceType">The type of the instance to get a value from.</typeparam>
+ /// <typeparam name="FieldType">The type of the field to get a value from.</typeparam>
+ /// <param name="fieldInfo">The <see cref="FieldInfo"/> instance describing the field to create a getter for.</param>
+ /// <returns>A delegate which gets the value of the given field.</returns>
+ /// <exception cref="System.ArgumentNullException">The fieldInfo parameter is null.</exception>
+ public static ValueGetter<InstanceType, FieldType> CreateInstanceFieldGetter<InstanceType, FieldType>(FieldInfo fieldInfo)
+ {
+ if (fieldInfo == null)
+ {
+ throw new ArgumentNullException("fieldInfo");
+ }
+
+ if (fieldInfo.IsStatic)
+ {
+ throw new ArgumentException("Field cannot be static.");
+ }
+
+ fieldInfo = fieldInfo.DeAliasField();
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return delegate (ref InstanceType classInstance)
+ {
+ return (FieldType)fieldInfo.GetValue(classInstance);
+ };
+#else
+ string methodName = fieldInfo.ReflectedType.FullName + ".get_" + fieldInfo.Name;
+
+ DynamicMethod getterMethod = new DynamicMethod(methodName, typeof(FieldType), new Type[1] { typeof(InstanceType).MakeByRefType() }, true);
+ ILGenerator gen = getterMethod.GetILGenerator();
+
+ if (typeof(InstanceType).IsValueType)
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldfld, fieldInfo);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldind_Ref);
+ gen.Emit(OpCodes.Ldfld, fieldInfo);
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (ValueGetter<InstanceType, FieldType>)getterMethod.CreateDelegate(typeof(ValueGetter<InstanceType, FieldType>));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a delegate which gets the value of a field from a weakly typed instance of a given type. If emitting is not supported on the current platform, the delegate will use reflection to get the value.
+ /// </summary>
+ /// <typeparam name="FieldType">The type of the field to get a value from.</typeparam>
+ /// <param name="instanceType">The <see cref="Type"/> of the instance to get a value from.</param>
+ /// <param name="fieldInfo">The <see cref="FieldInfo"/> instance describing the field to create a getter for.</param>
+ /// <returns>A delegate which gets the value of the given field.</returns>
+ /// <exception cref="System.ArgumentNullException">The fieldInfo parameter is null.</exception>
+ public static WeakValueGetter<FieldType> CreateWeakInstanceFieldGetter<FieldType>(Type instanceType, FieldInfo fieldInfo)
+ {
+ if (fieldInfo == null)
+ {
+ throw new ArgumentNullException("fieldInfo");
+ }
+
+ if (instanceType == null)
+ {
+ throw new ArgumentNullException("instanceType");
+ }
+
+ if (fieldInfo.IsStatic)
+ {
+ throw new ArgumentException("Field cannot be static.");
+ }
+
+ fieldInfo = fieldInfo.DeAliasField();
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return delegate (ref object classInstance)
+ {
+ return (FieldType)fieldInfo.GetValue(classInstance);
+ };
+#else
+ string methodName = fieldInfo.ReflectedType.FullName + ".get_" + fieldInfo.Name;
+
+ DynamicMethod getterMethod = new DynamicMethod(methodName, typeof(FieldType), new Type[1] { typeof(object).MakeByRefType() }, true);
+ ILGenerator gen = getterMethod.GetILGenerator();
+
+ if (instanceType.IsValueType)
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldind_Ref);
+ gen.Emit(OpCodes.Unbox_Any, instanceType);
+ gen.Emit(OpCodes.Ldfld, fieldInfo);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldind_Ref);
+ gen.Emit(OpCodes.Castclass, instanceType);
+ gen.Emit(OpCodes.Ldfld, fieldInfo);
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (WeakValueGetter<FieldType>)getterMethod.CreateDelegate(typeof(WeakValueGetter<FieldType>));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a delegate which gets the weakly typed value of a field from a weakly typed instance of a given type. If emitting is not supported on the current platform, the delegate will use reflection to get the value.
+ /// </summary>
+ /// <param name="instanceType">The <see cref="Type"/> of the instance to get a value from.</param>
+ /// <param name="fieldInfo">The <see cref="FieldInfo"/> instance describing the field to create a getter for.</param>
+ /// <returns>A delegate which gets the value of the given field.</returns>
+ /// <exception cref="System.ArgumentNullException">The fieldInfo parameter is null.</exception>
+ public static WeakValueGetter CreateWeakInstanceFieldGetter(Type instanceType, FieldInfo fieldInfo)
+ {
+ if (fieldInfo == null)
+ {
+ throw new ArgumentNullException("fieldInfo");
+ }
+
+ if (instanceType == null)
+ {
+ throw new ArgumentNullException("instanceType");
+ }
+
+ if (fieldInfo.IsStatic)
+ {
+ throw new ArgumentException("Field cannot be static.");
+ }
+
+ fieldInfo = fieldInfo.DeAliasField();
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return delegate (ref object classInstance)
+ {
+ return fieldInfo.GetValue(classInstance);
+ };
+#else
+ string methodName = fieldInfo.ReflectedType.FullName + ".get_" + fieldInfo.Name;
+
+ DynamicMethod getterMethod = new DynamicMethod(methodName, typeof(object), new Type[1] { typeof(object).MakeByRefType() }, true);
+ ILGenerator gen = getterMethod.GetILGenerator();
+
+ if (instanceType.IsValueType)
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldind_Ref);
+ gen.Emit(OpCodes.Unbox_Any, instanceType);
+ gen.Emit(OpCodes.Ldfld, fieldInfo);
+
+ if (fieldInfo.FieldType.IsValueType)
+ {
+ gen.Emit(OpCodes.Box, fieldInfo.FieldType);
+ }
+ }
+ else
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldind_Ref);
+ gen.Emit(OpCodes.Castclass, instanceType);
+ gen.Emit(OpCodes.Ldfld, fieldInfo);
+
+ if (fieldInfo.FieldType.IsValueType)
+ {
+ gen.Emit(OpCodes.Box, fieldInfo.FieldType);
+ }
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (WeakValueGetter)getterMethod.CreateDelegate(typeof(WeakValueGetter));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a delegate which sets the value of a field. If emitting is not supported on the current platform, the delegate will use reflection to set the value.
+ /// </summary>
+ /// <typeparam name="InstanceType">The type of the instance to set a value on.</typeparam>
+ /// <typeparam name="FieldType">The type of the field to set a value to.</typeparam>
+ /// <param name="fieldInfo">The <see cref="FieldInfo"/> instance describing the field to create a setter for.</param>
+ /// <returns>A delegate which sets the value of the given field.</returns>
+ /// <exception cref="System.ArgumentNullException">The fieldInfo parameter is null.</exception>
+ public static ValueSetter<InstanceType, FieldType> CreateInstanceFieldSetter<InstanceType, FieldType>(FieldInfo fieldInfo)
+ {
+ if (fieldInfo == null)
+ {
+ throw new ArgumentNullException("fieldInfo");
+ }
+
+ if (fieldInfo.IsStatic)
+ {
+ throw new ArgumentException("Field cannot be static.");
+ }
+
+ fieldInfo = fieldInfo.DeAliasField();
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return delegate (ref InstanceType classInstance, FieldType value)
+ {
+ if (typeof(InstanceType).IsValueType)
+ {
+ // Box value type so that the value will be properly set via reflection
+ object obj = classInstance;
+ fieldInfo.SetValue(obj, value);
+ // Unbox the boxed value type that was changed
+ classInstance = (InstanceType)obj;
+ }
+ else
+ {
+ fieldInfo.SetValue(classInstance, value);
+ }
+ };
+#else
+ string methodName = fieldInfo.ReflectedType.FullName + ".set_" + fieldInfo.Name;
+
+ DynamicMethod setterMethod = new DynamicMethod(methodName, null, new Type[2] { typeof(InstanceType).MakeByRefType(), typeof(FieldType) }, true);
+ ILGenerator gen = setterMethod.GetILGenerator();
+
+ if (typeof(InstanceType).IsValueType)
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldarg_1);
+ gen.Emit(OpCodes.Stfld, fieldInfo);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldind_Ref);
+ gen.Emit(OpCodes.Ldarg_1);
+ gen.Emit(OpCodes.Stfld, fieldInfo);
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (ValueSetter<InstanceType, FieldType>)setterMethod.CreateDelegate(typeof(ValueSetter<InstanceType, FieldType>));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a delegate which sets the value of a field on a weakly typed instance of a given type. If emitting is not supported on the current platform, the delegate will use reflection to set the value.
+ /// </summary>
+ /// <typeparam name="FieldType">The type of the field to set a value to.</typeparam>
+ /// <param name="instanceType">Type of the instance.</param>
+ /// <param name="fieldInfo">The <see cref="FieldInfo" /> instance describing the field to create a setter for.</param>
+ /// <returns>
+ /// A delegate which sets the value of the given field.
+ /// </returns>
+ /// <exception cref="System.ArgumentNullException">The fieldInfo parameter is null.</exception>
+ /// <exception cref="System.ArgumentException">Field cannot be static.</exception>
+ public static WeakValueSetter<FieldType> CreateWeakInstanceFieldSetter<FieldType>(Type instanceType, FieldInfo fieldInfo)
+ {
+ if (fieldInfo == null)
+ {
+ throw new ArgumentNullException("fieldInfo");
+ }
+
+ if (instanceType == null)
+ {
+ throw new ArgumentNullException("instanceType");
+ }
+
+ if (fieldInfo.IsStatic)
+ {
+ throw new ArgumentException("Field cannot be static.");
+ }
+
+ fieldInfo = fieldInfo.DeAliasField();
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return delegate (ref object classInstance, FieldType value)
+ {
+ fieldInfo.SetValue(classInstance, value);
+ };
+#else
+
+ string methodName = fieldInfo.ReflectedType.FullName + ".set_" + fieldInfo.Name;
+
+ DynamicMethod setterMethod = new DynamicMethod(methodName, null, new Type[2] { typeof(object).MakeByRefType(), typeof(FieldType) }, true);
+ ILGenerator gen = setterMethod.GetILGenerator();
+
+ if (instanceType.IsValueType)
+ {
+ var local = gen.DeclareLocal(instanceType);
+
+ gen.Emit(OpCodes.Ldarg_0); // Load object reference argument
+ gen.Emit(OpCodes.Ldind_Ref); // Load reference
+ gen.Emit(OpCodes.Unbox_Any, instanceType); // Unbox to struct
+ gen.Emit(OpCodes.Stloc, local); // Set local to struct value
+ gen.Emit(OpCodes.Ldloca_S, local); // Load address to local value
+ gen.Emit(OpCodes.Ldarg_1); // Load FieldType value
+ gen.Emit(OpCodes.Stfld, fieldInfo); // Set field on local struct value
+ gen.Emit(OpCodes.Ldarg_0); // Load object reference argument
+ gen.Emit(OpCodes.Ldloc, local); // Load local struct value
+ gen.Emit(OpCodes.Box, instanceType); // Box local struct
+ gen.Emit(OpCodes.Stind_Ref); // Set object reference argument
+ }
+ else
+ {
+ gen.Emit(OpCodes.Ldarg_0); // Load object reference argument
+ gen.Emit(OpCodes.Ldind_Ref); // Load reference
+ gen.Emit(OpCodes.Castclass, instanceType); // Cast to instance type
+ gen.Emit(OpCodes.Ldarg_1); // Load value argument
+ gen.Emit(OpCodes.Stfld, fieldInfo); // Set field
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (WeakValueSetter<FieldType>)setterMethod.CreateDelegate(typeof(WeakValueSetter<FieldType>));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a delegate which sets the weakly typed value of a field on a weakly typed instance of a given type. If emitting is not supported on the current platform, the delegate will use reflection to set the value.
+ /// </summary>
+ /// <param name="instanceType">Type of the instance.</param>
+ /// <param name="fieldInfo">The <see cref="FieldInfo" /> instance describing the field to create a setter for.</param>
+ /// <returns>
+ /// A delegate which sets the value of the given field.
+ /// </returns>
+ /// <exception cref="System.ArgumentNullException">The fieldInfo parameter is null.</exception>
+ /// <exception cref="System.ArgumentException">Field cannot be static.</exception>
+ public static WeakValueSetter CreateWeakInstanceFieldSetter(Type instanceType, FieldInfo fieldInfo)
+ {
+ if (fieldInfo == null)
+ {
+ throw new ArgumentNullException("fieldInfo");
+ }
+
+ if (instanceType == null)
+ {
+ throw new ArgumentNullException("instanceType");
+ }
+
+ if (fieldInfo.IsStatic)
+ {
+ throw new ArgumentException("Field cannot be static.");
+ }
+
+ fieldInfo = fieldInfo.DeAliasField();
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return delegate (ref object classInstance, object value)
+ {
+ fieldInfo.SetValue(classInstance, value);
+ };
+#else
+
+ string methodName = fieldInfo.ReflectedType.FullName + ".set_" + fieldInfo.Name;
+
+ DynamicMethod setterMethod = new DynamicMethod(methodName, null, new Type[2] { typeof(object).MakeByRefType(), typeof(object) }, true);
+ ILGenerator gen = setterMethod.GetILGenerator();
+
+ if (instanceType.IsValueType)
+ {
+ var local = gen.DeclareLocal(instanceType);
+
+ gen.Emit(OpCodes.Ldarg_0); // Load object reference argument
+ gen.Emit(OpCodes.Ldind_Ref); // Load reference
+ gen.Emit(OpCodes.Unbox_Any, instanceType); // Unbox to struct
+ gen.Emit(OpCodes.Stloc, local); // Set local to struct value
+ gen.Emit(OpCodes.Ldloca_S, local); // Load address to local value
+ gen.Emit(OpCodes.Ldarg_1); // Load FieldType value
+
+ if (fieldInfo.FieldType.IsValueType)
+ {
+ gen.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Castclass, fieldInfo.FieldType);
+ }
+
+ gen.Emit(OpCodes.Stfld, fieldInfo); // Set field on local struct value
+ gen.Emit(OpCodes.Ldarg_0); // Load object reference argument
+ gen.Emit(OpCodes.Ldloc, local); // Load local struct value
+ gen.Emit(OpCodes.Box, instanceType); // Box local struct
+ gen.Emit(OpCodes.Stind_Ref); // Set object reference argument
+ }
+ else
+ {
+ gen.Emit(OpCodes.Ldarg_0); // Load object reference argument
+ gen.Emit(OpCodes.Ldind_Ref); // Load reference
+ gen.Emit(OpCodes.Castclass, instanceType); // Cast to instance type
+ gen.Emit(OpCodes.Ldarg_1); // Load value argument
+
+ if (fieldInfo.FieldType.IsValueType)
+ {
+ gen.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Castclass, fieldInfo.FieldType);
+ }
+
+ gen.Emit(OpCodes.Stfld, fieldInfo); // Set field
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (WeakValueSetter)setterMethod.CreateDelegate(typeof(WeakValueSetter));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a delegate which gets the weakly typed value of a field from a weakly typed instance of a given type. If emitting is not supported on the current platform, the delegate will use reflection to get the value.
+ /// </summary>
+ /// <param name="instanceType">The <see cref="Type"/> of the instance to get a value from.</param>
+ /// <param name="propertyInfo">The <see cref="FieldInfo"/> instance describing the field to create a getter for.</param>
+ /// <returns>A delegate which gets the value of the given field.</returns>
+ /// <exception cref="System.ArgumentNullException">The fieldInfo parameter is null.</exception>
+ public static WeakValueGetter CreateWeakInstancePropertyGetter(Type instanceType, PropertyInfo propertyInfo)
+ {
+ if (propertyInfo == null)
+ {
+ throw new ArgumentNullException("propertyInfo");
+ }
+
+ if (instanceType == null)
+ {
+ throw new ArgumentNullException("instanceType");
+ }
+
+ propertyInfo = propertyInfo.DeAliasProperty();
+
+ if (propertyInfo.GetIndexParameters().Length > 0)
+ {
+ throw new ArgumentException("Property must not have any index parameters");
+ }
+
+ var getMethod = propertyInfo.GetGetMethod(true);
+
+ if (getMethod == null)
+ {
+ throw new ArgumentException("Property must have a getter.");
+ }
+
+ if (getMethod.IsStatic)
+ {
+ throw new ArgumentException("Property cannot be static.");
+ }
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return delegate (ref object classInstance)
+ {
+ return propertyInfo.GetValue(classInstance, null);
+ };
+#else
+
+ string methodName = propertyInfo.ReflectedType.FullName + ".get_" + propertyInfo.Name;
+
+ DynamicMethod getterMethod = new DynamicMethod(methodName, typeof(object), new Type[1] { typeof(object).MakeByRefType() }, true);
+ ILGenerator gen = getterMethod.GetILGenerator();
+
+ if (instanceType.IsValueType)
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldind_Ref);
+ gen.Emit(OpCodes.Unbox_Any, instanceType);
+
+ if (getMethod.IsVirtual || getMethod.IsAbstract)
+ {
+ gen.Emit(OpCodes.Callvirt, getMethod);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Call, getMethod);
+ }
+
+ if (propertyInfo.PropertyType.IsValueType)
+ {
+ gen.Emit(OpCodes.Box, propertyInfo.PropertyType);
+ }
+ }
+ else
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldind_Ref);
+ gen.Emit(OpCodes.Castclass, instanceType);
+
+ if (getMethod.IsVirtual || getMethod.IsAbstract)
+ {
+ gen.Emit(OpCodes.Callvirt, getMethod);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Call, getMethod);
+ }
+
+ if (propertyInfo.PropertyType.IsValueType)
+ {
+ gen.Emit(OpCodes.Box, propertyInfo.PropertyType);
+ }
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (WeakValueGetter)getterMethod.CreateDelegate(typeof(WeakValueGetter));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a delegate which sets the weakly typed value of a property on a weakly typed instance of a given type. If emitting is not supported on the current platform, the delegate will use reflection to set the value.
+ /// </summary>
+ /// <param name="instanceType">Type of the instance.</param>
+ /// <param name="propertyInfo">The <see cref="PropertyInfo" /> instance describing the property to create a setter for.</param>
+ /// <returns>
+ /// A delegate which sets the value of the given field.
+ /// </returns>
+ /// <exception cref="System.ArgumentNullException">The fieldInfo parameter is null.</exception>
+ /// <exception cref="System.ArgumentException">Property cannot be static.</exception>
+ public static WeakValueSetter CreateWeakInstancePropertySetter(Type instanceType, PropertyInfo propertyInfo)
+ {
+ if (propertyInfo == null)
+ {
+ throw new ArgumentNullException("propertyInfo");
+ }
+
+ if (instanceType == null)
+ {
+ throw new ArgumentNullException("instanceType");
+ }
+
+ propertyInfo = propertyInfo.DeAliasProperty();
+
+ if (propertyInfo.GetIndexParameters().Length > 0)
+ {
+ throw new ArgumentException("Property must not have any index parameters");
+ }
+
+ var setMethod = propertyInfo.GetSetMethod(true);
+
+ if (setMethod.IsStatic)
+ {
+ throw new ArgumentException("Property cannot be static.");
+ }
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return delegate (ref object classInstance, object value)
+ {
+ propertyInfo.SetValue(classInstance, value, null);
+ };
+#else
+
+ string methodName = propertyInfo.ReflectedType.FullName + ".set_" + propertyInfo.Name;
+
+ DynamicMethod setterMethod = new DynamicMethod(methodName, null, new Type[2] { typeof(object).MakeByRefType(), typeof(object) }, true);
+ ILGenerator gen = setterMethod.GetILGenerator();
+
+ if (instanceType.IsValueType)
+ {
+ var local = gen.DeclareLocal(instanceType);
+
+ gen.Emit(OpCodes.Ldarg_0); // Load object reference argument
+ gen.Emit(OpCodes.Ldind_Ref); // Load reference
+ gen.Emit(OpCodes.Unbox_Any, instanceType); // Unbox to struct
+ gen.Emit(OpCodes.Stloc, local); // Set local to struct value
+ gen.Emit(OpCodes.Ldloca_S, local); // Load address to local value
+ gen.Emit(OpCodes.Ldarg_1); // Load PropertyInfo value
+
+ if (propertyInfo.PropertyType.IsValueType)
+ {
+ gen.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Castclass, propertyInfo.PropertyType);
+ }
+
+ if (setMethod.IsVirtual || setMethod.IsAbstract)
+ {
+ gen.Emit(OpCodes.Callvirt, setMethod); // Set property on local struct value
+ }
+ else
+ {
+ gen.Emit(OpCodes.Call, setMethod); // Set property on local struct value
+ }
+
+ gen.Emit(OpCodes.Ldarg_0); // Load object reference argument
+ gen.Emit(OpCodes.Ldloc, local); // Load local struct value
+ gen.Emit(OpCodes.Box, instanceType); // Box local struct
+ gen.Emit(OpCodes.Stind_Ref); // Set object reference argument
+ }
+ else
+ {
+ gen.Emit(OpCodes.Ldarg_0); // Load object reference argument
+ gen.Emit(OpCodes.Ldind_Ref); // Load reference
+ gen.Emit(OpCodes.Castclass, instanceType); // Cast to instance type
+ gen.Emit(OpCodes.Ldarg_1); // Load value argument
+
+ if (propertyInfo.PropertyType.IsValueType)
+ {
+ gen.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Castclass, propertyInfo.PropertyType);
+ }
+
+ if (setMethod.IsVirtual || setMethod.IsAbstract)
+ {
+ gen.Emit(OpCodes.Callvirt, setMethod); // Set property on local struct value
+ }
+ else
+ {
+ gen.Emit(OpCodes.Call, setMethod); // Set property on local struct value
+ }
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (WeakValueSetter)setterMethod.CreateDelegate(typeof(WeakValueSetter));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a delegate which sets the value of a property. If emitting is not supported on the current platform, the delegate will use reflection to set the value.
+ /// </summary>
+ /// <typeparam name="PropType">The type of the property to set a value to.</typeparam>
+ /// <param name="propertyInfo">The <see cref="PropertyInfo"/> instance describing the property to create a setter for.</param>
+ /// <returns>A delegate which sets the value of the given property.</returns>
+ /// <exception cref="System.ArgumentNullException">The propertyInfo parameter is null.</exception>
+ public static Action<PropType> CreateStaticPropertySetter<PropType>(PropertyInfo propertyInfo)
+ {
+ if (propertyInfo == null)
+ {
+ throw new ArgumentNullException("fieldInfo");
+ }
+
+ propertyInfo = propertyInfo.DeAliasProperty();
+
+ if (propertyInfo.GetIndexParameters().Length > 0)
+ {
+ throw new ArgumentException("Property must not have any index parameters");
+ }
+
+ MethodInfo setMethod = propertyInfo.GetSetMethod(true);
+
+ if (setMethod == null)
+ {
+ throw new ArgumentException("Property must have a set method.");
+ }
+
+ if (!setMethod.IsStatic)
+ {
+ throw new ArgumentException("Property must be static.");
+ }
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return delegate (PropType value)
+ {
+ propertyInfo.SetValue(null, value, null);
+ };
+#else
+ string methodName = propertyInfo.ReflectedType.FullName + ".set_" + propertyInfo.Name;
+
+ DynamicMethod setterMethod = new DynamicMethod(methodName, null, new Type[] { typeof(PropType) }, true);
+ ILGenerator gen = setterMethod.GetILGenerator();
+
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Call, setMethod);
+ gen.Emit(OpCodes.Ret);
+
+ return (Action<PropType>)setterMethod.CreateDelegate(typeof(Action<PropType>));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a delegate which gets the value of a property. If emitting is not supported on the current platform, the delegate will use reflection to get the value.
+ /// </summary>
+ /// <typeparam name="PropType">The type of the property to get a value from.</typeparam>
+ /// <param name="propertyInfo">The <see cref="PropertyInfo"/> instance describing the property to create a getter for.</param>
+ /// <returns>A delegate which gets the value of the given property.</returns>
+ /// <exception cref="System.ArgumentNullException">The propertyInfo parameter is null.</exception>
+ public static Func<PropType> CreateStaticPropertyGetter<PropType>(PropertyInfo propertyInfo)
+ {
+ if (propertyInfo == null)
+ {
+ throw new ArgumentNullException("propertyInfo");
+ }
+
+ propertyInfo = propertyInfo.DeAliasProperty();
+
+ if (propertyInfo.GetIndexParameters().Length > 0)
+ {
+ throw new ArgumentException("Property must not have any index parameters");
+ }
+
+ MethodInfo getMethod = propertyInfo.GetGetMethod(true);
+
+ if (getMethod == null)
+ {
+ throw new ArgumentException("Property must have a get method.");
+ }
+
+ if (!getMethod.IsStatic)
+ {
+ throw new ArgumentException("Property must be static.");
+ }
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return delegate ()
+ {
+ return (PropType)propertyInfo.GetValue(null, null);
+ };
+#else
+
+ string methodName = propertyInfo.ReflectedType.FullName + ".get_" + propertyInfo.Name;
+
+ DynamicMethod getterMethod = new DynamicMethod(methodName, typeof(PropType), new Type[0], true);
+ ILGenerator gen = getterMethod.GetILGenerator();
+
+ gen.Emit(OpCodes.Call, getMethod);
+
+ var returnType = propertyInfo.GetReturnType();
+ if (returnType.IsValueType && !typeof(PropType).IsValueType)
+ {
+ gen.Emit(OpCodes.Box, returnType);
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (Func<PropType>)getterMethod.CreateDelegate(typeof(Func<PropType>));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a delegate which sets the value of a property. If emitting is not supported on the current platform, the delegate will use reflection to set the value.
+ /// </summary>
+ /// <typeparam name="InstanceType">The type of the instance to set a value on.</typeparam>
+ /// <typeparam name="PropType">The type of the property to set a value to.</typeparam>
+ /// <param name="propertyInfo">The <see cref="PropertyInfo"/> instance describing the property to create a setter for.</param>
+ /// <returns>A delegate which sets the value of the given property.</returns>
+ /// <exception cref="System.ArgumentNullException">The propertyInfo parameter is null.</exception>
+ public static ValueSetter<InstanceType, PropType> CreateInstancePropertySetter<InstanceType, PropType>(PropertyInfo propertyInfo)
+ {
+ if (propertyInfo == null)
+ {
+ throw new ArgumentNullException("fieldInfo");
+ }
+
+ propertyInfo = propertyInfo.DeAliasProperty();
+
+ if (propertyInfo.GetIndexParameters().Length > 0)
+ {
+ throw new ArgumentException("Property must not have any index parameters");
+ }
+
+ MethodInfo setMethod = propertyInfo.GetSetMethod(true);
+
+ if (setMethod == null)
+ {
+ throw new ArgumentException("Property must have a set method.");
+ }
+
+ if (setMethod.IsStatic)
+ {
+ throw new ArgumentException("Property cannot be static.");
+ }
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return delegate (ref InstanceType classInstance, PropType value)
+ {
+ if (typeof(InstanceType).IsValueType)
+ {
+ // Box value type so that the value will be properly set via reflection
+ object obj = classInstance;
+ propertyInfo.SetValue(obj, value, null);
+ // Unbox the boxed value type that was changed
+ classInstance = (InstanceType)obj;
+ }
+ else
+ {
+ propertyInfo.SetValue(classInstance, value, null);
+ }
+ };
+#else
+
+ string methodName = propertyInfo.ReflectedType.FullName + ".set_" + propertyInfo.Name;
+
+ DynamicMethod setterMethod = new DynamicMethod(methodName, null, new Type[2] { typeof(InstanceType).MakeByRefType(), typeof(PropType) }, true);
+ ILGenerator gen = setterMethod.GetILGenerator();
+
+ if (typeof(InstanceType).IsValueType)
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldarg_1);
+ gen.Emit(OpCodes.Callvirt, setMethod);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldind_Ref);
+ gen.Emit(OpCodes.Ldarg_1);
+ gen.Emit(OpCodes.Callvirt, setMethod);
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (ValueSetter<InstanceType, PropType>)setterMethod.CreateDelegate(typeof(ValueSetter<InstanceType, PropType>));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a delegate which gets the value of a property. If emitting is not supported on the current platform, the delegate will use reflection to get the value.
+ /// </summary>
+ /// <typeparam name="InstanceType">The type of the instance to get a value from.</typeparam>
+ /// <typeparam name="PropType">The type of the property to get a value from.</typeparam>
+ /// <param name="propertyInfo">The <see cref="PropertyInfo"/> instance describing the property to create a getter for.</param>
+ /// <returns>A delegate which gets the value of the given property.</returns>
+ /// <exception cref="System.ArgumentNullException">The propertyInfo parameter is null.</exception>
+ public static ValueGetter<InstanceType, PropType> CreateInstancePropertyGetter<InstanceType, PropType>(PropertyInfo propertyInfo)
+ {
+ if (propertyInfo == null)
+ {
+ throw new ArgumentNullException("propertyInfo");
+ }
+
+ propertyInfo = propertyInfo.DeAliasProperty();
+
+ if (propertyInfo.GetIndexParameters().Length > 0)
+ {
+ throw new ArgumentException("Property must not have any index parameters");
+ }
+
+ MethodInfo getMethod = propertyInfo.GetGetMethod(true);
+
+ if (getMethod == null)
+ {
+ throw new ArgumentException("Property must have a get method.");
+ }
+
+ if (getMethod.IsStatic)
+ {
+ throw new ArgumentException("Property cannot be static.");
+ }
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return delegate (ref InstanceType classInstance)
+ {
+ return (PropType)propertyInfo.GetValue(classInstance, null);
+ };
+#else
+
+ string methodName = propertyInfo.ReflectedType.FullName + ".get_" + propertyInfo.Name;
+
+ DynamicMethod getterMethod = new DynamicMethod(methodName, typeof(PropType), new Type[] { typeof(InstanceType).MakeByRefType() }, true);
+ ILGenerator gen = getterMethod.GetILGenerator();
+
+ if (typeof(InstanceType).IsValueType)
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Callvirt, getMethod);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldind_Ref);
+ gen.Emit(OpCodes.Callvirt, getMethod);
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (ValueGetter<InstanceType, PropType>)getterMethod.CreateDelegate(typeof(ValueGetter<InstanceType, PropType>));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a fast delegate method which calls a given parameterless instance method and returns the result.
+ /// </summary>
+ /// <typeparam name="InstanceType">The type of the class which the method is on.</typeparam>
+ /// <typeparam name="ReturnType">The type which is returned by the given method info.</typeparam>
+ /// <param name="methodInfo">The method info instance which is used.</param>
+ /// <returns>A delegate which calls the method and returns the result, except it's hundreds of times faster than MethodInfo.Invoke.</returns>
+ public static Func<InstanceType, ReturnType> CreateMethodReturner<InstanceType, ReturnType>(MethodInfo methodInfo)
+ {
+ if (methodInfo == null)
+ {
+ throw new ArgumentNullException("methodInfo");
+ }
+
+ if (methodInfo.IsStatic)
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' is static when it has to be an instance method.");
+ }
+
+ methodInfo = methodInfo.DeAliasMethod();
+
+ // Luckily there's no need to emit this - we can just create a delegate and it's only ~10% slower than calling the method directly
+ // from normal compiled/emitted code. As opposed to using MethodInfo.Invoke, which is on average 600 (!!!) times slower.
+ return (Func<InstanceType, ReturnType>)Delegate.CreateDelegate(typeof(Func<InstanceType, ReturnType>), methodInfo);
+ }
+
+ /// <summary>
+ /// Creates a fast delegate method which calls a given parameterless static method.
+ /// </summary>
+ /// <param name="methodInfo">The method info instance which is used.</param>
+ /// <returns>A delegate which calls the method and returns the result, except it's hundreds of times faster than MethodInfo.Invoke.</returns>
+ public static Action CreateStaticMethodCaller(MethodInfo methodInfo)
+ {
+ if (methodInfo == null)
+ {
+ throw new ArgumentNullException("methodInfo");
+ }
+
+ if (!methodInfo.IsStatic)
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' is an instance method when it has to be static.");
+ }
+
+ if (methodInfo.GetParameters().Length > 0)
+ {
+ throw new ArgumentException("Given method cannot have any parameters.");
+ }
+
+ methodInfo = methodInfo.DeAliasMethod();
+
+ // Luckily there's no need to emit this - we can just create a delegate and it's only ~10% slower than calling the method directly
+ // from normal compiled/emitted code. As opposed to using MethodInfo.Invoke, which is on average 600 (!!!) times slower.
+ return (Action)Delegate.CreateDelegate(typeof(Action), methodInfo);
+ }
+
+ /// <summary>
+ /// Creates a fast delegate method which calls a given parameterless weakly typed instance method.
+ /// </summary>
+ /// <param name="methodInfo">The method info instance which is used.</param>
+ /// <returns>A delegate which calls the method and returns the result, except it's hundreds of times faster than MethodInfo.Invoke.</returns>
+ public static Action<object, TArg1> CreateWeakInstanceMethodCaller<TArg1>(MethodInfo methodInfo)
+ {
+ if (methodInfo == null)
+ {
+ throw new ArgumentNullException("methodInfo");
+ }
+
+ if (methodInfo.IsStatic)
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' is static when it has to be an instance method.");
+ }
+
+ var parameters = methodInfo.GetParameters();
+
+ if (parameters.Length != 1)
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' must have exactly one parameter.");
+ }
+
+ if (parameters[0].ParameterType != typeof(TArg1))
+ {
+ throw new ArgumentException("The first parameter of the method '" + methodInfo.Name + "' must be of type " + typeof(TArg1) + ".");
+ }
+
+ methodInfo = methodInfo.DeAliasMethod();
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return (object classInstance, TArg1 arg) =>
+ {
+ methodInfo.Invoke(classInstance, new object[] { arg });
+ };
+#else
+
+ Type declaringType = methodInfo.DeclaringType;
+ string methodName = methodInfo.ReflectedType.FullName + ".call_" + methodInfo.Name;
+
+ DynamicMethod method = new DynamicMethod(methodName, null, new Type[] { typeof(object), typeof(TArg1) }, true);
+ ILGenerator gen = method.GetILGenerator();
+
+ if (declaringType.IsValueType)
+ {
+ var loc = gen.DeclareLocal(declaringType);
+
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Unbox_Any, declaringType);
+ gen.Emit(OpCodes.Stloc, loc);
+ gen.Emit(OpCodes.Ldloca_S, loc);
+ gen.Emit(OpCodes.Ldarg_1);
+ gen.Emit(OpCodes.Call, methodInfo);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Castclass, declaringType);
+ gen.Emit(OpCodes.Ldarg_1);
+ gen.Emit(OpCodes.Callvirt, methodInfo);
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (Action<object, TArg1>)method.CreateDelegate(typeof(Action<object, TArg1>));
+#endif
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static Action<object> CreateWeakInstanceMethodCaller(MethodInfo methodInfo)
+ {
+ if (methodInfo == null)
+ {
+ throw new ArgumentNullException("methodInfo");
+ }
+
+ if (methodInfo.IsStatic)
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' is static when it has to be an instance method.");
+ }
+
+ if (methodInfo.GetParameters().Length > 0)
+ {
+ throw new ArgumentException("Given method cannot have any parameters.");
+ }
+
+ methodInfo = methodInfo.DeAliasMethod();
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return delegate (object classInstance)
+ {
+ methodInfo.Invoke(classInstance, null);
+ };
+#else
+
+ Type declaringType = methodInfo.DeclaringType;
+ string methodName = methodInfo.ReflectedType.FullName + ".call_" + methodInfo.Name;
+
+ DynamicMethod method = new DynamicMethod(methodName, null, new Type[] { typeof(object) }, true);
+ ILGenerator gen = method.GetILGenerator();
+
+ if (declaringType.IsValueType)
+ {
+ var loc = gen.DeclareLocal(declaringType);
+
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Unbox_Any, declaringType);
+ gen.Emit(OpCodes.Stloc, loc);
+ gen.Emit(OpCodes.Ldloca_S, loc);
+ gen.Emit(OpCodes.Call, methodInfo);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Castclass, declaringType);
+ gen.Emit(OpCodes.Callvirt, methodInfo);
+ }
+
+ if (methodInfo.ReturnType != null && methodInfo.ReturnType != typeof(void))
+ {
+ // If there is a return type, pop the returned value off the stack, because we're not returning anything
+ gen.Emit(OpCodes.Pop);
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (Action<object>)method.CreateDelegate(typeof(Action<object>));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a fast delegate method which calls a given weakly typed instance method with one argument and returns a value.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result.</typeparam>
+ /// <typeparam name="TArg1">The type of the first argument.</typeparam>
+ /// <param name="methodInfo">The method info instance which is used.</param>
+ /// <returns>
+ /// A delegate which calls the method and returns the result, except it's hundreds of times faster than MethodInfo.Invoke.
+ /// </returns>
+ /// <exception cref="System.ArgumentNullException">methodInfo</exception>
+ /// <exception cref="System.ArgumentException">
+ /// Given method ' + methodInfo.Name + ' is static when it has to be an instance method.
+ /// or
+ /// Given method ' + methodInfo.Name + ' must return type + typeof(TResult) + .
+ /// or
+ /// Given method ' + methodInfo.Name + ' must have exactly one parameter.
+ /// or
+ /// The first parameter of the method ' + methodInfo.Name + ' must be of type + typeof(TArg1) + .
+ /// </exception>
+ public static Func<object, TArg1, TResult> CreateWeakInstanceMethodCaller<TResult, TArg1>(MethodInfo methodInfo)
+ {
+ if (methodInfo == null)
+ {
+ throw new ArgumentNullException("methodInfo");
+ }
+
+ if (methodInfo.IsStatic)
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' is static when it has to be an instance method.");
+ }
+
+ if (methodInfo.ReturnType != typeof(TResult))
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' must return type " + typeof(TResult) + ".");
+ }
+
+ var parameters = methodInfo.GetParameters();
+
+ if (parameters.Length != 1)
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' must have exactly one parameter.");
+ }
+
+ if (typeof(TArg1).InheritsFrom(parameters[0].ParameterType) == false)
+ {
+ throw new ArgumentException("The first parameter of the method '" + methodInfo.Name + "' must be of type " + typeof(TArg1) + ".");
+ }
+
+ methodInfo = methodInfo.DeAliasMethod();
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return (object classInstance, TArg1 arg1) =>
+ {
+ return (TResult)methodInfo.Invoke(classInstance, new object[] { arg1 });
+ };
+#else
+
+ Type declaringType = methodInfo.DeclaringType;
+ string methodName = methodInfo.ReflectedType.FullName + ".call_" + methodInfo.Name;
+
+ DynamicMethod method = new DynamicMethod(methodName, typeof(TResult), new Type[] { typeof(object), typeof(TArg1) }, true);
+ ILGenerator gen = method.GetILGenerator();
+
+ if (declaringType.IsValueType)
+ {
+ var loc = gen.DeclareLocal(declaringType);
+
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Unbox_Any, declaringType);
+ gen.Emit(OpCodes.Stloc, loc);
+ gen.Emit(OpCodes.Ldloca_S, loc);
+ gen.Emit(OpCodes.Ldarg_1);
+ gen.Emit(OpCodes.Call, methodInfo);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Castclass, declaringType);
+ gen.Emit(OpCodes.Ldarg_1);
+ gen.Emit(OpCodes.Callvirt, methodInfo);
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (Func<object, TArg1, TResult>)method.CreateDelegate(typeof(Func<object, TArg1, TResult>));
+#endif
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static Func<object, TResult> CreateWeakInstanceMethodCallerFunc<TResult>(MethodInfo methodInfo)
+ {
+ if (methodInfo == null)
+ {
+ throw new ArgumentNullException("methodInfo");
+ }
+
+ if (methodInfo.IsStatic)
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' is static when it has to be an instance method.");
+ }
+
+ if (methodInfo.ReturnType != typeof(TResult))
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' must return type " + typeof(TResult) + ".");
+ }
+
+ var parameters = methodInfo.GetParameters();
+
+ if (parameters.Length != 0)
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' must have no parameter.");
+ }
+
+ methodInfo = methodInfo.DeAliasMethod();
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return (object classInstance) =>
+ {
+ return (TResult)methodInfo.Invoke(classInstance, null);
+ };
+#else
+
+ Type declaringType = methodInfo.DeclaringType;
+ string methodName = methodInfo.ReflectedType.FullName + ".call_" + methodInfo.Name;
+
+ DynamicMethod method = new DynamicMethod(methodName, typeof(TResult), new Type[] { typeof(object) }, true);
+ ILGenerator gen = method.GetILGenerator();
+
+ if (declaringType.IsValueType)
+ {
+ var loc = gen.DeclareLocal(declaringType);
+
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Unbox_Any, declaringType);
+ gen.Emit(OpCodes.Stloc, loc);
+ gen.Emit(OpCodes.Ldloca_S, loc);
+ gen.Emit(OpCodes.Call, methodInfo);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Castclass, declaringType);
+ gen.Emit(OpCodes.Callvirt, methodInfo);
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (Func<object, TResult>)method.CreateDelegate(typeof(Func<object, TResult>));
+#endif
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static Func<object, TArg, TResult> CreateWeakInstanceMethodCallerFunc<TArg, TResult>(MethodInfo methodInfo)
+ {
+ if (methodInfo == null)
+ {
+ throw new ArgumentNullException("methodInfo");
+ }
+
+ if (methodInfo.IsStatic)
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' is static when it has to be an instance method.");
+ }
+
+ if (methodInfo.ReturnType != typeof(TResult))
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' must return type " + typeof(TResult) + ".");
+ }
+
+ var parameters = methodInfo.GetParameters();
+
+ if (parameters.Length != 1)
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' must have one parameter.");
+ }
+
+ if (!parameters[0].ParameterType.IsAssignableFrom(typeof(TArg)))
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' has an invalid parameter type.");
+ }
+
+ methodInfo = methodInfo.DeAliasMethod();
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return (object classInstance, TArg arg) =>
+ {
+ return (TResult)methodInfo.Invoke(classInstance, new object[] { arg });
+ };
+#else
+ Type declaringType = methodInfo.DeclaringType;
+ string methodName = methodInfo.ReflectedType.FullName + ".call_" + methodInfo.Name;
+
+ DynamicMethod method = new DynamicMethod(methodName, typeof(TResult), new Type[] { typeof(object), typeof(TArg) }, true);
+ ILGenerator gen = method.GetILGenerator();
+
+ if (declaringType.IsValueType)
+ {
+ var loc = gen.DeclareLocal(declaringType);
+
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Unbox_Any, declaringType);
+ gen.Emit(OpCodes.Stloc, loc);
+ gen.Emit(OpCodes.Ldloca_S, loc);
+ gen.Emit(OpCodes.Ldarg_1);
+ gen.Emit(OpCodes.Call, methodInfo);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Castclass, declaringType);
+ gen.Emit(OpCodes.Ldarg_1);
+ gen.Emit(OpCodes.Callvirt, methodInfo);
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (Func<object, TArg, TResult>)method.CreateDelegate(typeof(Func<object, TArg, TResult>));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a fast delegate method which calls a given parameterless instance method on a reference type.
+ /// </summary>
+ /// <typeparam name="InstanceType">The type of the class which the method is on.</typeparam>
+ /// <param name="methodInfo">The method info instance which is used.</param>
+ /// <returns>A delegate which calls the method and returns the result, except it's hundreds of times faster than MethodInfo.Invoke.</returns>
+ public static Action<InstanceType> CreateInstanceMethodCaller<InstanceType>(MethodInfo methodInfo)
+ {
+ if (methodInfo == null)
+ {
+ throw new ArgumentNullException("methodInfo");
+ }
+
+ if (methodInfo.IsStatic)
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' is static when it has to be an instance method.");
+ }
+
+ if (methodInfo.GetParameters().Length > 0)
+ {
+ throw new ArgumentException("Given method cannot have any parameters.");
+ }
+
+ if (typeof(InstanceType).IsValueType)
+ {
+ throw new ArgumentException("This method does not work with struct instances; please use CreateInstanceRefMethodCaller instead.");
+ }
+
+ methodInfo = methodInfo.DeAliasMethod();
+
+ // Luckily there's no need to emit this - we can just create a delegate and it's only ~10% slower than calling the method directly
+ // from normal compiled/emitted code. As opposed to using MethodInfo.Invoke, which is on average 600 (!!!) times slower.
+ return (Action<InstanceType>)Delegate.CreateDelegate(typeof(Action<InstanceType>), methodInfo);
+ }
+
+ /// <summary>
+ /// Creates a fast delegate method which calls a given instance method with a given argument on a reference type.
+ /// </summary>
+ /// <typeparam name="InstanceType">The type of the class which the method is on.</typeparam>
+ /// <typeparam name="Arg1">The type of the argument with which to call the method.</typeparam>
+ /// <param name="methodInfo">The method info instance which is used.</param>
+ /// <returns>A delegate which calls the method and returns the result, except it's hundreds of times faster than MethodInfo.Invoke.</returns>
+ public static Action<InstanceType, Arg1> CreateInstanceMethodCaller<InstanceType, Arg1>(MethodInfo methodInfo)
+ {
+ if (methodInfo == null)
+ {
+ throw new ArgumentNullException("methodInfo");
+ }
+
+ if (methodInfo.IsStatic)
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' is static when it has to be an instance method.");
+ }
+
+ if (methodInfo.GetParameters().Length != 1)
+ {
+ throw new ArgumentException("Given method must have only one parameter.");
+ }
+
+ if (typeof(InstanceType).IsValueType)
+ {
+ throw new ArgumentException("This method does not work with struct instances; please use CreateInstanceRefMethodCaller instead.");
+ }
+
+ methodInfo = methodInfo.DeAliasMethod();
+
+ // Luckily there's no need to emit this - we can just create a delegate and it's only ~10% slower than calling the method directly
+ // from normal compiled/emitted code. As opposed to using MethodInfo.Invoke, which is on average 600 (!!!) times slower.
+ return (Action<InstanceType, Arg1>)Delegate.CreateDelegate(typeof(Action<InstanceType, Arg1>), methodInfo);
+ }
+
+ public delegate void InstanceRefMethodCaller<InstanceType>(ref InstanceType instance);
+ public delegate void InstanceRefMethodCaller<InstanceType, TArg1>(ref InstanceType instance, TArg1 arg1);
+
+ /// <summary>
+ /// Creates a fast delegate method which calls a given parameterless instance method.
+ /// </summary>
+ /// <typeparam name="InstanceType">The type of the class which the method is on.</typeparam>
+ /// <param name="methodInfo">The method info instance which is used.</param>
+ /// <returns>A delegate which calls the method and returns the result, except it's hundreds of times faster than MethodInfo.Invoke.</returns>
+ public static InstanceRefMethodCaller<InstanceType> CreateInstanceRefMethodCaller<InstanceType>(MethodInfo methodInfo)
+ {
+ if (methodInfo == null)
+ {
+ throw new ArgumentNullException("methodInfo");
+ }
+
+ if (methodInfo.IsStatic)
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' is static when it has to be an instance method.");
+ }
+
+ if (methodInfo.GetParameters().Length > 0)
+ {
+ throw new ArgumentException("Given method cannot have any parameters.");
+ }
+
+ methodInfo = methodInfo.DeAliasMethod();
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return (ref InstanceType instance) =>
+ {
+ object obj = instance;
+ methodInfo.Invoke(obj, null);
+ instance = (InstanceType)obj;
+ };
+#else
+ Type declaringType = methodInfo.DeclaringType;
+ string methodName = methodInfo.ReflectedType.FullName + ".call_" + methodInfo.Name;
+
+ DynamicMethod method = new DynamicMethod(methodName, typeof(void), new Type[] { typeof(InstanceType).MakeByRefType() }, true);
+ ILGenerator gen = method.GetILGenerator();
+
+ if (declaringType.IsValueType)
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Call, methodInfo);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldind_Ref);
+ gen.Emit(OpCodes.Callvirt, methodInfo);
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (InstanceRefMethodCaller<InstanceType>)method.CreateDelegate(typeof(InstanceRefMethodCaller<InstanceType>));
+#endif
+ }
+
+ /// <summary>
+ /// Creates a fast delegate method which calls a given instance method with a given argument on a struct type.
+ /// </summary>
+ /// <typeparam name="InstanceType">The type of the class which the method is on.</typeparam>
+ /// <typeparam name="Arg1">The type of the argument with which to call the method.</typeparam>
+ /// <param name="methodInfo">The method info instance which is used.</param>
+ /// <returns>A delegate which calls the method and returns the result, except it's hundreds of times faster than MethodInfo.Invoke.</returns>
+ public static InstanceRefMethodCaller<InstanceType, Arg1> CreateInstanceRefMethodCaller<InstanceType, Arg1>(MethodInfo methodInfo)
+ {
+ if (methodInfo == null)
+ {
+ throw new ArgumentNullException("methodInfo");
+ }
+
+ if (methodInfo.IsStatic)
+ {
+ throw new ArgumentException("Given method '" + methodInfo.Name + "' is static when it has to be an instance method.");
+ }
+
+ if (methodInfo.GetParameters().Length != 1)
+ {
+ throw new ArgumentException("Given method must have only one parameter.");
+ }
+
+ methodInfo = methodInfo.DeAliasMethod();
+
+#if !CAN_EMIT
+ // Platform does not support emitting dynamic code
+ return (ref InstanceType instance, Arg1 arg1) =>
+ {
+ object obj = instance;
+ methodInfo.Invoke(obj, new object[] { arg1 });
+ instance = (InstanceType)obj;
+ };
+#else
+ Type declaringType = methodInfo.DeclaringType;
+ string methodName = methodInfo.ReflectedType.FullName + ".call_" + methodInfo.Name;
+
+ DynamicMethod method = new DynamicMethod(methodName, typeof(void), new Type[] { typeof(InstanceType).MakeByRefType(), typeof(Arg1) }, true);
+ ILGenerator gen = method.GetILGenerator();
+
+ if (declaringType.IsValueType)
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldarg_1);
+ gen.Emit(OpCodes.Call, methodInfo);
+ }
+ else
+ {
+ gen.Emit(OpCodes.Ldarg_0);
+ gen.Emit(OpCodes.Ldind_Ref);
+ gen.Emit(OpCodes.Ldarg_1);
+ gen.Emit(OpCodes.Callvirt, methodInfo);
+ }
+
+ gen.Emit(OpCodes.Ret);
+
+ return (InstanceRefMethodCaller<InstanceType, Arg1>)method.CreateDelegate(typeof(InstanceRefMethodCaller<InstanceType, Arg1>));
+#endif
+ }
+ }
+}
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/EmitUtilities.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/EmitUtilities.cs.meta
new file mode 100644
index 00000000..fb5d69a9
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/EmitUtilities.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: bda92ec6156282448e883bf8f6a781fd
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/FastTypeComparer.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/FastTypeComparer.cs
new file mode 100644
index 00000000..e53098b5
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/FastTypeComparer.cs
@@ -0,0 +1,44 @@
+//-----------------------------------------------------------------------
+// <copyright file="FastTypeComparer.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>
+ /// Compares types by reference before comparing them using the default type equality operator.
+ /// This can constitute a *significant* speedup when used as the comparer for dictionaries.
+ /// </summary>
+ /// <seealso cref="System.Collections.Generic.IEqualityComparer{System.Type}" />
+ public class FastTypeComparer : IEqualityComparer<Type>
+ {
+ public static readonly FastTypeComparer Instance = new FastTypeComparer();
+
+ public bool Equals(Type x, Type y)
+ {
+ if (object.ReferenceEquals(x, y)) return true; // Oft-used fast path over regular Type.Equals makes this much faster
+ return x == y;
+ }
+
+ public int GetHashCode(Type obj)
+ {
+ return obj.GetHashCode();
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/FastTypeComparer.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/FastTypeComparer.cs.meta
new file mode 100644
index 00000000..ce4a4497
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/FastTypeComparer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 570028979953bd2c60b7e89ff7cef92e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Flags.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Flags.cs
new file mode 100644
index 00000000..7876a6ba
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Flags.cs
@@ -0,0 +1,108 @@
+//-----------------------------------------------------------------------
+// <copyright file="Flags.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.Text;
+
+ /// <summary>
+ /// This class encapsulates common <see cref="BindingFlags"/> combinations.
+ /// </summary>
+ public static class Flags
+ {
+ /// <summary>
+ /// Search criteria encompassing all public and non-public members, including base members.
+ /// Note that you also need to specify either the Instance or Static flag.
+ /// </summary>
+ public const BindingFlags AnyVisibility = BindingFlags.Public | BindingFlags.NonPublic;
+
+ /// <summary>
+ /// Search criteria encompassing all public instance members, including base members.
+ /// </summary>
+ public const BindingFlags InstancePublic = BindingFlags.Public | BindingFlags.Instance;
+
+ /// <summary>
+ /// Search criteria encompassing all non-public instance members, including base members.
+ /// </summary>
+ public const BindingFlags InstancePrivate = BindingFlags.NonPublic | BindingFlags.Instance;
+
+ /// <summary>
+ /// Search criteria encompassing all public and non-public instance members, including base members.
+ /// </summary>
+ public const BindingFlags InstanceAnyVisibility = AnyVisibility | BindingFlags.Instance;
+
+ /// <summary>
+ /// Search criteria encompassing all public static members, including base members.
+ /// </summary>
+ public const BindingFlags StaticPublic = BindingFlags.Public | BindingFlags.Static;
+
+ /// <summary>
+ /// Search criteria encompassing all non-public static members, including base members.
+ /// </summary>
+ public const BindingFlags StaticPrivate = BindingFlags.NonPublic | BindingFlags.Static;
+
+ /// <summary>
+ /// Search criteria encompassing all public and non-public static members, including base members.
+ /// </summary>
+ public const BindingFlags StaticAnyVisibility = AnyVisibility | BindingFlags.Static;
+
+ /// <summary>
+ /// Search criteria encompassing all public instance members, excluding base members.
+ /// </summary>
+ public const BindingFlags InstancePublicDeclaredOnly = InstancePublic | BindingFlags.DeclaredOnly;
+
+ /// <summary>
+ /// Search criteria encompassing all non-public instance members, excluding base members.
+ /// </summary>
+ public const BindingFlags InstancePrivateDeclaredOnly = InstancePrivate | BindingFlags.DeclaredOnly;
+
+ /// <summary>
+ /// Search criteria encompassing all public and non-public instance members, excluding base members.
+ /// </summary>
+ public const BindingFlags InstanceAnyDeclaredOnly = InstanceAnyVisibility | BindingFlags.DeclaredOnly;
+
+ /// <summary>
+ /// Search criteria encompassing all public static members, excluding base members.
+ /// </summary>
+ public const BindingFlags StaticPublicDeclaredOnly = StaticPublic | BindingFlags.DeclaredOnly;
+
+ /// <summary>
+ /// Search criteria encompassing all non-public static members, excluding base members.
+ /// </summary>
+ public const BindingFlags StaticPrivateDeclaredOnly = StaticPrivate | BindingFlags.DeclaredOnly;
+
+ /// <summary>
+ /// Search criteria encompassing all public and non-public static members, excluding base members.
+ /// </summary>
+ public const BindingFlags StaticAnyDeclaredOnly = StaticAnyVisibility | BindingFlags.DeclaredOnly;
+
+ /// <summary>
+ /// Search criteria encompassing all members, including base and static members.
+ /// </summary>
+ public const BindingFlags StaticInstanceAnyVisibility = InstanceAnyVisibility | BindingFlags.Static;
+
+ /// <summary>
+ /// Search criteria encompassing all members (public and non-public, instance and static), including base members.
+ /// </summary>
+ public const BindingFlags AllMembers = StaticInstanceAnyVisibility | BindingFlags.FlattenHierarchy;
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Flags.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Flags.cs.meta
new file mode 100644
index 00000000..e7b1d3c6
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Flags.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 42e5d977e21c7a6524213a8a7dbee24a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ICacheNotificationReceiver.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ICacheNotificationReceiver.cs
new file mode 100644
index 00000000..5d5d3eca
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ICacheNotificationReceiver.cs
@@ -0,0 +1,36 @@
+//-----------------------------------------------------------------------
+// <copyright file="ICacheNotificationReceiver.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>
+ /// Provides notification callbacks for values that are cached using the <see cref="Cache{T}"/> class.
+ /// </summary>
+ public interface ICacheNotificationReceiver
+ {
+ /// <summary>
+ /// Called when the cached value is freed.
+ /// </summary>
+ void OnFreed();
+
+ /// <summary>
+ /// Called when the cached value is claimed.
+ /// </summary>
+ void OnClaimed();
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ICacheNotificationReceiver.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ICacheNotificationReceiver.cs.meta
new file mode 100644
index 00000000..18129377
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ICacheNotificationReceiver.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 783316da32d87acfae14953e341732a3
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ImmutableList.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ImmutableList.cs
new file mode 100644
index 00000000..60eae3c5
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ImmutableList.cs
@@ -0,0 +1,605 @@
+//-----------------------------------------------------------------------
+// <copyright file="ImmutableList.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;
+ using System.Collections.Generic;
+ using UnityEngine;
+
+ /// <summary>
+ /// Interface for immutable list.
+ /// </summary>
+ public interface IImmutableList : IList
+ {
+ }
+
+ /// <summary>
+ /// Interface for generic immutable list.
+ /// </summary>
+ public interface IImmutableList<T> : IImmutableList, IList<T>
+ {
+ /// <summary>
+ /// Index accessor.
+ /// </summary>
+ new T this[int index] { get; }
+ }
+
+ /// <summary>
+ /// Immutable list wraps another list, and allows for reading the inner list, without the ability to change it.
+ /// </summary>
+ [Serializable]
+ public sealed class ImmutableList : IImmutableList<object>
+ {
+ [SerializeField]
+ private IList innerList;
+
+ /// <summary>
+ /// Creates an immutable list around another list.
+ /// </summary>
+ public ImmutableList(IList innerList)
+ {
+ if (innerList == null)
+ {
+ throw new ArgumentNullException("innerList");
+ }
+
+ this.innerList = innerList;
+ }
+
+ /// <summary>
+ /// Number of items in the list.
+ /// </summary>
+ public int Count { get { return this.innerList.Count; } }
+
+ /// <summary>
+ /// Immutable list cannot be changed directly, so it's size is always fixed.
+ /// </summary>
+ public bool IsFixedSize { get { return true; } }
+
+ /// <summary>
+ /// Immutable list are always readonly.
+ /// </summary>
+ public bool IsReadOnly { get { return true; } }
+
+ /// <summary>
+ /// Returns <c>true</c> if the inner list is synchronized.
+ /// </summary>
+ public bool IsSynchronized { get { return this.innerList.IsSynchronized; } }
+
+ /// <summary>
+ /// Gets the sync root object.
+ /// </summary>
+ public object SyncRoot { get { return this.innerList.SyncRoot; } }
+
+ object IList.this[int index]
+ {
+ get
+ {
+ return this.innerList[index];
+ }
+
+ set
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+ }
+
+ object IList<object>.this[int index]
+ {
+ get
+ {
+ return this.innerList[index];
+ }
+
+ set
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+ }
+
+ /// <summary>
+ /// Index accessor.
+ /// </summary>
+ /// <param name="index">Index.</param>
+ public object this[int index] { get { return this.innerList[index]; } }
+
+ /// <summary>
+ /// Returns <c>true</c> if the item is contained in the list.
+ /// </summary>
+ /// <param name="value">The item's value.</param>
+ public bool Contains(object value)
+ {
+ return this.innerList.Contains(value);
+ }
+
+ /// <summary>
+ /// Copy the list to an array,
+ /// </summary>
+ /// <param name="array">Target array.</param>
+ /// <param name="arrayIndex">Index.</param>
+ public void CopyTo(object[] array, int arrayIndex)
+ {
+ this.innerList.CopyTo(array, arrayIndex);
+ }
+
+ /// <summary>
+ /// Copy the list to an array,
+ /// </summary>
+ /// <param name="array">Target array.</param>
+ /// <param name="index">Index.</param>
+ public void CopyTo(Array array, int index)
+ {
+ this.innerList.CopyTo(array, index);
+ }
+
+ /// <summary>
+ /// Gets an enumerator.
+ /// </summary>
+ public IEnumerator GetEnumerator()
+ {
+ return this.innerList.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+
+ IEnumerator<object> IEnumerable<object>.GetEnumerator()
+ {
+ foreach (var obj in this.innerList)
+ {
+ yield return obj;
+ }
+ }
+
+ int IList.Add(object value)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ void IList.Clear()
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ void IList.Insert(int index, object value)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ void IList.Remove(object value)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ void IList.RemoveAt(int index)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ /// <summary>
+ /// Get the index of a value.
+ /// </summary>
+ /// <param name="value">The item's value.</param>
+ public int IndexOf(object value)
+ {
+ return this.innerList.IndexOf(value);
+ }
+
+ /// <summary>
+ /// Immutable list cannot be edited.
+ /// </summary>
+ /// <param name="index">Index.</param>
+ void IList<object>.RemoveAt(int index)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ /// <summary>
+ /// Immutable list cannot be edited.
+ /// </summary>
+ /// <param name="index">Index.</param>
+ /// <param name="item">Item.</param>
+ void IList<object>.Insert(int index, object item)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ /// <summary>
+ /// Immutable list cannot be edited.
+ /// </summary>
+ /// <param name="item">Item.</param>
+ void ICollection<object>.Add(object item)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ /// <summary>
+ /// Immutable list cannot be edited.
+ /// </summary>
+ void ICollection<object>.Clear()
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ /// <summary>
+ /// Immutable list cannot be edited.
+ /// </summary>
+ /// <param name="item">Item.</param>
+ bool ICollection<object>.Remove(object item)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ [Serializable]
+ public sealed class ImmutableList<T> : IImmutableList<T>
+ {
+ [SerializeField]
+ private IList<T> innerList;
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public ImmutableList(IList<T> innerList)
+ {
+ if (innerList == null)
+ {
+ throw new ArgumentNullException("innerList");
+ }
+
+ this.innerList = innerList;
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public int Count { get { return this.innerList.Count; } }
+
+ bool ICollection.IsSynchronized { get { return false; } }
+ object ICollection.SyncRoot { get { return null; } }
+ bool IList.IsFixedSize { get { return true; } }
+ bool IList.IsReadOnly { get { return true; } }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public bool IsReadOnly { get { return true; } }
+
+ object IList.this[int index]
+ {
+ get
+ {
+ return this[index];
+ }
+
+ set
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+ }
+
+ T IList<T>.this[int index]
+ {
+ get
+ {
+ return this.innerList[index];
+ }
+
+ set
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public T this[int index]
+ {
+ get
+ {
+ return this.innerList[index];
+ }
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public bool Contains(T item)
+ {
+ return this.innerList.Contains(item);
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ this.innerList.CopyTo(array, arrayIndex);
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public IEnumerator<T> GetEnumerator()
+ {
+ return this.innerList.GetEnumerator();
+ }
+
+ void ICollection.CopyTo(Array array, int index)
+ {
+ this.innerList.CopyTo((T[])array, index);
+ }
+
+ void ICollection<T>.Add(T item)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ void ICollection<T>.Clear()
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ bool ICollection<T>.Remove(T item)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+
+ int IList.Add(object value)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ void IList.Clear()
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ bool IList.Contains(object value)
+ {
+ return this.innerList.Contains((T)value);
+ }
+
+ int IList.IndexOf(object value)
+ {
+ return this.innerList.IndexOf((T)value);
+ }
+
+ void IList.Insert(int index, object value)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ void IList.Remove(object value)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ void IList<T>.Insert(int index, T item)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ void IList.RemoveAt(int index)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public int IndexOf(T item)
+ {
+ return this.innerList.IndexOf(item);
+ }
+
+ void IList<T>.RemoveAt(int index)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+ }
+
+ /// <summary>
+ /// Immutable list wraps another list, and allows for reading the inner list, without the ability to change it.
+ /// </summary>
+ [Serializable]
+ public sealed class ImmutableList<TList, TElement> : IImmutableList<TElement> where TList : IList<TElement>
+ {
+ private TList innerList;
+
+ /// <summary>
+ /// Creates an immutable list around another list.
+ /// </summary>
+ public ImmutableList(TList innerList)
+ {
+ if (innerList == null)
+ {
+ throw new ArgumentNullException("innerList");
+ }
+
+ this.innerList = innerList;
+ }
+
+ /// <summary>
+ /// Number of items in the list.
+ /// </summary>
+ public int Count { get { return this.innerList.Count; } }
+
+ bool ICollection.IsSynchronized { get { return false; } }
+ object ICollection.SyncRoot { get { return null; } }
+ bool IList.IsFixedSize { get { return true; } }
+ bool IList.IsReadOnly { get { return true; } }
+
+ /// <summary>
+ /// Immutable list are always readonly.
+ /// </summary>
+ public bool IsReadOnly { get { return true; } }
+
+ object IList.this[int index]
+ {
+ get
+ {
+ return this[index];
+ }
+
+ set
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+ }
+
+ TElement IList<TElement>.this[int index]
+ {
+ get
+ {
+ return this.innerList[index];
+ }
+
+ set
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+ }
+
+ /// <summary>
+ /// Index accessor.
+ /// </summary>
+ /// <param name="index">Index.</param>
+ public TElement this[int index]
+ {
+ get
+ {
+ return this.innerList[index];
+ }
+ }
+
+ /// <summary>
+ /// Returns <c>true</c> if the item is contained in the list.
+ /// </summary>
+ public bool Contains(TElement item)
+ {
+ return this.innerList.Contains(item);
+ }
+
+ /// <summary>
+ /// Copies the list to an array.
+ /// </summary>
+ public void CopyTo(TElement[] array, int arrayIndex)
+ {
+ this.innerList.CopyTo(array, arrayIndex);
+ }
+
+ /// <summary>
+ /// Gets an enumerator.
+ /// </summary>
+ public IEnumerator<TElement> GetEnumerator()
+ {
+ return this.innerList.GetEnumerator();
+ }
+
+ void ICollection.CopyTo(Array array, int index)
+ {
+ this.innerList.CopyTo((TElement[])array, index);
+ }
+
+ void ICollection<TElement>.Add(TElement item)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ void ICollection<TElement>.Clear()
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ bool ICollection<TElement>.Remove(TElement item)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+
+ int IList.Add(object value)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ void IList.Clear()
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ bool IList.Contains(object value)
+ {
+ return this.innerList.Contains((TElement)value);
+ }
+
+ int IList.IndexOf(object value)
+ {
+ return this.innerList.IndexOf((TElement)value);
+ }
+
+ void IList.Insert(int index, object value)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ void IList.Remove(object value)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ void IList<TElement>.Insert(int index, TElement item)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ void IList.RemoveAt(int index)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+
+ /// <summary>
+ /// Gets the index of an item.
+ /// </summary>
+ public int IndexOf(TElement item)
+ {
+ return this.innerList.IndexOf(item);
+ }
+
+ void IList<TElement>.RemoveAt(int index)
+ {
+ throw new NotSupportedException("Immutable Lists cannot be edited.");
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ImmutableList.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ImmutableList.cs.meta
new file mode 100644
index 00000000..cfcee8c9
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ImmutableList.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1bc635f3755c60fe69f1895dd53974e2
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasFieldInfo.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasFieldInfo.cs
new file mode 100644
index 00000000..a1230b37
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasFieldInfo.cs
@@ -0,0 +1,179 @@
+//-----------------------------------------------------------------------
+// <copyright file="MemberAliasFieldInfo.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.Reflection;
+
+ /// <summary>
+ /// Provides a methods of representing imaginary fields which are unique to serialization.
+ /// <para />
+ /// We aggregate the FieldInfo associated with this member and return a mangled form of the name.
+ /// </summary>
+ /// <seealso cref="System.Reflection.FieldInfo" />
+ public sealed class MemberAliasFieldInfo : FieldInfo
+ {
+ /// <summary>
+ /// The default fake name separator string.
+ /// </summary>
+ private const string FAKE_NAME_SEPARATOR_STRING = "+";
+
+ private FieldInfo aliasedField;
+ private string mangledName;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MemberAliasFieldInfo"/> class.
+ /// </summary>
+ /// <param name="field">The field to alias.</param>
+ /// <param name="namePrefix">The name prefix to use.</param>
+ public MemberAliasFieldInfo(FieldInfo field, string namePrefix)
+ {
+ this.aliasedField = field;
+ this.mangledName = string.Concat(namePrefix, FAKE_NAME_SEPARATOR_STRING, this.aliasedField.Name);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MemberAliasFieldInfo"/> class.
+ /// </summary>
+ /// <param name="field">The field to alias.</param>
+ /// <param name="namePrefix">The name prefix to use.</param>
+ /// <param name="separatorString">The separator string to use.</param>
+ public MemberAliasFieldInfo(FieldInfo field, string namePrefix, string separatorString)
+ {
+ this.aliasedField = field;
+ this.mangledName = string.Concat(namePrefix, separatorString, this.aliasedField.Name);
+ }
+
+ /// <summary>
+ /// Gets the aliased field.
+ /// </summary>
+ /// <value>
+ /// The aliased field.
+ /// </value>
+ public FieldInfo AliasedField { get { return this.aliasedField; } }
+
+ /// <summary>
+ /// Gets the module in which the type that declares the member represented by the current <see cref="T:System.Reflection.MemberInfo" /> is defined.
+ /// </summary>
+ public override Module Module { get { return this.aliasedField.Module; } }
+
+ /// <summary>
+ /// Gets a value that identifies a metadata element.
+ /// </summary>
+ public override int MetadataToken { get { return this.aliasedField.MetadataToken; } }
+
+ /// <summary>
+ /// Gets the name of the current member.
+ /// </summary>
+ public override string Name { get { return this.mangledName; } }
+
+ /// <summary>
+ /// Gets the class that declares this member.
+ /// </summary>
+ public override Type DeclaringType { get { return this.aliasedField.DeclaringType; } }
+
+ /// <summary>
+ /// Gets the class object that was used to obtain this instance of MemberInfo.
+ /// </summary>
+ public override Type ReflectedType { get { return this.aliasedField.ReflectedType; } }
+
+ /// <summary>
+ /// Gets the type of the field.
+ /// </summary>
+ /// <value>
+ /// The type of the field.
+ /// </value>
+ public override Type FieldType { get { return this.aliasedField.FieldType; } }
+
+ /// <summary>
+ /// Gets a RuntimeFieldHandle, which is a handle to the internal metadata representation of a field.
+ /// </summary>
+ public override RuntimeFieldHandle FieldHandle { get { return this.aliasedField.FieldHandle; } }
+
+ /// <summary>
+ /// Gets the attributes.
+ /// </summary>
+ /// <value>
+ /// The attributes.
+ /// </value>
+ public override FieldAttributes Attributes { get { return this.aliasedField.Attributes; } }
+
+ /// <summary>
+ /// When overridden in a derived class, returns an array of all custom attributes applied to this member.
+ /// </summary>
+ /// <param name="inherit">True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
+ /// <returns>
+ /// An array that contains all the custom attributes applied to this member, or an array with zero elements if no attributes are defined.
+ /// </returns>
+ public override object[] GetCustomAttributes(bool inherit)
+ {
+ return this.aliasedField.GetCustomAttributes(inherit);
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, returns an array of custom attributes applied to this member and identified by <see cref="T:System.Type" />.
+ /// </summary>
+ /// <param name="attributeType">The type of attribute to search for. Only attributes that are assignable to this type are returned.</param>
+ /// <param name="inherit">True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
+ /// <returns>
+ /// An array of custom attributes applied to this member, or an array with zero elements if no attributes assignable to <paramref name="attributeType" /> have been applied.
+ /// </returns>
+ public override object[] GetCustomAttributes(Type attributeType, bool inherit)
+ {
+ return this.aliasedField.GetCustomAttributes(attributeType, inherit);
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, indicates whether one or more attributes of the specified type or of its derived types is applied to this member.
+ /// </summary>
+ /// <param name="attributeType">The type of custom attribute to search for. The search includes derived types.</param>
+ /// <param name="inherit">True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
+ /// <returns>
+ /// True if one or more instances of <paramref name="attributeType" /> or any of its derived types is applied to this member; otherwise, false.
+ /// </returns>
+ public override bool IsDefined(Type attributeType, bool inherit)
+ {
+ return this.aliasedField.IsDefined(attributeType, inherit);
+ }
+
+ /// <summary>
+ /// Gets the value of the field.
+ /// </summary>
+ /// <param name="obj">The object instance to get the value from.</param>
+ /// <returns>The value of the field.</returns>
+ public override object GetValue(object obj)
+ {
+ return this.aliasedField.GetValue(obj);
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, sets the value of the field supported by the given object.
+ /// </summary>
+ /// <param name="obj">The object whose field value will be set.</param>
+ /// <param name="value">The value to assign to the field.</param>
+ /// <param name="invokeAttr">A field of Binder that specifies the type of binding that is desired (for example, Binder.CreateInstance or Binder.ExactBinding).</param>
+ /// <param name="binder">A set of properties that enables the binding, coercion of argument types, and invocation of members through reflection. If <paramref name="binder" /> is null, then Binder.DefaultBinding is used.</param>
+ /// <param name="culture">The software preferences of a particular culture.</param>
+ public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture)
+ {
+ this.aliasedField.SetValue(obj, value, invokeAttr, binder, culture);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasFieldInfo.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasFieldInfo.cs.meta
new file mode 100644
index 00000000..591dfd06
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasFieldInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 000592e93b119574207ea3bf59f659e4
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasMethodInfo.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasMethodInfo.cs
new file mode 100644
index 00000000..80253fb1
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasMethodInfo.cs
@@ -0,0 +1,194 @@
+//-----------------------------------------------------------------------
+// <copyright file="MemberAliasMethodInfo.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.Reflection;
+
+ /// <summary>
+ /// Provides a methods of representing aliased methods.
+ /// <para />
+ /// In this case, what we're representing is a method on a parent class with the same name.
+ /// <para />
+ /// We aggregate the MethodInfo associated with this member and return a mangled form of the name.
+ /// The name that we return is "parentname+methodName".
+ /// </summary>
+ /// <seealso cref="System.Reflection.FieldInfo" />
+ public sealed class MemberAliasMethodInfo : MethodInfo
+ {
+ /// <summary>
+ /// The default fake name separator string.
+ /// </summary>
+ private const string FAKE_NAME_SEPARATOR_STRING = "+";
+
+ private MethodInfo aliasedMethod;
+ private string mangledName;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MemberAliasMethodInfo"/> class.
+ /// </summary>
+ /// <param name="method">The method to alias.</param>
+ /// <param name="namePrefix">The name prefix to use.</param>
+ public MemberAliasMethodInfo(MethodInfo method, string namePrefix)
+ {
+ this.aliasedMethod = method;
+ this.mangledName = string.Concat(namePrefix, FAKE_NAME_SEPARATOR_STRING, this.aliasedMethod.Name);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MemberAliasMethodInfo"/> class.
+ /// </summary>
+ /// <param name="method">The method to alias.</param>
+ /// <param name="namePrefix">The name prefix to use.</param>
+ /// <param name="separatorString">The separator string to use.</param>
+ public MemberAliasMethodInfo(MethodInfo method, string namePrefix, string separatorString)
+ {
+ this.aliasedMethod = method;
+ this.mangledName = string.Concat(namePrefix, separatorString, this.aliasedMethod.Name);
+ }
+
+ /// <summary>
+ /// Gets the aliased method.
+ /// </summary>
+ /// <value>
+ /// The aliased method.
+ /// </value>
+ public MethodInfo AliasedMethod { get { return this.aliasedMethod; } }
+
+ /// <summary>
+ /// Gets the custom attributes for the return type.
+ /// </summary>
+ public override ICustomAttributeProvider ReturnTypeCustomAttributes { get { return this.aliasedMethod.ReturnTypeCustomAttributes; } }
+
+ /// <summary>
+ /// Gets a handle to the internal metadata representation of a method.
+ /// </summary>
+ public override RuntimeMethodHandle MethodHandle { get { return this.aliasedMethod.MethodHandle; } }
+
+ /// <summary>
+ /// Gets the attributes associated with this method.
+ /// </summary>
+ public override MethodAttributes Attributes { get { return this.aliasedMethod.Attributes; } }
+
+ public override Type ReturnType { get { return this.aliasedMethod.ReturnType; } }
+
+ /// <summary>
+ /// Gets the class that declares this member.
+ /// </summary>
+ public override Type DeclaringType { get { return this.aliasedMethod.DeclaringType; } }
+
+ /// <summary>
+ /// Gets the name of the current member.
+ /// </summary>
+ public override string Name { get { return this.mangledName; } }
+
+ /// <summary>
+ /// Gets the class object that was used to obtain this instance of MemberInfo.
+ /// </summary>
+ public override Type ReflectedType { get { return this.aliasedMethod.ReflectedType; } }
+
+ /// <summary>
+ /// When overridden in a derived class, returns the MethodInfo object for the method on the direct or indirect base class in which the method represented by this instance was first declared.
+ /// </summary>
+ /// <returns>
+ /// A MethodInfo object for the first implementation of this method.
+ /// </returns>
+ public override MethodInfo GetBaseDefinition()
+ {
+ return this.aliasedMethod.GetBaseDefinition();
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, returns an array of all custom attributes applied to this member.
+ /// </summary>
+ /// <param name="inherit">true to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
+ /// <returns>
+ /// An array that contains all the custom attributes applied to this member, or an array with zero elements if no attributes are defined.
+ /// </returns>
+ public override object[] GetCustomAttributes(bool inherit)
+ {
+ return this.aliasedMethod.GetCustomAttributes(inherit);
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, returns an array of custom attributes applied to this member and identified by <see cref="T:System.Type" />.
+ /// </summary>
+ /// <param name="attributeType">The type of attribute to search for. Only attributes that are assignable to this type are returned.</param>
+ /// <param name="inherit">true to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
+ /// <returns>
+ /// An array of custom attributes applied to this member, or an array with zero elements if no attributes assignable to <paramref name="attributeType" /> have been applied.
+ /// </returns>
+ public override object[] GetCustomAttributes(Type attributeType, bool inherit)
+ {
+ return this.aliasedMethod.GetCustomAttributes(attributeType, inherit);
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, returns the <see cref="T:System.Reflection.MethodImplAttributes" /> flags.
+ /// </summary>
+ /// <returns>
+ /// The MethodImplAttributes flags.
+ /// </returns>
+ public override MethodImplAttributes GetMethodImplementationFlags()
+ {
+ return this.aliasedMethod.GetMethodImplementationFlags();
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, gets the parameters of the specified method or constructor.
+ /// </summary>
+ /// <returns>
+ /// An array of type ParameterInfo containing information that matches the signature of the method (or constructor) reflected by this MethodBase instance.
+ /// </returns>
+ public override ParameterInfo[] GetParameters()
+ {
+ return this.aliasedMethod.GetParameters();
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, invokes the reflected method or constructor with the given parameters.
+ /// </summary>
+ /// <param name="obj">The object on which to invoke the method or constructor. If a method is static, this argument is ignored. If a constructor is static, this argument must be null or an instance of the class that defines the constructor.</param>
+ /// <param name="invokeAttr">A bitmask that is a combination of 0 or more bit flags from <see cref="T:System.Reflection.BindingFlags" />. If <paramref name="binder" /> is null, this parameter is assigned the value <see cref="F:System.Reflection.BindingFlags.Default" />; thus, whatever you pass in is ignored.</param>
+ /// <param name="binder">An object that enables the binding, coercion of argument types, invocation of members, and retrieval of MemberInfo objects via reflection. If <paramref name="binder" /> is null, the default binder is used.</param>
+ /// <param name="parameters">An argument list for the invoked method or constructor. This is an array of objects with the same number, order, and type as the parameters of the method or constructor to be invoked. If there are no parameters, this should be null.If the method or constructor represented by this instance takes a ByRef parameter, there is no special attribute required for that parameter in order to invoke the method or constructor using this function. Any object in this array that is not explicitly initialized with a value will contain the default value for that object type. For reference-type elements, this value is null. For value-type elements, this value is 0, 0.0, or false, depending on the specific element type.</param>
+ /// <param name="culture">An instance of CultureInfo used to govern the coercion of types. If this is null, the CultureInfo for the current thread is used. (This is necessary to convert a String that represents 1000 to a Double value, for example, since 1000 is represented differently by different cultures.)</param>
+ /// <returns>
+ /// An Object containing the return value of the invoked method, or null in the case of a constructor, or null if the method's return type is void. Before calling the method or constructor, Invoke checks to see if the user has access permission and verifies that the parameters are valid.CautionElements of the <paramref name="parameters" /> array that represent parameters declared with the ref or out keyword may also be modified.
+ /// </returns>
+ public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)
+ {
+ return this.aliasedMethod.Invoke(obj, invokeAttr, binder, parameters, culture);
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, indicates whether one or more attributes of the specified type or of its derived types is applied to this member.
+ /// </summary>
+ /// <param name="attributeType">The type of custom attribute to search for. The search includes derived types.</param>
+ /// <param name="inherit">true to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
+ /// <returns>
+ /// true if one or more instances of <paramref name="attributeType" /> or any of its derived types is applied to this member; otherwise, false.
+ /// </returns>
+ public override bool IsDefined(Type attributeType, bool inherit)
+ {
+ return this.aliasedMethod.IsDefined(attributeType, inherit);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasMethodInfo.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasMethodInfo.cs.meta
new file mode 100644
index 00000000..1ea42af1
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasMethodInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c1e85c1ef449ccb40e05f0afd3dd717f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasPropertyInfo.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasPropertyInfo.cs
new file mode 100644
index 00000000..6e6db9c0
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasPropertyInfo.cs
@@ -0,0 +1,237 @@
+//-----------------------------------------------------------------------
+// <copyright file="MemberAliasPropertyInfo.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.Reflection;
+
+ /// <summary>
+ /// Provides a methods of representing imaginary properties which are unique to serialization.
+ /// <para />
+ /// We aggregate the PropertyInfo associated with this member and return a mangled form of the name.
+ /// </summary>
+ /// <seealso cref="System.Reflection.FieldInfo" />
+ public sealed class MemberAliasPropertyInfo : PropertyInfo
+ {
+ /// <summary>
+ /// The default fake name separator string.
+ /// </summary>
+ private const string FakeNameSeparatorString = "+";
+
+ private PropertyInfo aliasedProperty;
+ private string mangledName;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MemberAliasPropertyInfo"/> class.
+ /// </summary>
+ /// <param name="prop">The property to alias.</param>
+ /// <param name="namePrefix">The name prefix to use.</param>
+ public MemberAliasPropertyInfo(PropertyInfo prop, string namePrefix)
+ {
+ this.aliasedProperty = prop;
+ this.mangledName = string.Concat(namePrefix, FakeNameSeparatorString, this.aliasedProperty.Name);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MemberAliasPropertyInfo"/> class.
+ /// </summary>
+ /// <param name="prop">The property to alias.</param>
+ /// <param name="namePrefix">The name prefix to use.</param>
+ /// <param name="separatorString">The separator string to use.</param>
+ public MemberAliasPropertyInfo(PropertyInfo prop, string namePrefix, string separatorString)
+ {
+ this.aliasedProperty = prop;
+ this.mangledName = string.Concat(namePrefix, separatorString, this.aliasedProperty.Name);
+ }
+
+ /// <summary>
+ /// The backing PropertyInfo that is being aliased.
+ /// </summary>
+ public PropertyInfo AliasedProperty { get { return this.aliasedProperty; } }
+
+ /// <summary>
+ /// Gets the module in which the type that declares the member represented by the current <see cref="T:System.Reflection.MemberInfo" /> is defined.
+ /// </summary>
+ public override Module Module { get { return this.aliasedProperty.Module; } }
+
+ /// <summary>
+ /// Gets a value that identifies a metadata element.
+ /// </summary>
+ public override int MetadataToken { get { return this.aliasedProperty.MetadataToken; } }
+
+ /// <summary>
+ /// Gets the name of the current member.
+ /// </summary>
+ public override string Name { get { return this.mangledName; } }
+
+ /// <summary>
+ /// Gets the class that declares this member.
+ /// </summary>
+ public override Type DeclaringType { get { return this.aliasedProperty.DeclaringType; } }
+
+ /// <summary>
+ /// Gets the class object that was used to obtain this instance of MemberInfo.
+ /// </summary>
+ public override Type ReflectedType { get { return this.aliasedProperty.ReflectedType; } }
+
+ /// <summary>
+ /// Gets the type of the property.
+ /// </summary>
+ /// <value>
+ /// The type of the property.
+ /// </value>
+ public override Type PropertyType { get { return this.aliasedProperty.PropertyType; } }
+
+ /// <summary>
+ /// Gets the attributes.
+ /// </summary>
+ /// <value>
+ /// The attributes.
+ /// </value>
+ public override PropertyAttributes Attributes { get { return this.aliasedProperty.Attributes; } }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance can read.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this instance can read; otherwise, <c>false</c>.
+ /// </value>
+ public override bool CanRead { get { return this.aliasedProperty.CanRead; } }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance can write.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if this instance can write; otherwise, <c>false</c>.
+ /// </value>
+ public override bool CanWrite { get { return this.aliasedProperty.CanWrite; } }
+
+ /// <summary>
+ /// When overridden in a derived class, returns an array of all custom attributes applied to this member.
+ /// </summary>
+ /// <param name="inherit">True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
+ /// <returns>
+ /// An array that contains all the custom attributes applied to this member, or an array with zero elements if no attributes are defined.
+ /// </returns>
+ public override object[] GetCustomAttributes(bool inherit)
+ {
+ return this.aliasedProperty.GetCustomAttributes(inherit);
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, returns an array of custom attributes applied to this member and identified by <see cref="T:System.Type" />.
+ /// </summary>
+ /// <param name="attributeType">The type of attribute to search for. Only attributes that are assignable to this type are returned.</param>
+ /// <param name="inherit">True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
+ /// <returns>
+ /// An array of custom attributes applied to this member, or an array with zero elements if no attributes assignable to <paramref name="attributeType" /> have been applied.
+ /// </returns>
+ public override object[] GetCustomAttributes(Type attributeType, bool inherit)
+ {
+ return this.aliasedProperty.GetCustomAttributes(attributeType, inherit);
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, indicates whether one or more attributes of the specified type or of its derived types is applied to this member.
+ /// </summary>
+ /// <param name="attributeType">The type of custom attribute to search for. The search includes derived types.</param>
+ /// <param name="inherit">True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.</param>
+ /// <returns>
+ /// True if one or more instances of <paramref name="attributeType" /> or any of its derived types is applied to this member; otherwise, false.
+ /// </returns>
+ public override bool IsDefined(Type attributeType, bool inherit)
+ {
+ return this.aliasedProperty.IsDefined(attributeType, inherit);
+ }
+
+ /// <summary>
+ /// Returns an array whose elements reflect the public and, if specified, non-public get, set, and other accessors of the property reflected by the current instance.
+ /// </summary>
+ /// <param name="nonPublic">Indicates whether non-public methods should be returned in the MethodInfo array. true if non-public methods are to be included; otherwise, false.</param>
+ /// <returns>
+ /// An array of <see cref="T:System.Reflection.MethodInfo" /> objects whose elements reflect the get, set, and other accessors of the property reflected by the current instance. If <paramref name="nonPublic" /> is true, this array contains public and non-public get, set, and other accessors. If <paramref name="nonPublic" /> is false, this array contains only public get, set, and other accessors. If no accessors with the specified visibility are found, this method returns an array with zero (0) elements.
+ /// </returns>
+ public override MethodInfo[] GetAccessors(bool nonPublic)
+ {
+ return this.aliasedProperty.GetAccessors(nonPublic);
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, returns the public or non-public get accessor for this property.
+ /// </summary>
+ /// <param name="nonPublic">Indicates whether a non-public get accessor should be returned. true if a non-public accessor is to be returned; otherwise, false.</param>
+ /// <returns>
+ /// A MethodInfo object representing the get accessor for this property, if <paramref name="nonPublic" /> is true. Returns null if <paramref name="nonPublic" /> is false and the get accessor is non-public, or if <paramref name="nonPublic" /> is true but no get accessors exist.
+ /// </returns>
+ public override MethodInfo GetGetMethod(bool nonPublic)
+ {
+ return this.aliasedProperty.GetGetMethod(nonPublic);
+ }
+
+ /// <summary>
+ /// Gets the index parameters of the property.
+ /// </summary>
+ /// <returns>The index parameters of the property.</returns>
+ public override ParameterInfo[] GetIndexParameters()
+ {
+ return this.aliasedProperty.GetIndexParameters();
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, returns the set accessor for this property.
+ /// </summary>
+ /// <param name="nonPublic">Indicates whether the accessor should be returned if it is non-public. true if a non-public accessor is to be returned; otherwise, false.</param>
+ /// <returns>
+ /// Value Condition A <see cref="T:System.Reflection.MethodInfo" /> object representing the Set method for this property. The set accessor is public.-or- <paramref name="nonPublic" /> is true and the set accessor is non-public. null<paramref name="nonPublic" /> is true, but the property is read-only.-or- <paramref name="nonPublic" /> is false and the set accessor is non-public.-or- There is no set accessor.
+ /// </returns>
+ public override MethodInfo GetSetMethod(bool nonPublic)
+ {
+ return this.aliasedProperty.GetSetMethod(nonPublic);
+ }
+
+ /// <summary>
+ /// Gets the value of the property on the given instance.
+ /// </summary>
+ /// <param name="obj">The object to invoke the getter on.</param>
+ /// <param name="invokeAttr">The <see cref="BindingFlags"/> to invoke with.</param>
+ /// <param name="binder">The binder to use.</param>
+ /// <param name="index">The indices to use.</param>
+ /// <param name="culture">The culture to use.</param>
+ /// <returns>The value of the property on the given instance.</returns>
+ public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture)
+ {
+ return this.aliasedProperty.GetValue(obj, invokeAttr, binder, index, culture);
+ }
+
+ /// <summary>
+ /// Sets the value of the property on the given instance.
+ /// </summary>
+ /// <param name="obj">The object to set the value on.</param>
+ /// <param name="value">The value to set.</param>
+ /// <param name="invokeAttr">The <see cref="BindingFlags"/> to invoke with.</param>
+ /// <param name="binder">The binder to use.</param>
+ /// <param name="index">The indices to use.</param>
+ /// <param name="culture">The culture to use.</param>
+ public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture)
+ {
+ this.aliasedProperty.SetValue(obj, value, invokeAttr, binder, index, culture);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasPropertyInfo.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasPropertyInfo.cs.meta
new file mode 100644
index 00000000..c6b15163
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasPropertyInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 00bf47593f2a330c1bb41552bdc1233f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ReferenceEqualityComparer.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ReferenceEqualityComparer.cs
new file mode 100644
index 00000000..1b1984c9
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ReferenceEqualityComparer.cs
@@ -0,0 +1,57 @@
+//-----------------------------------------------------------------------
+// <copyright file="ReferenceEqualityComparer.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>
+ /// Compares objects by reference only, ignoring equality operators completely. This is used by the property tree reference dictionaries to keep track of references.
+ /// </summary>
+ public class ReferenceEqualityComparer<T> : IEqualityComparer<T> where T : class
+ {
+ /// <summary>
+ /// A default, cached instance of this generic variant of the reference equality comparer.
+ /// </summary>
+ public static readonly ReferenceEqualityComparer<T> Default = new ReferenceEqualityComparer<T>();
+
+ /// <summary>
+ /// Returns true if the object references are equal.
+ /// </summary>
+ public bool Equals(T x, T y)
+ {
+ return object.ReferenceEquals(x, y);
+ }
+
+ /// <summary>
+ /// Returns the result of the object's own GetHashCode method.
+ /// </summary>
+ public int GetHashCode(T obj)
+ {
+ try
+ {
+ return obj.GetHashCode();
+ }
+ catch (NullReferenceException)
+ {
+ return -1;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ReferenceEqualityComparer.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ReferenceEqualityComparer.cs.meta
new file mode 100644
index 00000000..9f4efc31
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ReferenceEqualityComparer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5ad884ed6013d621a310ceb4c954f62a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnityVersion.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnityVersion.cs
new file mode 100644
index 00000000..c4bbe5d5
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnityVersion.cs
@@ -0,0 +1,81 @@
+//-----------------------------------------------------------------------
+// <copyright file="UnityVersion.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 UnityEngine;
+
+ /// <summary>
+ /// Utility class indicating current Unity version.
+ /// </summary>
+#if UNITY_EDITOR
+
+ [UnityEditor.InitializeOnLoad]
+#endif
+ public static class UnityVersion
+ {
+ static UnityVersion()
+ {
+ string[] version = Application.unityVersion.Split('.');
+
+ if (version.Length < 2)
+ {
+ Debug.LogError("Could not parse current Unity version '" + Application.unityVersion + "'; not enough version elements.");
+ return;
+ }
+
+ if (int.TryParse(version[0], out Major) == false)
+ {
+ Debug.LogError("Could not parse major part '" + version[0] + "' of Unity version '" + Application.unityVersion + "'.");
+ }
+
+ if (int.TryParse(version[1], out Minor) == false)
+ {
+ Debug.LogError("Could not parse minor part '" + version[1] + "' of Unity version '" + Application.unityVersion + "'.");
+ }
+ }
+
+ [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
+ private static void EnsureLoaded()
+ {
+ // This method ensures that this type has been initialized before any loading of objects occurs.
+ // If this isn't done, the static constructor may be invoked at an illegal time that is not
+ // allowed by Unity. During scene deserialization, off the main thread, is an example.
+ }
+
+ /// <summary>
+ /// Tests current Unity version is equal or greater.
+ /// </summary>
+ /// <param name="major">Minimum major version.</param>
+ /// <param name="minor">Minimum minor version.</param>
+ /// <returns><c>true</c> if the current Unity version is greater. Otherwise <c>false</c>.</returns>
+ public static bool IsVersionOrGreater(int major, int minor)
+ {
+ return UnityVersion.Major > major || (UnityVersion.Major == major && UnityVersion.Minor >= minor);
+ }
+
+ /// <summary>
+ /// The current Unity version major.
+ /// </summary>
+ public static readonly int Major;
+
+ /// <summary>
+ /// The current Unity version minor.
+ /// </summary>
+ public static readonly int Minor;
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnityVersion.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnityVersion.cs.meta
new file mode 100644
index 00000000..5f93dea1
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnityVersion.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0fe3820fb4651e200f17905245ec8be2
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnsafeUtilities.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnsafeUtilities.cs
new file mode 100644
index 00000000..e6058115
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnsafeUtilities.cs
@@ -0,0 +1,408 @@
+//-----------------------------------------------------------------------
+// <copyright file="UnsafeUtilities.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.Unsafe
+{
+ using System;
+ using System.Runtime.InteropServices;
+
+#pragma warning disable 0649 // Field is never assigned to and will have its default value // VRC
+
+ /// <summary>
+ /// Contains utilities for performing common unsafe operations.
+ /// </summary>
+ public static class UnsafeUtilities
+ {
+ /// <summary>
+ /// Blindly creates an array of structs from an array of bytes via direct memory copy/blit.
+ /// </summary>
+ public static unsafe T[] StructArrayFromBytes<T>(byte[] bytes, int byteLength) where T : struct
+ {
+ return StructArrayFromBytes<T>(bytes, 0, 0);
+ }
+
+ /// <summary>
+ /// Blindly creates an array of structs from an array of bytes via direct memory copy/blit.
+ /// </summary>
+ public static unsafe T[] StructArrayFromBytes<T>(byte[] bytes, int byteLength, int byteOffset) where T : struct
+ {
+ if (bytes == null)
+ {
+ throw new ArgumentNullException("bytes");
+ }
+
+ if (byteLength <= 0)
+ {
+ throw new ArgumentException("Byte length must be larger than zero.");
+ }
+
+ if (byteOffset < 0)
+ {
+ throw new ArgumentException("Byte offset must be larger than or equal to zero.");
+ }
+
+ int typeSize = Marshal.SizeOf(typeof(T));
+
+ if (byteOffset % sizeof(ulong) != 0)
+ {
+ throw new ArgumentException("Byte offset must be divisible by " + sizeof(ulong) + " (IE, sizeof(ulong))");
+ }
+
+ if (byteLength + byteOffset >= bytes.Length)
+ {
+ throw new ArgumentException("Given byte array of size " + bytes.Length + " is not large enough to copy requested number of bytes " + byteLength + ".");
+ }
+
+ if ((byteLength - byteOffset) % typeSize != 0)
+ {
+ throw new ArgumentException("The length in the given byte array (" + bytes.Length + ", and " + (bytes.Length - byteOffset) + " minus byteOffset " + byteOffset + ") to convert to type " + typeof(T).Name + " is not divisible by the size of " + typeof(T).Name + " (" + typeSize + ").");
+ }
+
+ int elementCount = (bytes.Length - byteOffset) / typeSize;
+ T[] array = new T[elementCount];
+ MemoryCopy(bytes, array, byteLength, byteOffset, 0);
+ return array;
+ }
+
+ /// <summary>
+ /// Blindly copies an array of structs into an array of bytes via direct memory copy/blit.
+ /// </summary>
+ public static unsafe byte[] StructArrayToBytes<T>(T[] array) where T : struct
+ {
+ byte[] bytes = null;
+ return StructArrayToBytes(array, ref bytes, 0);
+ }
+
+ /// <summary>
+ /// Blindly copies an array of structs into an array of bytes via direct memory copy/blit.
+ /// </summary>
+ public static unsafe byte[] StructArrayToBytes<T>(T[] array, ref byte[] bytes, int byteOffset) where T : struct
+ {
+ if (array == null)
+ {
+ throw new ArgumentNullException("array");
+ }
+
+ if (byteOffset < 0)
+ {
+ throw new ArgumentException("Byte offset must be larger than or equal to zero.");
+ }
+
+ int typeSize = Marshal.SizeOf(typeof(T));
+ int byteCount = typeSize * array.Length;
+
+ if (bytes == null)
+ {
+ bytes = new byte[byteCount + byteOffset];
+ }
+ else if (bytes.Length + byteOffset > byteCount)
+ {
+ throw new ArgumentException("Byte array must be at least " + (bytes.Length + byteOffset) + " long with the given byteOffset.");
+ }
+
+ MemoryCopy(array, bytes, byteCount, 0, byteOffset);
+ return bytes;
+ }
+
+ /// <summary>
+ /// Creates a new string from the contents of a given byte buffer.
+ /// </summary>
+ public static unsafe string StringFromBytes(byte[] buffer, int charLength, bool needs16BitSupport)
+ {
+ int byteCount = needs16BitSupport ? charLength * 2 : charLength;
+
+ if (buffer.Length < byteCount)
+ {
+ throw new ArgumentException("Buffer is not large enough to contain the given string; a size of at least " + byteCount + " is required.");
+ }
+
+ GCHandle toHandle = default(GCHandle);
+ string result = new string(default(char), charLength); // Creaty empty string of required length
+
+ try
+ {
+ toHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
+
+ if (needs16BitSupport)
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ fixed (char* charPtr1 = result)
+ {
+ ushort* fromPtr1 = (ushort*)toHandle.AddrOfPinnedObject().ToPointer();
+ ushort* toPtr1 = (ushort*)charPtr1;
+
+ for (int i = 0; i < byteCount; i += sizeof(ushort))
+ {
+ *toPtr1++ = *fromPtr1++;
+ }
+ }
+ }
+ else
+ {
+ fixed (char* charPtr2 = result)
+ {
+ byte* fromPtr2 = (byte*)toHandle.AddrOfPinnedObject().ToPointer();
+ byte* toPtr2 = (byte*)charPtr2;
+
+ for (int i = 0; i < byteCount; i += sizeof(ushort))
+ {
+ *toPtr2 = *(fromPtr2 + 1);
+ *(toPtr2 + 1) = *fromPtr2;
+
+ fromPtr2 += 2;
+ toPtr2 += 2;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ fixed (char* charPtr3 = result)
+ {
+ byte* fromPtr3 = (byte*)toHandle.AddrOfPinnedObject().ToPointer();
+ byte* toPtr3 = (byte*)charPtr3;
+
+ for (int i = 0; i < byteCount; i += sizeof(byte))
+ {
+ *toPtr3++ = *fromPtr3++;
+ toPtr3++; // Skip every other string byte
+ }
+ }
+ }
+ else
+ {
+ fixed (char* charPtr4 = result)
+ {
+ byte* fromPtr4 = (byte*)toHandle.AddrOfPinnedObject().ToPointer();
+ byte* toPtr4 = (byte*)charPtr4;
+
+ for (int i = 0; i < byteCount; i += sizeof(byte))
+ {
+ toPtr4++; // Skip every other string byte
+ *toPtr4++ = *fromPtr4++;
+ }
+ }
+ }
+ }
+ }
+ finally
+ {
+ if (toHandle.IsAllocated)
+ {
+ toHandle.Free();
+ }
+ }
+
+ // Retrieve proper string reference from the intern pool.
+ // This code removed for now, as the slight decrease in memory use is not considered worth the performance cost of the intern lookup and the potential extra garbage to be collected.
+ // Might eventually become a global config option, if this is considered necessary.
+ //result = string.Intern(result);
+
+ return result;
+ }
+
+ /// <summary>
+ /// Writes the contents of a string into a given byte buffer.
+ /// </summary>
+ public static unsafe int StringToBytes(byte[] buffer, string value, bool needs16BitSupport)
+ {
+ int byteCount = needs16BitSupport ? value.Length * 2 : value.Length;
+
+ if (buffer.Length < byteCount)
+ {
+ throw new ArgumentException("Buffer is not large enough to contain the given string; a size of at least " + byteCount + " is required.");
+ }
+
+ GCHandle toHandle = default(GCHandle);
+
+ try
+ {
+ toHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
+
+ if (needs16BitSupport)
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ fixed (char* charPtr1 = value)
+ {
+ ushort* fromPtr1 = (ushort*)charPtr1;
+ ushort* toPtr1 = (ushort*)toHandle.AddrOfPinnedObject().ToPointer();
+
+ for (int i = 0; i < byteCount; i += sizeof(ushort))
+ {
+ *toPtr1++ = *fromPtr1++;
+ }
+ }
+ }
+ else
+ {
+ fixed (char* charPtr2 = value)
+ {
+ byte* fromPtr2 = (byte*)charPtr2;
+ byte* toPtr2 = (byte*)toHandle.AddrOfPinnedObject().ToPointer();
+
+ for (int i = 0; i < byteCount; i += sizeof(ushort))
+ {
+ *toPtr2 = *(fromPtr2 + 1);
+ *(toPtr2 + 1) = *fromPtr2;
+
+ fromPtr2 += 2;
+ toPtr2 += 2;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ fixed (char* charPtr3 = value)
+ {
+ byte* fromPtr3 = (byte*)charPtr3;
+ byte* toPtr3 = (byte*)toHandle.AddrOfPinnedObject().ToPointer();
+
+ for (int i = 0; i < byteCount; i += sizeof(byte))
+ {
+ fromPtr3++; // Skip every other string byte
+ *toPtr3++ = *fromPtr3++;
+ }
+ }
+ }
+ else
+ {
+ fixed (char* charPtr4 = value)
+ {
+ byte* fromPtr4 = (byte*)charPtr4;
+ byte* toPtr4 = (byte*)toHandle.AddrOfPinnedObject().ToPointer();
+
+ for (int i = 0; i < byteCount; i += sizeof(byte))
+ {
+ *toPtr4++ = *fromPtr4++;
+ fromPtr4++; // Skip every other string byte
+ }
+ }
+ }
+ }
+ }
+ finally
+ {
+ if (toHandle.IsAllocated)
+ {
+ toHandle.Free();
+ }
+ }
+
+ return byteCount;
+ }
+
+ private struct Struct256Bit
+ {
+ public decimal d1;
+ public decimal d2;
+ }
+
+ public static unsafe void MemoryCopy(void* from, void* to, int bytes)
+ {
+ byte* end = (byte*)to + bytes;
+
+ Struct256Bit* fromBigPtr = (Struct256Bit*)from;
+ Struct256Bit* toBigPtr = (Struct256Bit*)to;
+
+ while ((toBigPtr + 1) <= end)
+ {
+ *toBigPtr++ = *fromBigPtr++;
+ }
+
+ byte* fromSmallPtr = (byte*)fromBigPtr;
+ byte* toSmallPtr = (byte*)toBigPtr;
+
+ while (toSmallPtr < end)
+ {
+ *toSmallPtr++ = *fromSmallPtr++;
+ }
+ }
+
+ /// <summary>
+ /// Blindly mem-copies a given number of bytes from the memory location of one object to another. WARNING: This method is ridiculously dangerous. Only use if you know what you're doing.
+ /// </summary>
+ public static unsafe void MemoryCopy(object from, object to, int byteCount, int fromByteOffset, int toByteOffset)
+ {
+ GCHandle fromHandle = default(GCHandle);
+ GCHandle toHandle = default(GCHandle);
+
+ if (fromByteOffset % sizeof(ulong) != 0 || toByteOffset % sizeof(ulong) != 0)
+ {
+ throw new ArgumentException("Byte offset must be divisible by " + sizeof(ulong) + " (IE, sizeof(ulong))");
+ }
+
+ try
+ {
+ int restBytes = byteCount % sizeof(ulong);
+ int ulongCount = (byteCount - restBytes) / sizeof(ulong);
+ int fromOffsetCount = fromByteOffset / sizeof(ulong);
+ int toOffsetCount = toByteOffset / sizeof(ulong);
+
+ fromHandle = GCHandle.Alloc(from, GCHandleType.Pinned);
+ toHandle = GCHandle.Alloc(to, GCHandleType.Pinned);
+
+ ulong* fromUlongPtr = (ulong*)fromHandle.AddrOfPinnedObject().ToPointer();
+ ulong* toUlongPtr = (ulong*)toHandle.AddrOfPinnedObject().ToPointer();
+
+ if (fromOffsetCount > 0)
+ {
+ fromUlongPtr += fromOffsetCount;
+ }
+
+ if (toOffsetCount > 0)
+ {
+ toUlongPtr += toOffsetCount;
+ }
+
+ for (int i = 0; i < ulongCount; i++)
+ {
+ *toUlongPtr++ = *fromUlongPtr++;
+ }
+
+ if (restBytes > 0)
+ {
+ byte* fromBytePtr = (byte*)fromUlongPtr;
+ byte* toBytePtr = (byte*)toUlongPtr;
+
+ for (int i = 0; i < restBytes; i++)
+ {
+ *toBytePtr++ = *fromBytePtr++;
+ }
+ }
+ }
+ finally
+ {
+ if (fromHandle.IsAllocated)
+ {
+ fromHandle.Free();
+ }
+
+ if (toHandle.IsAllocated)
+ {
+ toHandle.Free();
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnsafeUtilities.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnsafeUtilities.cs.meta
new file mode 100644
index 00000000..2198f9c2
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnsafeUtilities.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 93b4d01199b118896c756b09a9206fc0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: