summaryrefslogtreecommitdiff
path: root/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc
diff options
context:
space:
mode:
authortylermurphy534 <tylermurphy534@gmail.com>2022-11-06 15:12:42 -0500
committertylermurphy534 <tylermurphy534@gmail.com>2022-11-06 15:12:42 -0500
commiteb84bb298d2b95aec7b2ae12cbf25ac64f25379a (patch)
treeefd616a157df06ab661c6d56651853431ac6b08b /VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc
downloadunityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.tar.gz
unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.tar.bz2
unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.zip
move to self host
Diffstat (limited to 'VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc')
-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
28 files changed, 4599 insertions, 0 deletions
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: