From eb84bb298d2b95aec7b2ae12cbf25ac64f25379a Mon Sep 17 00:00:00 2001 From: tylermurphy534 Date: Sun, 6 Nov 2022 15:12:42 -0500 Subject: move to self host --- .../Misc/AssemblyImportSettingsUtilities.cs | 289 ++++ .../Misc/AssemblyImportSettingsUtilities.cs.meta | 11 + .../OdinSerializer/Utilities/Misc/Cache.cs | 254 +++ .../OdinSerializer/Utilities/Misc/Cache.cs.meta | 11 + .../Utilities/Misc/DoubleLookupDictionary.cs | 181 ++ .../Utilities/Misc/DoubleLookupDictionary.cs.meta | 11 + .../OdinSerializer/Utilities/Misc/EmitUtilities.cs | 1772 ++++++++++++++++++++ .../Utilities/Misc/EmitUtilities.cs.meta | 11 + .../Utilities/Misc/FastTypeComparer.cs | 44 + .../Utilities/Misc/FastTypeComparer.cs.meta | 11 + .../OdinSerializer/Utilities/Misc/Flags.cs | 108 ++ .../OdinSerializer/Utilities/Misc/Flags.cs.meta | 11 + .../Utilities/Misc/ICacheNotificationReceiver.cs | 36 + .../Misc/ICacheNotificationReceiver.cs.meta | 11 + .../OdinSerializer/Utilities/Misc/ImmutableList.cs | 605 +++++++ .../Utilities/Misc/ImmutableList.cs.meta | 11 + .../Utilities/Misc/MemberAliasFieldInfo.cs | 179 ++ .../Utilities/Misc/MemberAliasFieldInfo.cs.meta | 11 + .../Utilities/Misc/MemberAliasMethodInfo.cs | 194 +++ .../Utilities/Misc/MemberAliasMethodInfo.cs.meta | 11 + .../Utilities/Misc/MemberAliasPropertyInfo.cs | 237 +++ .../Utilities/Misc/MemberAliasPropertyInfo.cs.meta | 11 + .../Utilities/Misc/ReferenceEqualityComparer.cs | 57 + .../Misc/ReferenceEqualityComparer.cs.meta | 11 + .../OdinSerializer/Utilities/Misc/UnityVersion.cs | 81 + .../Utilities/Misc/UnityVersion.cs.meta | 11 + .../Utilities/Misc/UnsafeUtilities.cs | 408 +++++ .../Utilities/Misc/UnsafeUtilities.cs.meta | 11 + 28 files changed, 4599 insertions(+) create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Cache.cs create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Cache.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/DoubleLookupDictionary.cs create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/DoubleLookupDictionary.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/EmitUtilities.cs create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/EmitUtilities.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/FastTypeComparer.cs create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/FastTypeComparer.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Flags.cs create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/Flags.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ICacheNotificationReceiver.cs create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ICacheNotificationReceiver.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ImmutableList.cs create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ImmutableList.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasFieldInfo.cs create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasFieldInfo.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasMethodInfo.cs create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasMethodInfo.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasPropertyInfo.cs create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/MemberAliasPropertyInfo.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ReferenceEqualityComparer.cs create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/ReferenceEqualityComparer.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnityVersion.cs create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnityVersion.cs.meta create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnsafeUtilities.cs create mode 100644 VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/UnsafeUtilities.cs.meta (limited to 'VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc') 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 (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. +// +//----------------------------------------------------------------------- + +#if UNITY_EDITOR + +namespace VRC.Udon.Serialization.OdinSerializer.Utilities.Editor +{ + using System; + using System.IO; + using System.Linq; + using System.Reflection; + using UnityEditor; + + /// + /// Defines how an assembly's import settings should be configured. + /// + public enum OdinAssemblyImportSettings + { + /// + /// Include the assembly in the build, but not in the editor. + /// + IncludeInBuildOnly, + /// + /// Include the assembly in the editor, but not in the build. + /// + IncludeInEditorOnly, + /// + /// Include the assembly in both the build and in the editor. + /// + IncludeInAll, + /// + /// Exclude the assembly from both the build and from the editor. + /// + ExcludeFromAll, + } + + /// + /// Utility for correctly setting import on OdinSerializer assemblies based on platform and scripting backend. + /// + public static class AssemblyImportSettingsUtilities + { + private static MethodInfo getPropertyIntMethod; + private static MethodInfo getScriptingBackendMethod; + private static MethodInfo getApiCompatibilityLevelMethod; + private static MethodInfo apiCompatibilityLevelProperty; + + /// + /// All valid Unity BuildTarget platforms. + /// + public static readonly ImmutableList Platforms; + + /// + /// All valid Unity BuildTarget platforms that support Just In Time compilation. + /// + public static readonly ImmutableList JITPlatforms; + + /// + /// All scripting backends that support JIT. + /// + public static readonly ImmutableList JITScriptingBackends; + + /// + /// All API compatibility levels that support JIT. + /// + public static readonly ImmutableList 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(Enum.GetValues(typeof(BuildTarget)) + .Cast() + .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(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(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(jitApiNames + .Where(x => apiLevelNames.Contains(x)) + .Select(x => (ApiCompatibilityLevel)Enum.Parse(typeof(ApiCompatibilityLevel), x)) + .ToArray()); + } + + /// + /// Set the import settings on the assembly. + /// + /// The path to the assembly to configure import settings from. + /// The import settings to configure for the assembly at the path. + 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); + } + + /// + /// Set the import settings on the assembly. + /// + /// The path to the assembly to configure import settings from. + /// Indicates if the assembly should be included in the build. + /// Indicates if the assembly should be included in the Unity editor. + 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(); + } + } + + /// + /// Gets the current scripting backend for the build from the Unity editor. This method is Unity version independent. + /// + /// + 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!"); + } + + /// + /// Gets the current API compatibility level from the Unity Editor. This method is Unity version independent. + /// + /// + 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!"); + } + + /// + /// Gets a value that indicates if the specified platform supports JIT. + /// + /// The platform to test. + /// true if the platform supports JIT; otherwise false. + public static bool PlatformSupportsJIT(BuildTarget platform) + { + return JITPlatforms.Contains(platform); + } + + /// + /// Gets a value that indicates if the specified scripting backend supports JIT. + /// + /// The backend to test. + /// true if the backend supports JIT; otherwise false. + public static bool ScriptingBackendSupportsJIT(ScriptingImplementation backend) + { + return JITScriptingBackends.Contains(backend); + } + + /// + /// Gets a value that indicates if the specified api level supports JIT. + /// + /// The api level to test. + /// true if the api level supports JIT; otherwise false. + public static bool ApiCompatibilityLevelSupportsJIT(ApiCompatibilityLevel apiLevel) + { + return JITApiCompatibilityLevels.Contains(apiLevel); + } + + /// + /// Gets a value that indicates if the specified build settings supports JIT. + /// + /// The platform build setting. + /// The scripting backend build settting. + /// The api level build setting. + /// true if the build settings supports JIT; otherwise false. + 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 (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. +// +//----------------------------------------------------------------------- + +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.Threading; + + public interface ICache : IDisposable + { + object Value { get; } + } + + /// + /// Provides an easy way of claiming and freeing cached values of any non-abstract reference type with a public parameterless constructor. + /// + /// Cached types which implement the interface will receive notifications when they are claimed and freed. + /// + /// Only one thread should be holding a given cache instance at a time if is implemented, since the invocation of + /// 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. + /// + /// The type which is cached. + /// + public sealed class Cache : 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; + + /// + /// Gets or sets the maximum size of the cache. This value can never go beneath 1. + /// + /// + /// The maximum size of the cache. + /// + public static int MaxCacheSize + { + get + { + return Cache.maxCacheSize; + } + + set + { + Cache.maxCacheSize = Math.Max(1, value); + } + } + + private Cache() + { + this.Value = new T(); + this.isFree = false; + } + + /// + /// The cached value. + /// + public T Value; + + /// + /// Gets a value indicating whether this cached value is free. + /// + /// + /// true if this cached value is free; otherwise, false. + /// + public bool IsFree { get { return this.isFree; } } + + object ICache.Value { get { return this.Value; } } + + /// + /// Claims a cached value of type . + /// + /// A cached value of type . + public static Cache Claim() + { + Cache 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)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(); + } + + if (IsNotificationReceiver) + { + (result.Value as ICacheNotificationReceiver).OnClaimed(); + } + + return result; + } + + /// + /// Releases a cached value. + /// + /// The cached value to release. + /// The cached value to release is null. + public static void Release(Cache 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; + + } + + /// + /// Performs an implicit conversion from to . + /// + /// The cache to convert. + /// + /// The result of the conversion. + /// + public static implicit operator T(Cache cache) + { + if (cache == null) + { + return default(T); + } + + return cache.Value; + } + + /// + /// Releases this cached value. + /// + public void Release() + { + Release(this); + } + + /// + /// Releases this cached value. + /// + void IDisposable.Dispose() + { + Cache.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 (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. +// +//----------------------------------------------------------------------- +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.Collections.Generic; + + /// + /// Not yet documented. + /// + [Serializable] + public class DoubleLookupDictionary : Dictionary> + { + private readonly IEqualityComparer secondKeyComparer; + + public DoubleLookupDictionary() + { + this.secondKeyComparer = EqualityComparer.Default; + } + + public DoubleLookupDictionary(IEqualityComparer firstKeyComparer, IEqualityComparer secondKeyComparer) + : base(firstKeyComparer) + { + this.secondKeyComparer = secondKeyComparer; + } + + /// + /// Not yet documented. + /// + public new Dictionary this[TFirstKey firstKey] + { + get + { + Dictionary innerDict; + + if (!this.TryGetValue(firstKey, out innerDict)) + { + innerDict = new Dictionary(this.secondKeyComparer); + this.Add(firstKey, innerDict); + } + + return innerDict; + } + } + + /// + /// Not yet documented. + /// + public int InnerCount(TFirstKey firstKey) + { + Dictionary innerDict; + + if (this.TryGetValue(firstKey, out innerDict)) + { + return innerDict.Count; + } + + return 0; + } + + /// + /// Not yet documented. + /// + public int TotalInnerCount() + { + int count = 0; + + if (this.Count > 0) + { + foreach (var innerDict in this.Values) + { + count += innerDict.Count; + } + } + + return count; + } + + /// + /// Not yet documented. + /// + public bool ContainsKeys(TFirstKey firstKey, TSecondKey secondKey) + { + Dictionary innerDict; + + return this.TryGetValue(firstKey, out innerDict) && innerDict.ContainsKey(secondKey); + } + + /// + /// Not yet documented. + /// + public bool TryGetInnerValue(TFirstKey firstKey, TSecondKey secondKey, out TValue value) + { + Dictionary innerDict; + + if (this.TryGetValue(firstKey, out innerDict) && innerDict.TryGetValue(secondKey, out value)) + { + return true; + } + + value = default(TValue); + return false; + } + + /// + /// Not yet documented. + /// + 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; + } + + /// + /// Not yet documented. + /// + public bool RemoveInner(TFirstKey firstKey, TSecondKey secondKey) + { + Dictionary innerDict; + + if (this.TryGetValue(firstKey, out innerDict)) + { + bool removed = innerDict.Remove(secondKey); + + if (innerDict.Count == 0) + { + this.Remove(firstKey); + } + + return removed; + } + + return false; + } + + /// + /// Not yet documented. + /// + public void RemoveWhere(Func predicate) + { + List toRemoveBufferFirstKey = new List(); + List toRemoveBufferSecondKey = new List(); + + 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 (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. +// +//----------------------------------------------------------------------- + +// #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 + + /// + /// Not yet documented. + /// + public delegate object WeakValueGetter(ref object instance); + + /// + /// Not yet documented. + /// + public delegate void WeakValueSetter(ref object instance, object value); + + /// + /// Not yet documented. + /// + public delegate FieldType WeakValueGetter(ref object instance); + + /// + /// Not yet documented. + /// + public delegate void WeakValueSetter(ref object instance, FieldType value); + + /// + /// Not yet documented. + /// + public delegate FieldType ValueGetter(ref InstanceType instance); + + /// + /// Not yet documented. + /// + public delegate void ValueSetter(ref InstanceType instance, FieldType value); + + /// + /// Provides utilities for using the namespace. + /// + /// This class is due for refactoring. Use at your own peril. + /// + public static class EmitUtilities + { + /// + /// Gets a value indicating whether emitting is supported on the current platform. + /// + /// + /// true if the current platform can emit; otherwise, false. + /// + public static bool CanEmit + { + get + { +#if CAN_EMIT + return true; +#else + return false; +#endif + } + } + + /// + /// 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. + /// + /// The type of the field to get a value from. + /// The instance describing the field to create a getter for. + /// A delegate which gets the value of the given field. + /// The fieldInfo parameter is null. + public static Func CreateStaticFieldGetter(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)getterMethod.CreateDelegate(typeof(Func)); +#endif + } + + /// + /// 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. + /// + /// The instance describing the field to create a getter for. + /// A delegate which gets the value of the given field. + /// The fieldInfo parameter is null. + public static Func 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)getterMethod.CreateDelegate(typeof(Func)); +#endif + } + + /// + /// 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. + /// + /// The type of the field to set a value to. + /// The instance describing the field to create a setter for. + /// A delegate which sets the value of the given field. + /// The fieldInfo parameter is null. + public static Action CreateStaticFieldSetter(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)setterMethod.CreateDelegate(typeof(Action)); +#endif + } + + /// + /// 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. + /// + /// The instance describing the field to create a setter for. + /// A delegate which sets the value of the given field. + /// The fieldInfo parameter is null. + public static Action 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)setterMethod.CreateDelegate(typeof(Action)); +#endif + } + + /// + /// 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. + /// + /// The type of the instance to get a value from. + /// The type of the field to get a value from. + /// The instance describing the field to create a getter for. + /// A delegate which gets the value of the given field. + /// The fieldInfo parameter is null. + public static ValueGetter CreateInstanceFieldGetter(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)getterMethod.CreateDelegate(typeof(ValueGetter)); +#endif + } + + /// + /// 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. + /// + /// The type of the field to get a value from. + /// The of the instance to get a value from. + /// The instance describing the field to create a getter for. + /// A delegate which gets the value of the given field. + /// The fieldInfo parameter is null. + 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 (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)getterMethod.CreateDelegate(typeof(WeakValueGetter)); +#endif + } + + /// + /// 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. + /// + /// The of the instance to get a value from. + /// The instance describing the field to create a getter for. + /// A delegate which gets the value of the given field. + /// The fieldInfo parameter is null. + 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 + } + + /// + /// 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. + /// + /// The type of the instance to set a value on. + /// The type of the field to set a value to. + /// The instance describing the field to create a setter for. + /// A delegate which sets the value of the given field. + /// The fieldInfo parameter is null. + public static ValueSetter CreateInstanceFieldSetter(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)setterMethod.CreateDelegate(typeof(ValueSetter)); +#endif + } + + /// + /// 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. + /// + /// The type of the field to set a value to. + /// Type of the instance. + /// The instance describing the field to create a setter for. + /// + /// A delegate which sets the value of the given field. + /// + /// The fieldInfo parameter is null. + /// Field cannot be static. + 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, 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)setterMethod.CreateDelegate(typeof(WeakValueSetter)); +#endif + } + + /// + /// 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. + /// + /// Type of the instance. + /// The instance describing the field to create a setter for. + /// + /// A delegate which sets the value of the given field. + /// + /// The fieldInfo parameter is null. + /// Field cannot be static. + 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 + } + + /// + /// 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. + /// + /// The of the instance to get a value from. + /// The instance describing the field to create a getter for. + /// A delegate which gets the value of the given field. + /// The fieldInfo parameter is null. + 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 + } + + /// + /// 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. + /// + /// Type of the instance. + /// The instance describing the property to create a setter for. + /// + /// A delegate which sets the value of the given field. + /// + /// The fieldInfo parameter is null. + /// Property cannot be static. + 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 + } + + /// + /// 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. + /// + /// The type of the property to set a value to. + /// The instance describing the property to create a setter for. + /// A delegate which sets the value of the given property. + /// The propertyInfo parameter is null. + public static Action CreateStaticPropertySetter(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)setterMethod.CreateDelegate(typeof(Action)); +#endif + } + + /// + /// 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. + /// + /// The type of the property to get a value from. + /// The instance describing the property to create a getter for. + /// A delegate which gets the value of the given property. + /// The propertyInfo parameter is null. + public static Func CreateStaticPropertyGetter(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)getterMethod.CreateDelegate(typeof(Func)); +#endif + } + + /// + /// 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. + /// + /// The type of the instance to set a value on. + /// The type of the property to set a value to. + /// The instance describing the property to create a setter for. + /// A delegate which sets the value of the given property. + /// The propertyInfo parameter is null. + public static ValueSetter CreateInstancePropertySetter(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)setterMethod.CreateDelegate(typeof(ValueSetter)); +#endif + } + + /// + /// 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. + /// + /// The type of the instance to get a value from. + /// The type of the property to get a value from. + /// The instance describing the property to create a getter for. + /// A delegate which gets the value of the given property. + /// The propertyInfo parameter is null. + public static ValueGetter CreateInstancePropertyGetter(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)getterMethod.CreateDelegate(typeof(ValueGetter)); +#endif + } + + /// + /// Creates a fast delegate method which calls a given parameterless instance method and returns the result. + /// + /// The type of the class which the method is on. + /// The type which is returned by the given method info. + /// The method info instance which is used. + /// A delegate which calls the method and returns the result, except it's hundreds of times faster than MethodInfo.Invoke. + public static Func CreateMethodReturner(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)Delegate.CreateDelegate(typeof(Func), methodInfo); + } + + /// + /// Creates a fast delegate method which calls a given parameterless static method. + /// + /// The method info instance which is used. + /// A delegate which calls the method and returns the result, except it's hundreds of times faster than MethodInfo.Invoke. + 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); + } + + /// + /// Creates a fast delegate method which calls a given parameterless weakly typed instance method. + /// + /// The method info instance which is used. + /// A delegate which calls the method and returns the result, except it's hundreds of times faster than MethodInfo.Invoke. + public static Action 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."); + } + + 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)method.CreateDelegate(typeof(Action)); +#endif + } + + /// + /// Not yet documented. + /// + public static Action 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)method.CreateDelegate(typeof(Action)); +#endif + } + + /// + /// Creates a fast delegate method which calls a given weakly typed instance method with one argument and returns a value. + /// + /// The type of the result. + /// The type of the first argument. + /// The method info instance which is used. + /// + /// A delegate which calls the method and returns the result, except it's hundreds of times faster than MethodInfo.Invoke. + /// + /// methodInfo + /// + /// 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) + . + /// + public static Func 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.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)method.CreateDelegate(typeof(Func)); +#endif + } + + /// + /// Not yet documented. + /// + public static Func CreateWeakInstanceMethodCallerFunc(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)method.CreateDelegate(typeof(Func)); +#endif + } + + /// + /// Not yet documented. + /// + public static Func CreateWeakInstanceMethodCallerFunc(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)method.CreateDelegate(typeof(Func)); +#endif + } + + /// + /// Creates a fast delegate method which calls a given parameterless instance method on a reference type. + /// + /// The type of the class which the method is on. + /// The method info instance which is used. + /// A delegate which calls the method and returns the result, except it's hundreds of times faster than MethodInfo.Invoke. + public static Action CreateInstanceMethodCaller(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)Delegate.CreateDelegate(typeof(Action), methodInfo); + } + + /// + /// Creates a fast delegate method which calls a given instance method with a given argument on a reference type. + /// + /// The type of the class which the method is on. + /// The type of the argument with which to call the method. + /// The method info instance which is used. + /// A delegate which calls the method and returns the result, except it's hundreds of times faster than MethodInfo.Invoke. + public static Action CreateInstanceMethodCaller(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)Delegate.CreateDelegate(typeof(Action), methodInfo); + } + + public delegate void InstanceRefMethodCaller(ref InstanceType instance); + public delegate void InstanceRefMethodCaller(ref InstanceType instance, TArg1 arg1); + + /// + /// Creates a fast delegate method which calls a given parameterless instance method. + /// + /// The type of the class which the method is on. + /// The method info instance which is used. + /// A delegate which calls the method and returns the result, except it's hundreds of times faster than MethodInfo.Invoke. + public static InstanceRefMethodCaller CreateInstanceRefMethodCaller(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)method.CreateDelegate(typeof(InstanceRefMethodCaller)); +#endif + } + + /// + /// Creates a fast delegate method which calls a given instance method with a given argument on a struct type. + /// + /// The type of the class which the method is on. + /// The type of the argument with which to call the method. + /// The method info instance which is used. + /// A delegate which calls the method and returns the result, except it's hundreds of times faster than MethodInfo.Invoke. + public static InstanceRefMethodCaller CreateInstanceRefMethodCaller(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)method.CreateDelegate(typeof(InstanceRefMethodCaller)); +#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 (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. +// +//----------------------------------------------------------------------- + +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.Collections.Generic; + + /// + /// 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. + /// + /// + public class FastTypeComparer : IEqualityComparer + { + 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 (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. +// +//----------------------------------------------------------------------- + +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using System.Text; + + /// + /// This class encapsulates common combinations. + /// + public static class Flags + { + /// + /// 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. + /// + public const BindingFlags AnyVisibility = BindingFlags.Public | BindingFlags.NonPublic; + + /// + /// Search criteria encompassing all public instance members, including base members. + /// + public const BindingFlags InstancePublic = BindingFlags.Public | BindingFlags.Instance; + + /// + /// Search criteria encompassing all non-public instance members, including base members. + /// + public const BindingFlags InstancePrivate = BindingFlags.NonPublic | BindingFlags.Instance; + + /// + /// Search criteria encompassing all public and non-public instance members, including base members. + /// + public const BindingFlags InstanceAnyVisibility = AnyVisibility | BindingFlags.Instance; + + /// + /// Search criteria encompassing all public static members, including base members. + /// + public const BindingFlags StaticPublic = BindingFlags.Public | BindingFlags.Static; + + /// + /// Search criteria encompassing all non-public static members, including base members. + /// + public const BindingFlags StaticPrivate = BindingFlags.NonPublic | BindingFlags.Static; + + /// + /// Search criteria encompassing all public and non-public static members, including base members. + /// + public const BindingFlags StaticAnyVisibility = AnyVisibility | BindingFlags.Static; + + /// + /// Search criteria encompassing all public instance members, excluding base members. + /// + public const BindingFlags InstancePublicDeclaredOnly = InstancePublic | BindingFlags.DeclaredOnly; + + /// + /// Search criteria encompassing all non-public instance members, excluding base members. + /// + public const BindingFlags InstancePrivateDeclaredOnly = InstancePrivate | BindingFlags.DeclaredOnly; + + /// + /// Search criteria encompassing all public and non-public instance members, excluding base members. + /// + public const BindingFlags InstanceAnyDeclaredOnly = InstanceAnyVisibility | BindingFlags.DeclaredOnly; + + /// + /// Search criteria encompassing all public static members, excluding base members. + /// + public const BindingFlags StaticPublicDeclaredOnly = StaticPublic | BindingFlags.DeclaredOnly; + + /// + /// Search criteria encompassing all non-public static members, excluding base members. + /// + public const BindingFlags StaticPrivateDeclaredOnly = StaticPrivate | BindingFlags.DeclaredOnly; + + /// + /// Search criteria encompassing all public and non-public static members, excluding base members. + /// + public const BindingFlags StaticAnyDeclaredOnly = StaticAnyVisibility | BindingFlags.DeclaredOnly; + + /// + /// Search criteria encompassing all members, including base and static members. + /// + public const BindingFlags StaticInstanceAnyVisibility = InstanceAnyVisibility | BindingFlags.Static; + + /// + /// Search criteria encompassing all members (public and non-public, instance and static), including base members. + /// + 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 (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. +// +//----------------------------------------------------------------------- + +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + /// + /// Provides notification callbacks for values that are cached using the class. + /// + public interface ICacheNotificationReceiver + { + /// + /// Called when the cached value is freed. + /// + void OnFreed(); + + /// + /// Called when the cached value is claimed. + /// + 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 (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. +// +//----------------------------------------------------------------------- +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.Collections; + using System.Collections.Generic; + using UnityEngine; + + /// + /// Interface for immutable list. + /// + public interface IImmutableList : IList + { + } + + /// + /// Interface for generic immutable list. + /// + public interface IImmutableList : IImmutableList, IList + { + /// + /// Index accessor. + /// + new T this[int index] { get; } + } + + /// + /// Immutable list wraps another list, and allows for reading the inner list, without the ability to change it. + /// + [Serializable] + public sealed class ImmutableList : IImmutableList + { + [SerializeField] + private IList innerList; + + /// + /// Creates an immutable list around another list. + /// + public ImmutableList(IList innerList) + { + if (innerList == null) + { + throw new ArgumentNullException("innerList"); + } + + this.innerList = innerList; + } + + /// + /// Number of items in the list. + /// + public int Count { get { return this.innerList.Count; } } + + /// + /// Immutable list cannot be changed directly, so it's size is always fixed. + /// + public bool IsFixedSize { get { return true; } } + + /// + /// Immutable list are always readonly. + /// + public bool IsReadOnly { get { return true; } } + + /// + /// Returns true if the inner list is synchronized. + /// + public bool IsSynchronized { get { return this.innerList.IsSynchronized; } } + + /// + /// Gets the sync root object. + /// + 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.this[int index] + { + get + { + return this.innerList[index]; + } + + set + { + throw new NotSupportedException("Immutable Lists cannot be edited."); + } + } + + /// + /// Index accessor. + /// + /// Index. + public object this[int index] { get { return this.innerList[index]; } } + + /// + /// Returns true if the item is contained in the list. + /// + /// The item's value. + public bool Contains(object value) + { + return this.innerList.Contains(value); + } + + /// + /// Copy the list to an array, + /// + /// Target array. + /// Index. + public void CopyTo(object[] array, int arrayIndex) + { + this.innerList.CopyTo(array, arrayIndex); + } + + /// + /// Copy the list to an array, + /// + /// Target array. + /// Index. + public void CopyTo(Array array, int index) + { + this.innerList.CopyTo(array, index); + } + + /// + /// Gets an enumerator. + /// + public IEnumerator GetEnumerator() + { + return this.innerList.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + IEnumerator IEnumerable.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."); + } + + /// + /// Get the index of a value. + /// + /// The item's value. + public int IndexOf(object value) + { + return this.innerList.IndexOf(value); + } + + /// + /// Immutable list cannot be edited. + /// + /// Index. + void IList.RemoveAt(int index) + { + throw new NotSupportedException("Immutable Lists cannot be edited."); + } + + /// + /// Immutable list cannot be edited. + /// + /// Index. + /// Item. + void IList.Insert(int index, object item) + { + throw new NotSupportedException("Immutable Lists cannot be edited."); + } + + /// + /// Immutable list cannot be edited. + /// + /// Item. + void ICollection.Add(object item) + { + throw new NotSupportedException("Immutable Lists cannot be edited."); + } + + /// + /// Immutable list cannot be edited. + /// + void ICollection.Clear() + { + throw new NotSupportedException("Immutable Lists cannot be edited."); + } + + /// + /// Immutable list cannot be edited. + /// + /// Item. + bool ICollection.Remove(object item) + { + throw new NotSupportedException("Immutable Lists cannot be edited."); + } + } + + /// + /// Not yet documented. + /// + [Serializable] + public sealed class ImmutableList : IImmutableList + { + [SerializeField] + private IList innerList; + + /// + /// Not yet documented. + /// + public ImmutableList(IList innerList) + { + if (innerList == null) + { + throw new ArgumentNullException("innerList"); + } + + this.innerList = innerList; + } + + /// + /// Not yet documented. + /// + 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; } } + + /// + /// Not yet documented. + /// + 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.this[int index] + { + get + { + return this.innerList[index]; + } + + set + { + throw new NotSupportedException("Immutable Lists cannot be edited."); + } + } + + /// + /// Not yet documented. + /// + public T this[int index] + { + get + { + return this.innerList[index]; + } + } + + /// + /// Not yet documented. + /// + public bool Contains(T item) + { + return this.innerList.Contains(item); + } + + /// + /// Not yet documented. + /// + public void CopyTo(T[] array, int arrayIndex) + { + this.innerList.CopyTo(array, arrayIndex); + } + + /// + /// Not yet documented. + /// + public IEnumerator GetEnumerator() + { + return this.innerList.GetEnumerator(); + } + + void ICollection.CopyTo(Array array, int index) + { + this.innerList.CopyTo((T[])array, index); + } + + void ICollection.Add(T item) + { + throw new NotSupportedException("Immutable Lists cannot be edited."); + } + + void ICollection.Clear() + { + throw new NotSupportedException("Immutable Lists cannot be edited."); + } + + bool ICollection.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.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."); + } + + /// + /// Not yet documented. + /// + public int IndexOf(T item) + { + return this.innerList.IndexOf(item); + } + + void IList.RemoveAt(int index) + { + throw new NotSupportedException("Immutable Lists cannot be edited."); + } + } + + /// + /// Immutable list wraps another list, and allows for reading the inner list, without the ability to change it. + /// + [Serializable] + public sealed class ImmutableList : IImmutableList where TList : IList + { + private TList innerList; + + /// + /// Creates an immutable list around another list. + /// + public ImmutableList(TList innerList) + { + if (innerList == null) + { + throw new ArgumentNullException("innerList"); + } + + this.innerList = innerList; + } + + /// + /// Number of items in the list. + /// + 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; } } + + /// + /// Immutable list are always readonly. + /// + 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.this[int index] + { + get + { + return this.innerList[index]; + } + + set + { + throw new NotSupportedException("Immutable Lists cannot be edited."); + } + } + + /// + /// Index accessor. + /// + /// Index. + public TElement this[int index] + { + get + { + return this.innerList[index]; + } + } + + /// + /// Returns true if the item is contained in the list. + /// + public bool Contains(TElement item) + { + return this.innerList.Contains(item); + } + + /// + /// Copies the list to an array. + /// + public void CopyTo(TElement[] array, int arrayIndex) + { + this.innerList.CopyTo(array, arrayIndex); + } + + /// + /// Gets an enumerator. + /// + public IEnumerator GetEnumerator() + { + return this.innerList.GetEnumerator(); + } + + void ICollection.CopyTo(Array array, int index) + { + this.innerList.CopyTo((TElement[])array, index); + } + + void ICollection.Add(TElement item) + { + throw new NotSupportedException("Immutable Lists cannot be edited."); + } + + void ICollection.Clear() + { + throw new NotSupportedException("Immutable Lists cannot be edited."); + } + + bool ICollection.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.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."); + } + + /// + /// Gets the index of an item. + /// + public int IndexOf(TElement item) + { + return this.innerList.IndexOf(item); + } + + void IList.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 (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. +// +//----------------------------------------------------------------------- + +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.Globalization; + using System.Reflection; + + /// + /// Provides a methods of representing imaginary fields which are unique to serialization. + /// + /// We aggregate the FieldInfo associated with this member and return a mangled form of the name. + /// + /// + public sealed class MemberAliasFieldInfo : FieldInfo + { + /// + /// The default fake name separator string. + /// + private const string FAKE_NAME_SEPARATOR_STRING = "+"; + + private FieldInfo aliasedField; + private string mangledName; + + /// + /// Initializes a new instance of the class. + /// + /// The field to alias. + /// The name prefix to use. + public MemberAliasFieldInfo(FieldInfo field, string namePrefix) + { + this.aliasedField = field; + this.mangledName = string.Concat(namePrefix, FAKE_NAME_SEPARATOR_STRING, this.aliasedField.Name); + } + + /// + /// Initializes a new instance of the class. + /// + /// The field to alias. + /// The name prefix to use. + /// The separator string to use. + public MemberAliasFieldInfo(FieldInfo field, string namePrefix, string separatorString) + { + this.aliasedField = field; + this.mangledName = string.Concat(namePrefix, separatorString, this.aliasedField.Name); + } + + /// + /// Gets the aliased field. + /// + /// + /// The aliased field. + /// + public FieldInfo AliasedField { get { return this.aliasedField; } } + + /// + /// Gets the module in which the type that declares the member represented by the current is defined. + /// + public override Module Module { get { return this.aliasedField.Module; } } + + /// + /// Gets a value that identifies a metadata element. + /// + public override int MetadataToken { get { return this.aliasedField.MetadataToken; } } + + /// + /// Gets the name of the current member. + /// + public override string Name { get { return this.mangledName; } } + + /// + /// Gets the class that declares this member. + /// + public override Type DeclaringType { get { return this.aliasedField.DeclaringType; } } + + /// + /// Gets the class object that was used to obtain this instance of MemberInfo. + /// + public override Type ReflectedType { get { return this.aliasedField.ReflectedType; } } + + /// + /// Gets the type of the field. + /// + /// + /// The type of the field. + /// + public override Type FieldType { get { return this.aliasedField.FieldType; } } + + /// + /// Gets a RuntimeFieldHandle, which is a handle to the internal metadata representation of a field. + /// + public override RuntimeFieldHandle FieldHandle { get { return this.aliasedField.FieldHandle; } } + + /// + /// Gets the attributes. + /// + /// + /// The attributes. + /// + public override FieldAttributes Attributes { get { return this.aliasedField.Attributes; } } + + /// + /// When overridden in a derived class, returns an array of all custom attributes applied to this member. + /// + /// True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks. + /// + /// An array that contains all the custom attributes applied to this member, or an array with zero elements if no attributes are defined. + /// + public override object[] GetCustomAttributes(bool inherit) + { + return this.aliasedField.GetCustomAttributes(inherit); + } + + /// + /// When overridden in a derived class, returns an array of custom attributes applied to this member and identified by . + /// + /// The type of attribute to search for. Only attributes that are assignable to this type are returned. + /// True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks. + /// + /// An array of custom attributes applied to this member, or an array with zero elements if no attributes assignable to have been applied. + /// + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return this.aliasedField.GetCustomAttributes(attributeType, inherit); + } + + /// + /// 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. + /// + /// The type of custom attribute to search for. The search includes derived types. + /// True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks. + /// + /// True if one or more instances of or any of its derived types is applied to this member; otherwise, false. + /// + public override bool IsDefined(Type attributeType, bool inherit) + { + return this.aliasedField.IsDefined(attributeType, inherit); + } + + /// + /// Gets the value of the field. + /// + /// The object instance to get the value from. + /// The value of the field. + public override object GetValue(object obj) + { + return this.aliasedField.GetValue(obj); + } + + /// + /// When overridden in a derived class, sets the value of the field supported by the given object. + /// + /// The object whose field value will be set. + /// The value to assign to the field. + /// A field of Binder that specifies the type of binding that is desired (for example, Binder.CreateInstance or Binder.ExactBinding). + /// A set of properties that enables the binding, coercion of argument types, and invocation of members through reflection. If is null, then Binder.DefaultBinding is used. + /// The software preferences of a particular culture. + 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 (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. +// +//----------------------------------------------------------------------- + +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.Globalization; + using System.Reflection; + + /// + /// Provides a methods of representing aliased methods. + /// + /// In this case, what we're representing is a method on a parent class with the same name. + /// + /// We aggregate the MethodInfo associated with this member and return a mangled form of the name. + /// The name that we return is "parentname+methodName". + /// + /// + public sealed class MemberAliasMethodInfo : MethodInfo + { + /// + /// The default fake name separator string. + /// + private const string FAKE_NAME_SEPARATOR_STRING = "+"; + + private MethodInfo aliasedMethod; + private string mangledName; + + /// + /// Initializes a new instance of the class. + /// + /// The method to alias. + /// The name prefix to use. + public MemberAliasMethodInfo(MethodInfo method, string namePrefix) + { + this.aliasedMethod = method; + this.mangledName = string.Concat(namePrefix, FAKE_NAME_SEPARATOR_STRING, this.aliasedMethod.Name); + } + + /// + /// Initializes a new instance of the class. + /// + /// The method to alias. + /// The name prefix to use. + /// The separator string to use. + public MemberAliasMethodInfo(MethodInfo method, string namePrefix, string separatorString) + { + this.aliasedMethod = method; + this.mangledName = string.Concat(namePrefix, separatorString, this.aliasedMethod.Name); + } + + /// + /// Gets the aliased method. + /// + /// + /// The aliased method. + /// + public MethodInfo AliasedMethod { get { return this.aliasedMethod; } } + + /// + /// Gets the custom attributes for the return type. + /// + public override ICustomAttributeProvider ReturnTypeCustomAttributes { get { return this.aliasedMethod.ReturnTypeCustomAttributes; } } + + /// + /// Gets a handle to the internal metadata representation of a method. + /// + public override RuntimeMethodHandle MethodHandle { get { return this.aliasedMethod.MethodHandle; } } + + /// + /// Gets the attributes associated with this method. + /// + public override MethodAttributes Attributes { get { return this.aliasedMethod.Attributes; } } + + public override Type ReturnType { get { return this.aliasedMethod.ReturnType; } } + + /// + /// Gets the class that declares this member. + /// + public override Type DeclaringType { get { return this.aliasedMethod.DeclaringType; } } + + /// + /// Gets the name of the current member. + /// + public override string Name { get { return this.mangledName; } } + + /// + /// Gets the class object that was used to obtain this instance of MemberInfo. + /// + public override Type ReflectedType { get { return this.aliasedMethod.ReflectedType; } } + + /// + /// 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. + /// + /// + /// A MethodInfo object for the first implementation of this method. + /// + public override MethodInfo GetBaseDefinition() + { + return this.aliasedMethod.GetBaseDefinition(); + } + + /// + /// When overridden in a derived class, returns an array of all custom attributes applied to this member. + /// + /// true to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks. + /// + /// An array that contains all the custom attributes applied to this member, or an array with zero elements if no attributes are defined. + /// + public override object[] GetCustomAttributes(bool inherit) + { + return this.aliasedMethod.GetCustomAttributes(inherit); + } + + /// + /// When overridden in a derived class, returns an array of custom attributes applied to this member and identified by . + /// + /// The type of attribute to search for. Only attributes that are assignable to this type are returned. + /// true to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks. + /// + /// An array of custom attributes applied to this member, or an array with zero elements if no attributes assignable to have been applied. + /// + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return this.aliasedMethod.GetCustomAttributes(attributeType, inherit); + } + + /// + /// When overridden in a derived class, returns the flags. + /// + /// + /// The MethodImplAttributes flags. + /// + public override MethodImplAttributes GetMethodImplementationFlags() + { + return this.aliasedMethod.GetMethodImplementationFlags(); + } + + /// + /// When overridden in a derived class, gets the parameters of the specified method or constructor. + /// + /// + /// An array of type ParameterInfo containing information that matches the signature of the method (or constructor) reflected by this MethodBase instance. + /// + public override ParameterInfo[] GetParameters() + { + return this.aliasedMethod.GetParameters(); + } + + /// + /// When overridden in a derived class, invokes the reflected method or constructor with the given parameters. + /// + /// 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. + /// A bitmask that is a combination of 0 or more bit flags from . If is null, this parameter is assigned the value ; thus, whatever you pass in is ignored. + /// An object that enables the binding, coercion of argument types, invocation of members, and retrieval of MemberInfo objects via reflection. If is null, the default binder is used. + /// 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. + /// 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.) + /// + /// 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 array that represent parameters declared with the ref or out keyword may also be modified. + /// + public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + { + return this.aliasedMethod.Invoke(obj, invokeAttr, binder, parameters, culture); + } + + /// + /// 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. + /// + /// The type of custom attribute to search for. The search includes derived types. + /// true to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks. + /// + /// true if one or more instances of or any of its derived types is applied to this member; otherwise, false. + /// + 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 (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. +// +//----------------------------------------------------------------------- + +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.Globalization; + using System.Reflection; + + /// + /// Provides a methods of representing imaginary properties which are unique to serialization. + /// + /// We aggregate the PropertyInfo associated with this member and return a mangled form of the name. + /// + /// + public sealed class MemberAliasPropertyInfo : PropertyInfo + { + /// + /// The default fake name separator string. + /// + private const string FakeNameSeparatorString = "+"; + + private PropertyInfo aliasedProperty; + private string mangledName; + + /// + /// Initializes a new instance of the class. + /// + /// The property to alias. + /// The name prefix to use. + public MemberAliasPropertyInfo(PropertyInfo prop, string namePrefix) + { + this.aliasedProperty = prop; + this.mangledName = string.Concat(namePrefix, FakeNameSeparatorString, this.aliasedProperty.Name); + } + + /// + /// Initializes a new instance of the class. + /// + /// The property to alias. + /// The name prefix to use. + /// The separator string to use. + public MemberAliasPropertyInfo(PropertyInfo prop, string namePrefix, string separatorString) + { + this.aliasedProperty = prop; + this.mangledName = string.Concat(namePrefix, separatorString, this.aliasedProperty.Name); + } + + /// + /// The backing PropertyInfo that is being aliased. + /// + public PropertyInfo AliasedProperty { get { return this.aliasedProperty; } } + + /// + /// Gets the module in which the type that declares the member represented by the current is defined. + /// + public override Module Module { get { return this.aliasedProperty.Module; } } + + /// + /// Gets a value that identifies a metadata element. + /// + public override int MetadataToken { get { return this.aliasedProperty.MetadataToken; } } + + /// + /// Gets the name of the current member. + /// + public override string Name { get { return this.mangledName; } } + + /// + /// Gets the class that declares this member. + /// + public override Type DeclaringType { get { return this.aliasedProperty.DeclaringType; } } + + /// + /// Gets the class object that was used to obtain this instance of MemberInfo. + /// + public override Type ReflectedType { get { return this.aliasedProperty.ReflectedType; } } + + /// + /// Gets the type of the property. + /// + /// + /// The type of the property. + /// + public override Type PropertyType { get { return this.aliasedProperty.PropertyType; } } + + /// + /// Gets the attributes. + /// + /// + /// The attributes. + /// + public override PropertyAttributes Attributes { get { return this.aliasedProperty.Attributes; } } + + /// + /// Gets a value indicating whether this instance can read. + /// + /// + /// true if this instance can read; otherwise, false. + /// + public override bool CanRead { get { return this.aliasedProperty.CanRead; } } + + /// + /// Gets a value indicating whether this instance can write. + /// + /// + /// true if this instance can write; otherwise, false. + /// + public override bool CanWrite { get { return this.aliasedProperty.CanWrite; } } + + /// + /// When overridden in a derived class, returns an array of all custom attributes applied to this member. + /// + /// True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks. + /// + /// An array that contains all the custom attributes applied to this member, or an array with zero elements if no attributes are defined. + /// + public override object[] GetCustomAttributes(bool inherit) + { + return this.aliasedProperty.GetCustomAttributes(inherit); + } + + /// + /// When overridden in a derived class, returns an array of custom attributes applied to this member and identified by . + /// + /// The type of attribute to search for. Only attributes that are assignable to this type are returned. + /// True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks. + /// + /// An array of custom attributes applied to this member, or an array with zero elements if no attributes assignable to have been applied. + /// + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return this.aliasedProperty.GetCustomAttributes(attributeType, inherit); + } + + /// + /// 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. + /// + /// The type of custom attribute to search for. The search includes derived types. + /// True to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks. + /// + /// True if one or more instances of or any of its derived types is applied to this member; otherwise, false. + /// + public override bool IsDefined(Type attributeType, bool inherit) + { + return this.aliasedProperty.IsDefined(attributeType, inherit); + } + + /// + /// 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. + /// + /// Indicates whether non-public methods should be returned in the MethodInfo array. true if non-public methods are to be included; otherwise, false. + /// + /// An array of objects whose elements reflect the get, set, and other accessors of the property reflected by the current instance. If is true, this array contains public and non-public get, set, and other accessors. If 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. + /// + public override MethodInfo[] GetAccessors(bool nonPublic) + { + return this.aliasedProperty.GetAccessors(nonPublic); + } + + /// + /// When overridden in a derived class, returns the public or non-public get accessor for this property. + /// + /// Indicates whether a non-public get accessor should be returned. true if a non-public accessor is to be returned; otherwise, false. + /// + /// A MethodInfo object representing the get accessor for this property, if is true. Returns null if is false and the get accessor is non-public, or if is true but no get accessors exist. + /// + public override MethodInfo GetGetMethod(bool nonPublic) + { + return this.aliasedProperty.GetGetMethod(nonPublic); + } + + /// + /// Gets the index parameters of the property. + /// + /// The index parameters of the property. + public override ParameterInfo[] GetIndexParameters() + { + return this.aliasedProperty.GetIndexParameters(); + } + + /// + /// When overridden in a derived class, returns the set accessor for this property. + /// + /// Indicates whether the accessor should be returned if it is non-public. true if a non-public accessor is to be returned; otherwise, false. + /// + /// Value Condition A object representing the Set method for this property. The set accessor is public.-or- is true and the set accessor is non-public. null is true, but the property is read-only.-or- is false and the set accessor is non-public.-or- There is no set accessor. + /// + public override MethodInfo GetSetMethod(bool nonPublic) + { + return this.aliasedProperty.GetSetMethod(nonPublic); + } + + /// + /// Gets the value of the property on the given instance. + /// + /// The object to invoke the getter on. + /// The to invoke with. + /// The binder to use. + /// The indices to use. + /// The culture to use. + /// The value of the property on the given instance. + public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture) + { + return this.aliasedProperty.GetValue(obj, invokeAttr, binder, index, culture); + } + + /// + /// Sets the value of the property on the given instance. + /// + /// The object to set the value on. + /// The value to set. + /// The to invoke with. + /// The binder to use. + /// The indices to use. + /// The culture to use. + 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 (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. +// +//----------------------------------------------------------------------- + +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using System; + using System.Collections.Generic; + + /// + /// Compares objects by reference only, ignoring equality operators completely. This is used by the property tree reference dictionaries to keep track of references. + /// + public class ReferenceEqualityComparer : IEqualityComparer where T : class + { + /// + /// A default, cached instance of this generic variant of the reference equality comparer. + /// + public static readonly ReferenceEqualityComparer Default = new ReferenceEqualityComparer(); + + /// + /// Returns true if the object references are equal. + /// + public bool Equals(T x, T y) + { + return object.ReferenceEquals(x, y); + } + + /// + /// Returns the result of the object's own GetHashCode method. + /// + 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 (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. +// +//----------------------------------------------------------------------- +namespace VRC.Udon.Serialization.OdinSerializer.Utilities +{ + using UnityEngine; + + /// + /// Utility class indicating current Unity version. + /// +#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. + } + + /// + /// Tests current Unity version is equal or greater. + /// + /// Minimum major version. + /// Minimum minor version. + /// true if the current Unity version is greater. Otherwise false. + public static bool IsVersionOrGreater(int major, int minor) + { + return UnityVersion.Major > major || (UnityVersion.Major == major && UnityVersion.Minor >= minor); + } + + /// + /// The current Unity version major. + /// + public static readonly int Major; + + /// + /// The current Unity version minor. + /// + 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 (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. +// +//----------------------------------------------------------------------- +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 + + /// + /// Contains utilities for performing common unsafe operations. + /// + public static class UnsafeUtilities + { + /// + /// Blindly creates an array of structs from an array of bytes via direct memory copy/blit. + /// + public static unsafe T[] StructArrayFromBytes(byte[] bytes, int byteLength) where T : struct + { + return StructArrayFromBytes(bytes, 0, 0); + } + + /// + /// Blindly creates an array of structs from an array of bytes via direct memory copy/blit. + /// + public static unsafe T[] StructArrayFromBytes(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; + } + + /// + /// Blindly copies an array of structs into an array of bytes via direct memory copy/blit. + /// + public static unsafe byte[] StructArrayToBytes(T[] array) where T : struct + { + byte[] bytes = null; + return StructArrayToBytes(array, ref bytes, 0); + } + + /// + /// Blindly copies an array of structs into an array of bytes via direct memory copy/blit. + /// + public static unsafe byte[] StructArrayToBytes(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; + } + + /// + /// Creates a new string from the contents of a given byte buffer. + /// + 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; + } + + /// + /// Writes the contents of a string into a given byte buffer. + /// + 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++; + } + } + + /// + /// 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. + /// + 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: -- cgit v1.2.3-freya