using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// Exercises the Microsoft NativeAOT runtime contracts that the bare-metal PAL
// must support. Compilation alone is never a guest runtime verification.
internal static class Program
{
[DllImport("xfe_pal", EntryPoint = "xfe_runtime_probe_report")]
private static extern void Report(int stage, long value);
private sealed class Root(int value) { internal int Value = value; }
private sealed class DependentValue(object owner)
{
internal object Owner = owner;
~DependentValue() => Interlocked.Increment(ref finalized);
}
[ThreadStatic] private static int threadToken;
private static readonly object Gate = new();
private static int finalized, stopWorker, iterations, workerResult, zeroDivisor;
private static bool workerReady;
private static int Main()
{
var table = new ConditionalWeakTable<object, Root>();
var key = new object();
table.Add(key, new Root(42));
byte[] bytes = new byte[32768]; bytes[^1] = 211;
threadToken = 7;
GC.Collect();
if (table.GetValue(key, _ => new Root(-1)).Value != 42 || bytes[^1] != 211 || threadToken != 7)
return 1;
Report(1, GC.GetTotalMemory(false));
int exceptionMask = 0;
try
{
try { Throw(); }
catch (InvalidOperationException) { exceptionMask |= 1; }
try { _ = ReadNull(null); }
catch (NullReferenceException) { exceptionMask |= 2; }
try { _ = Divide(70, Volatile.Read(ref zeroDivisor)); }
catch (DivideByZeroException) { exceptionMask |= 4; }
try { _ = Divide(int.MinValue, Volatile.Read(ref zeroDivisor) - 1); }
catch (OverflowException) { exceptionMask |= 8; }
try { _ = DivideLong(long.MinValue, Volatile.Read(ref zeroDivisor) - 1); }
catch (OverflowException) { exceptionMask |= 16; }
}
finally { Report(2, exceptionMask == 31 ? 1 : 0); }
if (exceptionMask != 31) return 2;
var cycleTable = new ConditionalWeakTable<object, DependentValue>();
CreateUnrootedCycle(cycleTable, out WeakReference weakKey, out WeakReference weakValue);
for (int i = 0; i < 4; i++) { GC.Collect(); GC.WaitForPendingFinalizers(); }
if (weakKey.IsAlive || weakValue.IsAlive || Volatile.Read(ref finalized) != 1) return 3;
GC.KeepAlive(cycleTable);
Report(3, 1);
var worker = new Thread(Worker);
worker.Start();
lock (Gate)
while (!workerReady)
if (!Monitor.Wait(Gate, 5000)) return 4;
if (Volatile.Read(ref workerResult) != 0) return 4;
Report(4, 1);
for (int i = 0; i < 8; i++)
{
_ = new byte[64 * 1024];
GC.Collect(2, GCCollectionMode.Forced, blocking: true, compacting: true);
if (Volatile.Read(ref workerResult) != 0 || bytes[^1] != 211 || table.GetValue(key, _ => new Root(-1)).Value != 42)
return 5;
Thread.Yield();
}
if (Volatile.Read(ref iterations) < 8 || threadToken != 7) return 5;
Report(5, 1);
Volatile.Write(ref stopWorker, 1);
if (!worker.Join(5000) || Volatile.Read(ref workerResult) != 1 || threadToken != 7) return 6;
Report(6, 1);
GC.KeepAlive(key);
GC.KeepAlive(bytes);
return 0;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void Throw() => throw new InvalidOperationException("NativeAOT EH probe");
[MethodImpl(MethodImplOptions.NoInlining)]
private static int ReadNull(Root? value) => value!.Value;
[MethodImpl(MethodImplOptions.NoInlining)]
private static int Divide(int numerator, int denominator) => numerator / denominator;
[MethodImpl(MethodImplOptions.NoInlining)]
private static long DivideLong(long numerator, long denominator) => numerator / denominator;
[MethodImpl(MethodImplOptions.NoInlining)]
private static void CreateUnrootedCycle(ConditionalWeakTable<object, DependentValue> table, out WeakReference keyReference, out WeakReference valueReference)
{
var key = new object(); var value = new DependentValue(key);
table.Add(key, value); keyReference = new WeakReference(key); valueReference = new WeakReference(value);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void Worker()
{
try
{
threadToken = 91;
var root = new Root(91);
var retained = new byte[65536]; retained[0] = 17; retained[^1] = 193;
lock (Gate) { workerReady = true; Monitor.PulseAll(Gate); }
while (Volatile.Read(ref stopWorker) == 0)
{
var allocation = new byte[4096]; allocation[^1] = 113;
if (root.Value != 91 || retained[0] != 17 || retained[^1] != 193 || allocation[^1] != 113 || threadToken != 91)
{ Volatile.Write(ref workerResult, -1); return; }
Interlocked.Increment(ref iterations);
Thread.SpinWait(64);
}
try { Throw(); }
catch (InvalidOperationException) { Volatile.Write(ref workerResult, 1); }
GC.KeepAlive(root); GC.KeepAlive(retained);
}
catch (Exception) { Volatile.Write(ref workerResult, -2); }
finally { if (threadToken != 91) Volatile.Write(ref workerResult, -3); }
}
}
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// Exercises the Microsoft NativeAOT runtime contracts that the bare-metal PAL
// must support. Compilation alone is never a guest runtime verification.
internal static class Program
{
[DllImport("xfe_pal", EntryPoint = "xfe_runtime_probe_report")]
private static extern void Report(int stage, long value);
private sealed class Root(int value) { internal int Value = value; }
private sealed class DependentValue(object owner)
{
internal object Owner = owner;
~DependentValue() => Interlocked.Increment(ref finalized);
}
[ThreadStatic] private static int threadToken;
private static readonly object Gate = new();
private static int finalized, stopWorker, iterations, workerResult, zeroDivisor;
private static bool workerReady;
private static int Main()
{
var table = new ConditionalWeakTable<object, Root>();
var key = new object();
table.Add(key, new Root(42));
byte[] bytes = new byte[32768]; bytes[^1] = 211;
threadToken = 7;
GC.Collect();
if (table.GetValue(key, _ => new Root(-1)).Value != 42 || bytes[^1] != 211 || threadToken != 7)
return 1;
Report(1, GC.GetTotalMemory(false));
int exceptionMask = 0;
try
{
try { Throw(); }
catch (InvalidOperationException) { exceptionMask |= 1; }
try { _ = ReadNull(null); }
catch (NullReferenceException) { exceptionMask |= 2; }
try { _ = Divide(70, Volatile.Read(ref zeroDivisor)); }
catch (DivideByZeroException) { exceptionMask |= 4; }
try { _ = Divide(int.MinValue, Volatile.Read(ref zeroDivisor) - 1); }
catch (OverflowException) { exceptionMask |= 8; }
try { _ = DivideLong(long.MinValue, Volatile.Read(ref zeroDivisor) - 1); }
catch (OverflowException) { exceptionMask |= 16; }
}
finally { Report(2, exceptionMask == 31 ? 1 : 0); }
if (exceptionMask != 31) return 2;
var cycleTable = new ConditionalWeakTable<object, DependentValue>();
CreateUnrootedCycle(cycleTable, out WeakReference weakKey, out WeakReference weakValue);
for (int i = 0; i < 4; i++) { GC.Collect(); GC.WaitForPendingFinalizers(); }
if (weakKey.IsAlive || weakValue.IsAlive || Volatile.Read(ref finalized) != 1) return 3;
GC.KeepAlive(cycleTable);
Report(3, 1);
var worker = new Thread(Worker);
worker.Start();
lock (Gate)
while (!workerReady)
if (!Monitor.Wait(Gate, 5000)) return 4;
if (Volatile.Read(ref workerResult) != 0) return 4;
Report(4, 1);
for (int i = 0; i < 8; i++)
{
_ = new byte[64 * 1024];
GC.Collect(2, GCCollectionMode.Forced, blocking: true, compacting: true);
if (Volatile.Read(ref workerResult) != 0 || bytes[^1] != 211 || table.GetValue(key, _ => new Root(-1)).Value != 42)
return 5;
Thread.Yield();
}
if (Volatile.Read(ref iterations) < 8 || threadToken != 7) return 5;
Report(5, 1);
Volatile.Write(ref stopWorker, 1);
if (!worker.Join(5000) || Volatile.Read(ref workerResult) != 1 || threadToken != 7) return 6;
Report(6, 1);
GC.KeepAlive(key);
GC.KeepAlive(bytes);
return 0;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void Throw() => throw new InvalidOperationException("NativeAOT EH probe");
[MethodImpl(MethodImplOptions.NoInlining)]
private static int ReadNull(Root? value) => value!.Value;
[MethodImpl(MethodImplOptions.NoInlining)]
private static int Divide(int numerator, int denominator) => numerator / denominator;
[MethodImpl(MethodImplOptions.NoInlining)]
private static long DivideLong(long numerator, long denominator) => numerator / denominator;
[MethodImpl(MethodImplOptions.NoInlining)]
private static void CreateUnrootedCycle(ConditionalWeakTable<object, DependentValue> table, out WeakReference keyReference, out WeakReference valueReference)
{
var key = new object(); var value = new DependentValue(key);
table.Add(key, value); keyReference = new WeakReference(key); valueReference = new WeakReference(value);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void Worker()
{
try
{
threadToken = 91;
var root = new Root(91);
var retained = new byte[65536]; retained[0] = 17; retained[^1] = 193;
lock (Gate) { workerReady = true; Monitor.PulseAll(Gate); }
while (Volatile.Read(ref stopWorker) == 0)
{
var allocation = new byte[4096]; allocation[^1] = 113;
if (root.Value != 91 || retained[0] != 17 || retained[^1] != 193 || allocation[^1] != 113 || threadToken != 91)
{ Volatile.Write(ref workerResult, -1); return; }
Interlocked.Increment(ref iterations);
Thread.SpinWait(64);
}
try { Throw(); }
catch (InvalidOperationException) { Volatile.Write(ref workerResult, 1); }
GC.KeepAlive(root); GC.KeepAlive(retained);
}
catch (Exception) { Volatile.Write(ref workerResult, -2); }
finally { if (threadToken != 91) Volatile.Write(ref workerResult, -3); }
}
}