using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using XFESystem.Collections;
namespace XFESystem.Kernel;
/// Actual guest gate for the interpreter's owned ephemeron storage.
/// Host success is a fixture control only, not proof about the native collector.
public static class NativeWeakCollectionsRegression
{
public static int ChecksPassed { get; private set; }
public static int Failure { get; private set; }
public static bool LiveValuePreserved { get; private set; }
public static bool UpdatedValuePreserved { get; private set; }
public static bool DeadKeyCycleCollected { get; private set; }
public static bool DeadTablePruned { get; private set; }
public static bool ReleasedScopeRejectsAccess { get; private set; }
public static bool HandleCountRestored { get; private set; }
public static bool NativeHandleCountChecked { get; private set; }
public static ulong DependentHandlesBefore { get; private set; }
public static ulong DependentHandlesAfter { get; private set; }
public static bool Run(Func? dependentHandleCount = null)
{
Failure = ChecksPassed = 0;
LiveValuePreserved = UpdatedValuePreserved = DeadKeyCycleCollected = DeadTablePruned = ReleasedScopeRejectsAccess = false;
NativeHandleCountChecked = dependentHandleCount is not null;
// Initialize the collection and ObjectDisposedException paths before
// measuring native slots; every warmup handle is explicitly released.
bool warmup = Warmup();
DependentHandlesBefore = dependentHandleCount?.Invoke() ?? 0;
var live = CreateLive();
CollectTwice();
LiveValuePreserved = VerifyLive(live, 17);
Record(LiveValuePreserved, 1);
ReplaceValue(live);
CollectTwice();
UpdatedValuePreserved = VerifyLive(live, 42) && live.Scope.Lifetime is { Entries: 1, Created: 1, Disposed: 0 };
Record(UpdatedValuePreserved, 2);
live.WeakValue.Free(); live.Scope.Release();
GC.KeepAlive(live);
var cycle = CreateDeadKeyCycle();
CollectTwice();
var cycleLifetime = cycle.Scope.Prune();
DeadKeyCycleCollected = cycle.WeakKey.Target is null && cycle.WeakValue.Target is null &&
cycleLifetime is { Tables: 1, Entries: 0, Created: 1, Disposed: 1 };
Record(DeadKeyCycleCollected, 3);
cycle.WeakKey.Free(); cycle.WeakValue.Free(); cycle.Scope.Release();
GC.KeepAlive(cycle.Table);
var abandoned = CreateDeadTable();
CollectTwice();
bool tableDied = abandoned.WeakTable.Target is null;
var tableLifetime = abandoned.Scope.Prune();
CollectTwice();
DeadTablePruned = tableDied && abandoned.WeakValue.Target is null &&
tableLifetime is { Tables: 0, Entries: 0, Created: 1, Disposed: 1, TableHandlesCreated: 1, TableHandlesDisposed: 1 };
Record(DeadTablePruned, 4);
abandoned.WeakTable.Free(); abandoned.WeakValue.Free(); abandoned.Scope.Release();
GC.KeepAlive(abandoned.Key);
ReleasedScopeRejectsAccess = warmup && VerifyReleasedScope();
Record(ReleasedScopeRejectsAccess, 5);
DependentHandlesAfter = dependentHandleCount?.Invoke() ?? 0;
HandleCountRestored = !NativeHandleCountChecked || DependentHandlesAfter == DependentHandlesBefore;
Record(HandleCountRestored, 6);
return Failure == 0 && ChecksPassed == 6;
}
private static void Record(bool passed, int failure)
{
if (passed) ChecksPassed++;
else if (Failure == 0) Failure = failure;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static bool Warmup()
{
var scope = new WeakCollectionScope(); var table = new OwnedWeakTable