Refactor struct serializer generator with explicit opt-in

Changes struct serializer generator to require explicit [GenerateStructSerializer] attribute instead of auto-detecting fields with [EmulatedInline]/[EmulatedPointer]. Major internal refactoring includes:

- Replace implicit detection with explicit opt-in via marker attribute
- Refactor field processing into FieldSlot-based recursive walking for better nested struct/array support
- Add diagnostic reporting for unsupported field types and nesting depth limits
This commit is contained in:
AdvDebug
2026-07-15 20:49:28 +03:00
parent 8c3675da39
commit 09a7669b52
7 changed files with 709 additions and 302 deletions
File diff suppressed because it is too large Load Diff
@@ -4,6 +4,7 @@ using Brovan.Core.Emulation.OS.Linux.Files;
namespace Brovan.Core.Emulation.OS.Linux
{
[GenerateStructSerializer]
public struct Utsname
{
[EmulatedInline(65, Ascii = true)]
@@ -26,6 +27,7 @@ namespace Brovan.Core.Emulation.OS.Linux
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Explicit)]
public struct LinuxStat32
{
@@ -90,6 +92,7 @@ namespace Brovan.Core.Emulation.OS.Linux
public uint __unused5;
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Explicit)]
public struct LinuxStatCompat64
{
@@ -162,6 +165,7 @@ namespace Brovan.Core.Emulation.OS.Linux
public long tv_nsec;
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Explicit)]
public struct LinuxStat64
{
@@ -25,6 +25,16 @@ namespace Brovan.Core.Emulation.OS
public EmulatedInlineAttribute(int Size) => this.Size = Size;
}
/// <summary>
/// Opts a struct into <see cref="Brovan.Generators.StructSerializerGenerator"/>, which emits
/// reflection-free WriteStructDirect_/ParseStructDirect_/GetStructSizeDirect_ methods on
/// <see cref="StructSerializer"/> for it.
/// </summary>
[AttributeUsage(AttributeTargets.Struct)]
public sealed class GenerateStructSerializerAttribute : Attribute
{
}
public enum WriteStructError
{
None, NullDestination, DestinationNotMapped, PointerAllocationFailed,
@@ -186,6 +196,31 @@ namespace Brovan.Core.Emulation.OS
return Cache[T] = new TypeDesc(T);
}
// --- Generated-serializer dispatch ---
internal delegate bool ParseStructAddrDelegate<T>(BinaryEmulator Emulator, ulong Address, out T Value);
internal delegate bool ParseStructBytesDelegate<T>(BinaryEmulator Emulator, byte[] Raw, out T Value);
private static readonly Dictionary<Type, Delegate> DirectWrite = new();
private static readonly Dictionary<Type, Delegate> DirectParseAddr = new();
private static readonly Dictionary<Type, Delegate> DirectParseBytes = new();
private static readonly Dictionary<Type, Delegate> DirectSizeBool = new();
private static readonly Dictionary<Type, Delegate> DirectSizeEmulator = new();
internal static void RegisterDirect<T>(
Func<BinaryEmulator, ulong, T, WriteStructResult> Write,
ParseStructAddrDelegate<T> ParseAddr,
ParseStructBytesDelegate<T> ParseBytes,
Func<bool, int> SizeBool,
Func<BinaryEmulator, int> SizeEmulator) where T : struct
{
DirectWrite[typeof(T)] = Write;
DirectParseAddr[typeof(T)] = ParseAddr;
DirectParseBytes[typeof(T)] = ParseBytes;
DirectSizeBool[typeof(T)] = SizeBool;
DirectSizeEmulator[typeof(T)] = SizeEmulator;
}
// --- Public API ---
/// <summary>
@@ -196,6 +231,9 @@ namespace Brovan.Core.Emulation.OS
/// <returns>return the size.</returns>
public static uint GetStructSize<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] T>(BinaryEmulator Emulator) where T : struct
{
if (DirectSizeEmulator.TryGetValue(typeof(T), out Delegate Fast))
return (uint)((Func<BinaryEmulator, int>)Fast)(Emulator);
bool Is64 = Emulator._binary.Architecture == BinaryArchitecture.x64;
return (uint)GetStructSize(typeof(T), Is64);
}
@@ -208,6 +246,9 @@ namespace Brovan.Core.Emulation.OS
/// <returns>return the size.</returns>
public static int GetStructSize<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] T>(bool Is64) where T : struct
{
if (DirectSizeBool.TryGetValue(typeof(T), out Delegate Fast))
return ((Func<bool, int>)Fast)(Is64);
return GetStructSize(typeof(T), Is64);
}
@@ -232,6 +273,9 @@ namespace Brovan.Core.Emulation.OS
/// <returns>Returns <see cref="WriteStructResult.Ok"/> on success, otherwise a failure result describing the error.</returns>
public static WriteStructResult WriteStruct<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] T>(BinaryEmulator Emulator, ulong Address, T Value) where T : struct
{
if (DirectWrite.TryGetValue(typeof(T), out Delegate Fast))
return ((Func<BinaryEmulator, ulong, T, WriteStructResult>)Fast)(Emulator, Address, Value);
if (Address == 0)
return WriteStructResult.Fail(WriteStructError.NullDestination, $"Destination address is NULL for {typeof(T).Name}");
@@ -293,6 +337,9 @@ namespace Brovan.Core.Emulation.OS
/// <returns>Returns true if successful, otherwise false.</returns>
public static bool ParseStruct<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] T>(BinaryEmulator Emulator, ulong Address, out T Value) where T : struct
{
if (DirectParseAddr.TryGetValue(typeof(T), out Delegate Fast))
return ((ParseStructAddrDelegate<T>)Fast)(Emulator, Address, out Value);
Value = default;
if (Address == 0) return false;
@@ -342,6 +389,9 @@ namespace Brovan.Core.Emulation.OS
/// </remarks>
public static bool ParseStruct<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] T>(BinaryEmulator Emulator, byte[] Data, out T Value) where T : struct
{
if (DirectParseBytes.TryGetValue(typeof(T), out Delegate Fast))
return ((ParseStructBytesDelegate<T>)Fast)(Emulator, Data, out Value);
Value = default;
bool Is64 = Emulator._binary.Architecture == BinaryArchitecture.x64;
@@ -16,8 +16,9 @@ namespace Brovan.Core.Emulation.OS.Windows
private const uint IoctlConDrvGetDisplayMode = 0x00500027;
private const uint IoctlConDrvSetDisplayMode = 0x0050002B;
[GenerateStructSerializer]
[StructLayout(LayoutKind.Explicit, Size = 0x30)]
private struct ConsoleApiMsgHeader
internal struct ConsoleApiMsgHeader
{
[FieldOffset(0x28)] public uint ApiNumber;
[FieldOffset(0x2C)] public uint ApiDescriptorSize;
@@ -8,7 +8,8 @@ namespace Brovan.Core.Emulation.OS.Windows
internal class NtConnectPort : IWinSyscall
{
[StructLayout(LayoutKind.Sequential)]
private struct PORT_VIEW64
[GenerateStructSerializer]
internal struct PORT_VIEW64
{
public uint Length;
public uint Padding0;
@@ -20,8 +21,9 @@ namespace Brovan.Core.Emulation.OS.Windows
public ulong ViewRemoteBase;
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Sequential)]
private struct REMOTE_PORT_VIEW64
internal struct REMOTE_PORT_VIEW64
{
public uint Length;
public uint Padding0;
@@ -177,7 +177,8 @@ namespace Brovan.Core.Emulation.OS.Windows.Win32k
}
[StructLayout(LayoutKind.Explicit, Size = 80)]
private struct WNDCLASSEXW64
[GenerateStructSerializer]
internal struct WNDCLASSEXW64
{
[FieldOffset(0)] public uint cbSize;
[FieldOffset(4)] public uint style;
@@ -193,8 +194,9 @@ namespace Brovan.Core.Emulation.OS.Windows.Win32k
[FieldOffset(72)] public ulong hIconSm;
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Explicit, Size = 24)]
private struct CLSMENUNAME64
internal struct CLSMENUNAME64
{
[FieldOffset(0)] public ulong pszClientAnsiMenuName;
[FieldOffset(8)] public ulong pwszClientUnicodeMenuName;
@@ -9,6 +9,7 @@ using static System.Runtime.InteropServices.JavaScript.JSType;
namespace Brovan.Core.Emulation.OS.Windows
{
[GenerateStructSerializer]
[StructLayout(LayoutKind.Explicit)]
public struct UNICODE_STRING
{
@@ -22,6 +23,7 @@ namespace Brovan.Core.Emulation.OS.Windows
public uint Buffer;
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Explicit)]
public struct UNICODE_STRING64
{
@@ -35,6 +37,7 @@ namespace Brovan.Core.Emulation.OS.Windows
public ulong Buffer;
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Explicit, Size = 48)]
public struct OBJECT_ATTRIBUTES64
{
@@ -57,6 +60,7 @@ namespace Brovan.Core.Emulation.OS.Windows
public ulong SecurityQualityOfService;
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Sequential)]
public struct MEMORY_REGION_INFORMATION
{
@@ -69,6 +73,7 @@ namespace Brovan.Core.Emulation.OS.Windows
public ulong NodePreference;
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Sequential)]
public struct MEMORY_IMAGE_EXTENSION_INFORMATION
{
@@ -78,6 +83,7 @@ namespace Brovan.Core.Emulation.OS.Windows
public ulong ExtensionSize;
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Sequential)]
public struct MEMORY_WORKING_SET_EX_INFORMATION
{
@@ -154,6 +160,7 @@ namespace Brovan.Core.Emulation.OS.Windows
public ulong Rip;
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Sequential)]
public struct MEMORY_BASIC_INFORMATION
{
@@ -169,6 +176,7 @@ namespace Brovan.Core.Emulation.OS.Windows
public uint Reserved2; // tail padding to 48 bytes
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Sequential)]
public struct MEMORY_IMAGE_INFORMATION
{
@@ -217,6 +225,7 @@ namespace Brovan.Core.Emulation.OS.Windows
MutantOwnerInformation = 1
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MUTANT_BASIC_INFORMATION
{
@@ -231,6 +240,7 @@ namespace Brovan.Core.Emulation.OS.Windows
SemaphoreBasicInformation = 0
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SEMAPHORE_BASIC_INFORMATION
{
@@ -238,6 +248,7 @@ namespace Brovan.Core.Emulation.OS.Windows
public int MaximumCount;
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct OBJECT_BASIC_INFORMATION_DATA
{
@@ -256,6 +267,7 @@ namespace Brovan.Core.Emulation.OS.Windows
public long CreationTime;
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct OBJECT_TYPE_INFORMATION64
{
@@ -284,6 +296,7 @@ namespace Brovan.Core.Emulation.OS.Windows
public uint DefaultNonPagedPoolCharge;
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct OBJECT_TYPE_INFORMATION32
{
@@ -312,6 +325,7 @@ namespace Brovan.Core.Emulation.OS.Windows
public uint DefaultNonPagedPoolCharge;
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct LARGE_STRING64
{
@@ -325,6 +339,7 @@ namespace Brovan.Core.Emulation.OS.Windows
public ulong Buffer;
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
@@ -334,6 +349,7 @@ namespace Brovan.Core.Emulation.OS.Windows
public int Bottom;
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Sequential)]
public struct SYSTEM_BATTERY_STATE
{
@@ -361,6 +377,7 @@ namespace Brovan.Core.Emulation.OS.Windows
public uint Capacity;
}
[GenerateStructSerializer]
[StructLayout(LayoutKind.Sequential)]
public struct SYSTEM_POWER_CAPABILITIES
{