#nullable enable
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
namespace ShrinkContext
{
///
/// 一等上下文(论文 Γ∞ 的运行时对应物)。
/// - 树结构:由 派生隔离子上下文,或由运行时为纤程派生子上下文;
/// - 效应:本上下文注册的可逆效应按注册逆序(LIFO)在 中回滚;
/// - 余效应:Set/Get 委托给运行时共享存储,键先经隔离表解析为 realm 再查表。
///
public sealed class ShrinkCtx
{
private readonly List _effects = new();
private readonly Dictionary? _isolateOverlay;
private Dictionary>? _interceptOverlay;
internal ShrinkCtx(ShrinkContextRuntime runtime, ShrinkCtx? parent, ShrinkFiber? ownerFiber,
Dictionary? isolateOverlay,
IReadOnlyDictionary>? interceptOverlay = null)
{
Runtime = runtime;
Parent = parent;
OwnerFiber = ownerFiber;
_isolateOverlay = isolateOverlay;
ReplaceIntercept(interceptOverlay);
}
public ShrinkContextRuntime Runtime { get; }
public ShrinkCtx? Parent { get; }
public ShrinkFiber? OwnerFiber { get; }
// ---------------- 效应(论文 3.1 / 算法 1) ----------------
/// 同步前向 + 可选同步逆操作的可逆效应;前向立即执行。
public ShrinkEffectHandle Effect(Action forward, Action? inverse = null)
{
if (forward == null)
throw new ArgumentNullException(nameof(forward));
forward();
var handle = new ShrinkEffectHandle();
if (inverse != null)
handle.AttachInverse(() =>
{
inverse();
return UniTask.CompletedTask;
});
Attach(handle);
return handle;
}
/// 异步前向,完成时产出异步逆操作(论文 𝔈Γ)。DisposeAsync 会等待进行中的前向。
public async UniTask EffectAsync(Func>> effectFn)
{
if (effectFn == null)
throw new ArgumentNullException(nameof(effectFn));
var handle = new ShrinkEffectHandle();
var gate = new UniTaskCompletionSource();
handle.BeginForward(gate.Task);
Attach(handle);
Func? inverse = null;
try
{
inverse = await effectFn();
handle.CompleteForward(inverse);
}
finally
{
// 先登记逆操作再放行 gate,保证等待前向的 DisposeAsync 能读到完整逆链
gate.TrySetResult();
}
return handle;
}
internal void Attach(ShrinkEffectHandle handle) => _effects.Add(handle);
///
/// 前向已在外部执行完毕的效应:仅登记逆操作(供桥接适配器把既有 API 的"注册/注销"对包装为可逆效应)。
///
public ShrinkEffectHandle EffectInverse(Func inverse)
{
if (inverse == null)
throw new ArgumentNullException(nameof(inverse));
var handle = new ShrinkEffectHandle();
handle.AttachInverse(inverse);
Attach(handle);
return handle;
}
/// 按 LIFO 顺序执行本上下文注册的全部逆操作。
public async UniTask DisposeAsync()
{
for (var i = _effects.Count - 1; i >= 0; i--)
await _effects[i].DisposeAsync();
_effects.Clear();
}
// ---------------- 余效应(论文 3.2 / 算法 2) ----------------
///
/// 提供依赖(set 是可逆效应):立即安装绑定并通知依赖者;
/// 逆操作撤回绑定并再次通知。句柄同时挂在当前上下文上,随上下文卸载自动撤回。
///
public ShrinkEffectHandle Set(string key, T value) => Runtime.SetBinding(this, key, value, null);
/// 提供带访问策略的依赖;策略只在介导 Get 时运行,原始编排查找不会运行策略。
public ShrinkEffectHandle Set(string key, T value, IShrinkCoeffectAccessPolicy accessPolicy) =>
Runtime.SetBinding(this, key, value, accessPolicy ?? throw new ArgumentNullException(nameof(accessPolicy)));
public ShrinkEffectHandle Set(ShrinkKey key, T value) =>
Set((key ?? throw new ArgumentNullException(nameof(key))).Id, value);
public ShrinkEffectHandle Set(ShrinkKey key, T value, IShrinkCoeffectAccessPolicy accessPolicy) =>
Set((key ?? throw new ArgumentNullException(nameof(key))).Id, value, accessPolicy);
///
/// 代理介导的依赖访问(论文算法 6):沿纤程链解析已提交视图;
/// 声明了但未激活的键抛 ,
/// 全链未声明的键抛 。
///
public T Get(string key)
{
var fiber = OwnerFiber;
while (fiber != null)
{
if (fiber.Committed != null && fiber.Committed.TryGetValue(key, out var binding))
return Runtime.ResolveAccess(this, key, binding);
if (fiber.InjectSet.Contains(key))
throw new ShrinkInactiveAccessException(key, fiber.Name);
fiber = fiber.Parent;
}
throw new ShrinkUndeclaredAccessException(key);
}
public T Get(ShrinkKey key) => Get(
(key ?? throw new ArgumentNullException(nameof(key))).Id);
/// 与 相同的介导规则;未激活返回 false 而不抛异常。
public bool TryGet(string key, out T value)
{
var fiber = OwnerFiber;
while (fiber != null)
{
if (fiber.Committed != null && fiber.Committed.TryGetValue(key, out var binding))
{
value = Runtime.ResolveAccess(this, key, binding);
return true;
}
if (fiber.InjectSet.Contains(key))
break;
fiber = fiber.Parent;
}
value = default!;
return false;
}
public bool TryGet(ShrinkKey key, out T value) => TryGet(
(key ?? throw new ArgumentNullException(nameof(key))).Id, out value);
///
/// 派生隔离子上下文(论文定义 28/29 的派生实现):
/// 覆盖 key 的 realm 解析;父上下文不受影响;丢弃子上下文即隐式恢复。
///
public ShrinkCtx Isolate(string key, string realm)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentException("Key must not be null or empty.", nameof(key));
if (string.IsNullOrEmpty(realm))
throw new ArgumentException("Realm must not be null or empty.", nameof(realm));
return new ShrinkCtx(Runtime, this, OwnerFiber, new Dictionary { [key] = realm });
}
public ShrinkCtx Isolate(ShrinkKey key, string realm) => Isolate(
(key ?? throw new ArgumentNullException(nameof(key))).Id, realm);
///
/// 派生带拦截元数据的子上下文。子级同名 metadata 覆盖父级;丢弃子上下文即恢复。
/// 该操作不改变 realm、provider 或 target,因此不会触发 fiber 重载。
///
public ShrinkCtx Intercept(string key, IReadOnlyDictionary metadata)
{
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentException("Key must not be null or empty.", nameof(key));
if (metadata == null)
throw new ArgumentNullException(nameof(metadata));
var metadataCopy = new Dictionary(StringComparer.Ordinal);
foreach (var pair in metadata)
metadataCopy[pair.Key] = pair.Value;
var overlay = new Dictionary>(StringComparer.Ordinal)
{
[key] = metadataCopy
};
return new ShrinkCtx(Runtime, this, OwnerFiber, null, overlay);
}
public ShrinkCtx Intercept(ShrinkKey key, IReadOnlyDictionary metadata) =>
Intercept((key ?? throw new ArgumentNullException(nameof(key))).Id, metadata);
internal ShrinkCtx CreateChildForFiber(ShrinkFiber fiber) => new(Runtime, this, fiber, null);
/// 在派生子上下文中叠加隔离覆盖(同键后者优先),供加载器为条目配置隔离域。
internal ShrinkCtx WithIsolate(IReadOnlyDictionary isolate)
{
var overlay = new Dictionary();
if (_isolateOverlay != null)
{
foreach (var pair in _isolateOverlay)
overlay[pair.Key] = pair.Value;
}
foreach (var pair in isolate)
overlay[pair.Key] = pair.Value;
return new ShrinkCtx(Runtime, this, OwnerFiber, overlay);
}
/// 在当前纤程上下文上叠加 loader 条目配置的 intercept。
internal ShrinkCtx WithIntercept(
IReadOnlyDictionary> intercept) =>
new(Runtime, this, OwnerFiber, null, interceptOverlay: intercept);
/// 原地替换本层 intercept,供 loader 在不重载 fiber 的情况下更新访问策略。
internal void ReplaceIntercept(
IReadOnlyDictionary>? intercept)
{
if (intercept == null || intercept.Count == 0)
{
_interceptOverlay = null;
return;
}
var copy = new Dictionary>(StringComparer.Ordinal);
foreach (var pair in intercept)
{
if (string.IsNullOrWhiteSpace(pair.Key))
throw new ArgumentException("Intercept key must not be empty.", nameof(intercept));
if (pair.Value == null)
throw new ArgumentException($"Intercept metadata for '{pair.Key}' must not be null.",
nameof(intercept));
var metadataCopy = new Dictionary(StringComparer.Ordinal);
foreach (var metadataPair in pair.Value)
metadataCopy[metadataPair.Key] = metadataPair.Value;
copy[pair.Key] = metadataCopy;
}
_interceptOverlay = copy;
}
/// 解析 key 在本上下文中归属的 realm:沿父链找最近的隔离覆盖,默认 realm 为 key 自身。
internal string ResolveRealm(string key)
{
var current = this;
while (current != null)
{
if (current._isolateOverlay != null && current._isolateOverlay.TryGetValue(key, out var realm))
return realm;
current = current.Parent;
}
return key;
}
/// 按根到叶顺序合并拦截元数据;越接近访问方的上下文优先。
internal IReadOnlyDictionary ResolveInterceptMetadata(string key)
{
var chain = new Stack();
for (var current = this; current != null; current = current.Parent)
chain.Push(current);
var merged = new Dictionary(StringComparer.Ordinal);
while (chain.Count > 0)
{
var current = chain.Pop();
if (current._interceptOverlay == null ||
!current._interceptOverlay.TryGetValue(key, out var metadata))
continue;
foreach (var pair in metadata)
merged[pair.Key] = pair.Value;
}
return merged;
}
internal IReadOnlyCollection GetLocalInterceptKeys() =>
_interceptOverlay?.Keys ?? (IReadOnlyCollection)Array.Empty();
}
}