Added gas grid optimization, various despawn optimizations, new hauling optimizations utilizing events and regions, bugfixes for old patches and for vanilla functions too, as well as smaller additions. 1.3 files removed.
This commit is contained in:
@@ -4,14 +4,15 @@
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PerformanceFish.System;
|
||||
|
||||
public class EqualityComparerOptimization : ClassWithFishPatches
|
||||
public sealed class EqualityComparerOptimization : ClassWithFishPatches
|
||||
{
|
||||
public class Optimization : FishPatch
|
||||
public sealed class Optimization : FishPatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "Specialized EqualityComparers for slightly faster collection lookups on ValueTypes. Can reduce memory "
|
||||
@@ -74,7 +75,7 @@ public class EqualityComparerOptimization : ClassWithFishPatches
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class EquatableValueTypeEqualityComparer<T> : EqualityComparer<T> where T : struct, IEquatable<T>
|
||||
public sealed class EquatableValueTypeEqualityComparer<T> : EqualityComparer<T> where T : struct, IEquatable<T>
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(T x, T y) => x.Equals(y);
|
||||
@@ -85,71 +86,73 @@ public class EquatableValueTypeEqualityComparer<T> : EqualityComparer<T> where T
|
||||
/// <summary>
|
||||
/// Equals method for the comparer itself.
|
||||
/// </summary>
|
||||
public override bool Equals(object obj) => obj is EquatableValueTypeEqualityComparer<T>;
|
||||
public override bool Equals(object? obj) => obj is EquatableValueTypeEqualityComparer<T>;
|
||||
|
||||
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class EquatableNullableEqualityComparer<T> : EqualityComparer<T?> where T : struct, IEquatable<T>
|
||||
public sealed class EquatableNullableEqualityComparer<T> : EqualityComparer<T?> where T : struct, IEquatable<T>
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(T? x, T? y)
|
||||
=> x.HasValue
|
||||
? y.HasValue && x.Value.Equals(y.Value)
|
||||
? y.HasValue && x.GetValueOrDefault().Equals(y.GetValueOrDefault())
|
||||
: !y.HasValue;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode(T? obj) => !obj.HasValue ? 0 : HashCode.Get(obj);
|
||||
public override int GetHashCode(T? obj) => !obj.HasValue ? 0 : HashCode.Get(obj.GetValueOrDefault());
|
||||
|
||||
/// <summary>
|
||||
/// Equals method for the comparer itself.
|
||||
/// </summary>
|
||||
public override bool Equals(object obj) => obj is EquatableNullableEqualityComparer<T>;
|
||||
public override bool Equals(object? obj) => obj is EquatableNullableEqualityComparer<T>;
|
||||
|
||||
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class NullableEqualityComparer<T> : EqualityComparer<T?> where T : struct
|
||||
public sealed class NullableEqualityComparer<T> : EqualityComparer<T?> where T : struct
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(T? x, T? y)
|
||||
=> x.HasValue
|
||||
? y.HasValue && x.Value.Equals<T>(y.Value)
|
||||
? y.HasValue && x.GetValueOrDefault().Equals<T>(y.GetValueOrDefault())
|
||||
: !y.HasValue;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode(T? obj) => !obj.HasValue ? 0 : HashCode.Get(obj);
|
||||
public override int GetHashCode(T? obj) => !obj.HasValue ? 0 : HashCode.Get(obj.GetValueOrDefault());
|
||||
|
||||
/// <summary>
|
||||
/// Equals method for the comparer itself.
|
||||
/// </summary>
|
||||
public override bool Equals(object obj) => obj is NullableEqualityComparer<T>;
|
||||
public override bool Equals(object? obj) => obj is NullableEqualityComparer<T>;
|
||||
|
||||
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class StringEqualityComparer : EqualityComparer<string>
|
||||
public sealed class StringEqualityComparer : EqualityComparer<string?>
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(string x, string y)
|
||||
[SuppressMessage("Globalization", "CA1307")]
|
||||
[SuppressMessage("Globalization", "CA1309")]
|
||||
public override bool Equals(string? x, string? y)
|
||||
=> string.Equals(x, y); // already does a null check, so this skips the extra check that'd otherwise happen
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode(string obj) => obj.GetHashCode();
|
||||
public override int GetHashCode(string? obj) => obj?.GetHashCode() ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// Equals method for the comparer itself.
|
||||
/// </summary>
|
||||
public override bool Equals(object obj) => obj is StringEqualityComparer;
|
||||
public override bool Equals(object? obj) => obj is StringEqualityComparer;
|
||||
|
||||
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class IntEqualityComparer : EqualityComparer<int>
|
||||
public sealed class IntEqualityComparer : EqualityComparer<int>
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(int x, int y) => x == y;
|
||||
@@ -160,13 +163,13 @@ public class IntEqualityComparer : EqualityComparer<int>
|
||||
/// <summary>
|
||||
/// Equals method for the comparer itself.
|
||||
/// </summary>
|
||||
public override bool Equals(object obj) => obj is IntEqualityComparer;
|
||||
public override bool Equals(object? obj) => obj is IntEqualityComparer;
|
||||
|
||||
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class UshortEqualityComparer : EqualityComparer<ushort>
|
||||
public sealed class UshortEqualityComparer : EqualityComparer<ushort>
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(ushort x, ushort y) => x == y;
|
||||
@@ -177,13 +180,13 @@ public class UshortEqualityComparer : EqualityComparer<ushort>
|
||||
/// <summary>
|
||||
/// Equals method for the comparer itself.
|
||||
/// </summary>
|
||||
public override bool Equals(object obj) => obj is UshortEqualityComparer;
|
||||
public override bool Equals(object? obj) => obj is UshortEqualityComparer;
|
||||
|
||||
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ReferenceEqualityComparer<T> : EqualityComparer<T>
|
||||
public sealed class ReferenceEqualityComparer<T> : EqualityComparer<T>
|
||||
{
|
||||
public new static readonly ReferenceEqualityComparer<T> Default = new();
|
||||
|
||||
@@ -196,13 +199,13 @@ public class ReferenceEqualityComparer<T> : EqualityComparer<T>
|
||||
/// <summary>
|
||||
/// Equals method for the comparer itself.
|
||||
/// </summary>
|
||||
public override bool Equals(object obj) => obj is ReferenceEqualityComparer<T>;
|
||||
public override bool Equals(object? obj) => obj is ReferenceEqualityComparer<T>;
|
||||
|
||||
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ValueTypeEqualityComparer<T> : EqualityComparer<T> where T : struct
|
||||
public sealed class ValueTypeEqualityComparer<T> : EqualityComparer<T> where T : struct
|
||||
{
|
||||
public new static readonly ValueTypeEqualityComparer<T> Default = new();
|
||||
|
||||
@@ -215,7 +218,7 @@ public class ValueTypeEqualityComparer<T> : EqualityComparer<T> where T : struct
|
||||
/// <summary>
|
||||
/// Equals method for the comparer itself.
|
||||
/// </summary>
|
||||
public override bool Equals(object obj) => obj is ReferenceEqualityComparer<T>;
|
||||
public override bool Equals(object? obj) => obj is ReferenceEqualityComparer<T>;
|
||||
|
||||
public override int GetHashCode() => GetType().Name.GetHashCode();
|
||||
}
|
||||
@@ -3,13 +3,13 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Threading.Tasks;
|
||||
using PerformanceFish.Cache;
|
||||
using PerformanceFish.ModCompatibility;
|
||||
using ConstructorInfoCache
|
||||
= PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle, System.Reflection.BindingFlags,
|
||||
PerformanceFish.System.ReflectionCaching.ByValueComparableArray<System.Type>,
|
||||
@@ -49,14 +49,14 @@ using TypeFullNameCache
|
||||
using ActivatorCache
|
||||
= PerformanceFish.Cache.ByReferenceUnclearable<System.RuntimeTypeHandle,
|
||||
PerformanceFish.System.ReflectionCaching.ActivatorPatches.ActivatorCacheValue>;
|
||||
using ActivatorWithArgumentsCache
|
||||
= PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle,
|
||||
PerformanceFish.System.ReflectionCaching.ByValueComparableArray<System.Type>,
|
||||
PerformanceFish.System.ReflectionCaching.ActivatorPatches.ActivatorCacheValue>;
|
||||
// using ActivatorWithArgumentsCache
|
||||
// = PerformanceFish.Cache.ByReference<System.RuntimeTypeHandle,
|
||||
// PerformanceFish.System.ReflectionCaching.ByValueComparableArray<System.Type>,
|
||||
// PerformanceFish.System.ReflectionCaching.ActivatorPatches.ActivatorCacheValue>;
|
||||
|
||||
namespace PerformanceFish.System;
|
||||
|
||||
public class ReflectionCaching : ClassWithFishPatches
|
||||
public sealed class ReflectionCaching : ClassWithFishPatches
|
||||
{
|
||||
static ReflectionCaching() // necessary to prevent recursion between patches and function pointer creation
|
||||
{
|
||||
@@ -85,15 +85,15 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
public BindingFlags Flags;
|
||||
}
|
||||
|
||||
public class TypePatches
|
||||
public sealed class TypePatches
|
||||
{
|
||||
public class GetField_Patch : FishPatch
|
||||
public sealed class GetField_Patch : FishPatch
|
||||
{
|
||||
public override string Description { get; } = "Caches GetField lookups";
|
||||
|
||||
public override MethodBase TargetMethodInfo { get; }
|
||||
= AccessTools.Method(typeof(RuntimeType), nameof(RuntimeType.GetField),
|
||||
new[] { typeof(string), typeof(BindingFlags) })!;
|
||||
[typeof(string), typeof(BindingFlags)])!;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Prefix(Type __instance, string name, BindingFlags bindingAttr, out FieldInfo? __result,
|
||||
@@ -132,11 +132,12 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
= __result;
|
||||
}
|
||||
|
||||
public class GetMethodWithFlags_Patch : FishPatch
|
||||
public sealed class GetMethodWithFlags_Patch : FishPatch
|
||||
{
|
||||
public override string Description { get; } = "Caches GetMethod lookups";
|
||||
|
||||
public override Expression<Action> TargetMethod { get; }
|
||||
// ReSharper disable once ReturnValueOfPureMethodIsNotUsed
|
||||
= static () => default(Type)!.GetMethod(null!, default(BindingFlags));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -168,11 +169,12 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
= __result;
|
||||
}
|
||||
|
||||
public class GetMethodWithTypes_Patch : FishPatch
|
||||
public sealed class GetMethodWithTypes_Patch : FishPatch
|
||||
{
|
||||
public override string Description { get; } = "Caches GetMethod lookups";
|
||||
|
||||
public override Expression<Action> TargetMethod { get; }
|
||||
// ReSharper disable once ReturnValueOfPureMethodIsNotUsed
|
||||
= static () => default(Type)!.GetMethod(null!, null!);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -201,7 +203,7 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
= __result;
|
||||
}
|
||||
|
||||
public class GetMethodWithFlagsAndTypes_Patch : FishPatch
|
||||
public sealed class GetMethodWithFlagsAndTypes_Patch : FishPatch
|
||||
{
|
||||
public override string Description { get; } = "Caches GetMethod lookups";
|
||||
|
||||
@@ -244,7 +246,7 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
new(types)).Info = __result;
|
||||
}
|
||||
|
||||
public class GetConstructor_Patch : FishPatch
|
||||
public sealed class GetConstructor_Patch : FishPatch
|
||||
{
|
||||
public override string Description { get; } = "Caches GetConstructor lookups";
|
||||
|
||||
@@ -285,11 +287,12 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
= __result;
|
||||
}
|
||||
|
||||
public class GetProperty_Patch : FishPatch
|
||||
public sealed class GetProperty_Patch : FishPatch
|
||||
{
|
||||
public override string Description { get; } = "Caches GetProperty lookups";
|
||||
|
||||
public override Expression<Action> TargetMethod { get; }
|
||||
// ReSharper disable once ReturnValueOfPureMethodIsNotUsed
|
||||
= static () => default(Type)!.GetProperty(null!, default(BindingFlags));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -322,7 +325,7 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
= __result;
|
||||
}
|
||||
|
||||
public class GetFullName : FishPatch
|
||||
public sealed class GetFullName : FishPatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "Normally averages at over 1000ns per call. Caching makes this about 30 times faster, including "
|
||||
@@ -354,7 +357,6 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
ref var centralCache
|
||||
= ref TypeFullNameCache.GetDirectly.GetOrAddReference(instance.TypeHandle);
|
||||
cache.Name = centralCache.Name;
|
||||
// cache.Cached = centralCache.Cached;
|
||||
}
|
||||
|
||||
return cache.Cached;
|
||||
@@ -374,14 +376,12 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
{
|
||||
ref var cache = ref TypeFullNameCache.GetExistingReference(__instance.TypeHandle);
|
||||
cache.Name = __result;
|
||||
// cache.Cached = 1;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
ref var centralCache
|
||||
= ref TypeFullNameCache.GetDirectly.GetReference(__instance.TypeHandle);
|
||||
centralCache.Name = __result;
|
||||
// centralCache.Cached = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,6 +390,7 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
|
||||
// probably never actually needed
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[SuppressMessage("Globalization", "CA1308")]
|
||||
private static string ToLowerIfNeededForBindingFlags(string name, BindingFlags bindingAttr)
|
||||
=> (bindingAttr & BindingFlags.IgnoreCase) != 0 ? name.ToLowerInvariant() : name;
|
||||
|
||||
@@ -436,9 +437,9 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
}
|
||||
}
|
||||
|
||||
public class MonoCustomAttrs
|
||||
public sealed class MonoCustomAttrs
|
||||
{
|
||||
public class GetCustomAttributes : FishPatch
|
||||
public sealed class GetCustomAttributes : FishPatch
|
||||
{
|
||||
public override string? Description { get; }
|
||||
= "Caches attributes for reflection lookups. Decent load time improvement.";
|
||||
@@ -544,18 +545,24 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
=> _hashCode;
|
||||
}
|
||||
|
||||
public class ActivatorPatches
|
||||
public sealed class ActivatorPatches
|
||||
{
|
||||
public class CreateInstance_Type : FishPatch
|
||||
public sealed class CreateInstance_Type : FishPatch
|
||||
{
|
||||
public override List<string> IncompatibleModIDs { get; } = [PackageIDs.MULTIPLAYER];
|
||||
|
||||
public override string? Description { get; }
|
||||
= "Optimizes the parameterless Activator.CreateInstance method by invoking through specialized cached "
|
||||
+ "delegates";
|
||||
|
||||
public override Delegate? TargetMethodGroup { get; } = (Func<Type, object>)Activator.CreateInstance;
|
||||
|
||||
public static bool Prefix(Type type, ref object __result)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Prefix(Type? type, ref object __result)
|
||||
{
|
||||
if (type is null)
|
||||
return true;
|
||||
|
||||
ref var cache = ref ActivatorCache.GetOrAddReference(type.TypeHandle);
|
||||
|
||||
if (cache.Dirty
|
||||
@@ -570,7 +577,7 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
}
|
||||
}
|
||||
|
||||
// public class CreateInstance_Type_Args : FishPatch
|
||||
// public sealed class CreateInstance_Type_Args : FishPatch
|
||||
// {
|
||||
// public override Delegate? TargetMethodGroup { get; }
|
||||
// = (Func<Type, object[], object>)Activator.CreateInstance;
|
||||
@@ -669,9 +676,9 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
// }
|
||||
// }
|
||||
|
||||
public class FieldInfoPatches
|
||||
public sealed class FieldInfoPatches
|
||||
{
|
||||
public class GetValue_Patch : FishPatch
|
||||
public sealed class GetValue_Patch : FishPatch
|
||||
{
|
||||
public override string Description { get; }
|
||||
= "Optimizes the GetValue method by invoking it through specialized cached delegates";
|
||||
@@ -705,7 +712,7 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
}
|
||||
}
|
||||
|
||||
public class SetValue_Patch : FishPatch
|
||||
public sealed class SetValue_Patch : FishPatch
|
||||
{
|
||||
public override string Description { get; }
|
||||
= "Optimizes the SetValue method by invoking it through specialized cached delegates";
|
||||
@@ -718,29 +725,45 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
{
|
||||
ref var cache = ref FieldSetters.GetOrAddReference(__instance.FieldHandle);
|
||||
|
||||
if (cache.Dirty
|
||||
&& !FieldSetters.UpdateAsyncCache<FieldInfoSetterCache, FieldInfo, FieldSetter>(ref cache,
|
||||
__instance.FieldHandle, __instance))
|
||||
{
|
||||
if (cache.Dirty && !TryUpdate(__instance, ref cache))
|
||||
return true;
|
||||
}
|
||||
|
||||
cache.Result!(obj, value);
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static bool TryUpdate(FieldInfo __instance, ref FieldInfoSetterCache cache)
|
||||
=> cache.Task != _fieldSetterFailureTask && __instance.IsConst()
|
||||
? ErrorForInvalidCallAttempt(__instance, ref cache)
|
||||
: FieldSetters.UpdateAsyncCache<FieldInfoSetterCache, FieldInfo, FieldSetter>(ref cache,
|
||||
__instance.FieldHandle, __instance);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static bool ErrorForInvalidCallAttempt(FieldInfo info, ref FieldInfoSetterCache cache)
|
||||
{
|
||||
cache.Task = _fieldSetterFailureTask;
|
||||
Log.Error($"FieldInfo.SetValue has been called on '{
|
||||
info.FullDescription()}'. This is a const that gets copied at compile time. An attempt at "
|
||||
+ $"setting a value will always fail.");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static Task<FieldSetter> _fieldSetterFailureTask = new(static () => null!);
|
||||
|
||||
public static FieldGetter? MakeGetterDelegate(FieldInfo info)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dm = new DynamicMethod($"FieldGetter_{info.Name}", typeof(object), new[] { typeof(object) },
|
||||
var dm = new DynamicMethod($"FieldGetter_{info.Name}", typeof(object), [typeof(object)],
|
||||
GetOwnerType(info), true);
|
||||
var il = dm.GetILGenerator();
|
||||
|
||||
TryEmitFieldInstanceArgument(il, info);
|
||||
|
||||
il.Emit(info is { IsLiteral: true, IsInitOnly: false }
|
||||
il.Emit(info.IsConst()
|
||||
? FishTranspiler.Constant(info.GetRawConstantValue())
|
||||
: FishTranspiler.Field(info));
|
||||
|
||||
@@ -755,7 +778,7 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
{
|
||||
Log.Error($"PerformanceFish failed to generate an optimized delegate for {
|
||||
info.FullDescription()}. Reverting to default behaviour instead.\n{ex}\n{
|
||||
new StackTrace() /*StackTraceUtility.ExtractStackTrace()*/}");
|
||||
Environment.StackTrace}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -765,7 +788,7 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
try
|
||||
{
|
||||
var dm = new DynamicMethod($"FieldSetter_{info.Name}", typeof(void),
|
||||
new[] { typeof(object), typeof(object) }, GetOwnerType(info), true);
|
||||
[typeof(object), typeof(object)], GetOwnerType(info), true);
|
||||
var il = dm.GetILGenerator();
|
||||
|
||||
TryEmitFieldInstanceArgument(il, info);
|
||||
@@ -776,18 +799,17 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
{
|
||||
il.Emit(FishTranspiler.Call(typeof(ReflectionCaching),
|
||||
info.FieldType.IsNullable() ? nameof(UnboxNullableSafely) : nameof(UnboxSafely),
|
||||
new[] { typeof(object) },
|
||||
new[]
|
||||
{
|
||||
[typeof(object)],
|
||||
[
|
||||
info.FieldType.IsNullable() // TODO: verify that this is actually correct
|
||||
? Nullable.GetUnderlyingType(info.FieldType)
|
||||
: info.FieldType
|
||||
}));
|
||||
]));
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(FishTranspiler.Call(typeof(ReflectionCaching), nameof(CastOrConvert),
|
||||
generics: new[] { info.FieldType }));
|
||||
generics: [info.FieldType]));
|
||||
}
|
||||
|
||||
il.Emit(FishTranspiler.StoreField(info));
|
||||
@@ -800,7 +822,7 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
{
|
||||
Log.Error($"PerformanceFish failed to generate an optimized delegate for {
|
||||
info.FullDescription()}. Reverting to default behaviour instead.\n{ex}\n{
|
||||
new StackTrace() /*StackTraceUtility.ExtractStackTrace()*/}");
|
||||
Environment.StackTrace}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -839,9 +861,9 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
public delegate void FieldSetter(object? obj, object? value);
|
||||
}
|
||||
|
||||
public class MethodBasePatches
|
||||
public sealed class MethodBasePatches
|
||||
{
|
||||
public class Invoke_Patch : FishPatch
|
||||
public sealed class Invoke_Patch : FishPatch
|
||||
{
|
||||
public override bool DefaultState => false; // TODO: fix
|
||||
|
||||
@@ -885,6 +907,7 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
}
|
||||
|
||||
// Similar to HarmonyLib.MethodInvoker, but correctly handles a couple of edge cases Harmony would throw on.
|
||||
[SuppressMessage("Globalization", "CA1310")]
|
||||
public static MethodInvoker? MakeInvokeDelegate(MethodBase methodBase)
|
||||
{
|
||||
if (methodBase.DeclaringType is null && methodBase.Name.StartsWith("PadMethod"))
|
||||
@@ -902,7 +925,7 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
return null;
|
||||
|
||||
var dm = new DynamicMethod($"MethodInvoker_{methodBase.Name}", typeof(object),
|
||||
new[] { typeof(object), typeof(object[]) },
|
||||
[typeof(object), typeof(object[])],
|
||||
GetOwnerType(methodBase), true);
|
||||
var il = dm.GetILGenerator();
|
||||
|
||||
@@ -937,7 +960,7 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
{
|
||||
Log.Warning($"PerformanceFish failed to generate an optimized delegate for {
|
||||
Reflection.FullDescription(methodBase)}. Reverting to default behaviour instead.\n{ex}\n{
|
||||
new StackTrace() /*StackTraceUtility.ExtractStackTrace()*/}");
|
||||
Environment.StackTrace}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -961,15 +984,14 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
il.Emit(parameterType.IsValueType
|
||||
? FishTranspiler.Call(typeof(ReflectionCaching),
|
||||
parameterType.IsNullable() ? nameof(UnboxNullableSafely) : nameof(UnboxSafely),
|
||||
new[] { typeof(object) },
|
||||
new[]
|
||||
{
|
||||
[typeof(object)],
|
||||
[
|
||||
parameterType.IsNullable()
|
||||
? Nullable.GetUnderlyingType(parameterType)
|
||||
: parameterType
|
||||
})
|
||||
])
|
||||
: FishTranspiler.Call(typeof(ReflectionCaching), nameof(CastOrConvert),
|
||||
generics: new[] { parameterType }));
|
||||
generics: [parameterType]));
|
||||
}
|
||||
|
||||
private static void EmitInvokeByRefParameterLoad(ILGenerator il, Type parameterType)
|
||||
@@ -981,7 +1003,7 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
if (parameterType.IsNullable())
|
||||
{
|
||||
il.Emit(FishTranspiler.Call(typeof(ReflectionCaching), nameof(VerifyNullableBoxObject),
|
||||
generics: new[] { Nullable.GetUnderlyingType(parameterType) }));
|
||||
generics: [Nullable.GetUnderlyingType(parameterType)]));
|
||||
|
||||
il.Emit(FishTranspiler.UnboxAddress(parameterType));
|
||||
}
|
||||
@@ -989,16 +1011,13 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
{
|
||||
il.Emit(parameterType.IsValueType
|
||||
? FishTranspiler.Call(typeof(ReflectionCaching), nameof(UnboxRefSafely),
|
||||
// new[] { typeof(object).MakeByRefType() },
|
||||
generics: new[]
|
||||
{
|
||||
generics:
|
||||
[
|
||||
/*parameterType.IsNullable()
|
||||
? Nullable.GetUnderlyingType(parameterType)
|
||||
:*/ parameterType
|
||||
})
|
||||
: FishTranspiler.Call(typeof(ReflectionCaching), nameof(CastRefSafely),
|
||||
// new[] { typeof(object).MakeByRefType() },
|
||||
generics: new[] { parameterType }));
|
||||
])
|
||||
: FishTranspiler.Call(typeof(ReflectionCaching), nameof(CastRefSafely), generics: [parameterType]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1022,14 +1041,12 @@ public class ReflectionCaching : ClassWithFishPatches
|
||||
if (declaringType.IsValueType)
|
||||
{
|
||||
il.Emit(FishTranspiler.ArgumentAddress(0));
|
||||
il.Emit(FishTranspiler.Call(typeof(ReflectionCaching), nameof(UnboxRefSafely),
|
||||
generics: new[] { declaringType }));
|
||||
il.Emit(FishTranspiler.Call(typeof(ReflectionCaching), nameof(UnboxRefSafely), generics: [declaringType]));
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(FishTranspiler.This);
|
||||
il.Emit(FishTranspiler.Call(typeof(ReflectionCaching), nameof(CastOrConvert),
|
||||
generics: new[] { declaringType }));
|
||||
il.Emit(FishTranspiler.Call(typeof(ReflectionCaching), nameof(CastOrConvert), generics: [declaringType]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user