// MIT License - Copyright (c) 2025 wallstop
// Full license text: https://github.com/wallstop/unity-helpers/blob/main/LICENSE
#if !((UNITY_WEBGL && !UNITY_EDITOR) || ENABLE_IL2CPP)
#define EMIT_DYNAMIC_IL
#define SUPPORT_EXPRESSION_COMPILE
#endif
namespace WallstopStudios.UnityHelpers.Core.Helper
{
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
#if !SINGLE_THREADED
using System.Collections.Concurrent;
#endif
// ReflectionHelpers.Factory.cs - Delegate creation and strategy management
// See ReflectionHelpers.cs for full architecture documentation
public static partial class ReflectionHelpers
{
///
/// Specifies which implementation strategy to use for creating reflection delegates.
///
///
/// The tries strategies in order: Expressions → DynamicIl → Reflection.
/// Failed strategies are tracked in a blocklist to avoid repeated attempts.
///
internal enum ReflectionDelegateStrategy
{
[Obsolete("Use a concrete strategy value.", false)]
Unknown = 0,
Expressions = 1,
DynamicIl = 2,
Reflection = 3,
}
///
/// Internal factory responsible for creating, caching, and managing reflection delegates.
///
///
///
/// For each member type (FieldInfo, PropertyInfo, MethodInfo, ConstructorInfo), this factory:
///
///
/// - Checks if a delegate is already cached for the requested strategy
/// - Checks if the strategy has previously failed (blocklist)
/// - Attempts to create a new delegate using the strategy
/// - On failure, marks the strategy as unavailable and tries the next
/// - Caches successful delegates for future use
///
///
/// The factory uses to uniquely identify cache entries
/// by both member and strategy, allowing different strategies to coexist in the cache.
///
///
private static class DelegateFactory
{
private const byte StrategyUnavailableSentinel = 0;
private readonly struct CapabilityKey : IEquatable>
{
internal CapabilityKey(T member, ReflectionDelegateStrategy strategy)
{
Member = member;
Strategy = strategy;
}
internal T Member { get; }
internal ReflectionDelegateStrategy Strategy { get; }
public bool Equals(CapabilityKey other)
{
return Strategy == other.Strategy
&& EqualityComparer.Default.Equals(Member, other.Member);
}
public override bool Equals(object obj)
{
if (obj is CapabilityKey other)
{
return Equals(other);
}
return false;
}
public override int GetHashCode()
{
return Objects.HashCode(Member, Strategy);
}
}
private sealed class StrategyHolder
{
internal StrategyHolder(
ReflectionDelegateStrategy strategy,
object memberKey,
Type delegateType
)
{
Strategy = strategy;
MemberKey = memberKey;
DelegateType = delegateType;
}
internal ReflectionDelegateStrategy Strategy { get; }
internal object MemberKey { get; }
internal Type DelegateType { get; }
internal static StrategyHolder Create(
CapabilityKey key,
Type delegateType
)
{
object memberKey = key.Member is null ? NullMemberKey : key.Member;
return new StrategyHolder(key.Strategy, memberKey, delegateType);
}
private static readonly object NullMemberKey = new();
}
private static readonly ConditionalWeakTable<
Delegate,
StrategyHolder
> DelegateStrategyTable = new();
#if !SINGLE_THREADED
private static readonly ConcurrentDictionary<
CapabilityKey,
Func