localstack-dotnet-client icon indicating copy to clipboard operation
localstack-dotnet-client copied to clipboard

Native AOT Support via Source Generator + UnsafeAccessor Architecture

Open Blind-Striker opened this issue 7 months ago โ€ข 1 comments

๐Ÿ”„ Status: Work in Progress - Experimental

โš ๏ธ This PR is currently experimental and under active development. Do not merge until all objectives are completed and thoroughly tested.

๐Ÿ“‹ Overview

This PR implements a comprehensive architecture to enable Native AOT (Ahead-of-Time) compilation support for LocalStack.Client while maintaining backward compatibility with existing target frameworks. The solution uses a Source Generator + UnsafeAccessor pattern to eliminate reflection usage in .NET 8+ builds while preserving traditional reflection for legacy frameworks.

๐ŸŽฏ Objectives

Phase 1: Foundation Architecture โœ…

  • [x] Implement conditional compilation strategy for multi-framework support
  • [x] Create IAwsAccessor interface for type-safe AWS SDK private member access
  • [x] Build AwsAccessorRegistry for thread-safe accessor lookup in .NET 8+ builds
  • [x] Add SessionReflectionModern using UnsafeAccessor pattern (zero reflection)
  • [x] Preserve SessionReflectionLegacy for .NET Framework/Standard 2.0 compatibility
  • [x] Refactor SessionReflection as platform-specific facade with conditional compilation
  • [x] Enable AOT analyzers (EnableTrimAnalyzer, EnableSingleFileAnalyzer, EnableAotAnalyzer) for .NET 8+ targets
  • [x] Add proof-of-concept AmazonS3ClientAccessor demonstrating generated accessor pattern
  • [x] Configure projects for IsAotCompatible=true to enforce IL warning detection

Phase 2: Source Generator Implementation ๐Ÿ”„

  • [ ] Create Roslyn Incremental Source Generator project
  • [ ] Implement AWS client discovery via semantic analysis
  • [ ] Add ClientConfig type resolution through naming conventions
  • [ ] Generate strongly-typed accessor classes with UnsafeAccessor attributes
  • [ ] Emit DynamicDependency attributes for trimming/AOT preservation
  • [ ] Generate ModuleInitializer for automatic runtime registration
  • [ ] Add comprehensive diagnostics and error handling

Phase 3: Testing & Validation ๐Ÿ”„

  • [ ] Create AOT test console application with PublishAot=true
  • [ ] Test with multiple AWS services (S3, DynamoDB, Lambda, SQS)
  • [ ] Validate zero IL warnings in generated accessors
  • [ ] Ensure backward compatibility with all target frameworks
  • [ ] Performance benchmarking vs reflection-based approach
  • [ ] Integration testing with LocalStack.Client.Integration.Tests

Phase 4: Documentation & Polish ๐Ÿ”„

  • [ ] Update strategy documentation with implementation details
  • [ ] Add code generation samples and examples
  • [ ] Create migration guide for consumers
  • [ ] Update README with AOT compatibility information
  • [ ] Add XML documentation for all public APIs

๐Ÿ—๏ธ Architecture

Multi-Framework Strategy

Target Framework Implementation Reflection Source Generator UnsafeAccessor AOT Compatible
netstandard2.0 SessionReflectionLegacy โœ… Yes โŒ No โŒ No โŒ No
net472 SessionReflectionLegacy โœ… Yes โŒ No โŒ No โŒ No
net8.0 SessionReflectionModern โŒ No โœ… Yes โœ… Yes โœ… Yes
net9.0 SessionReflectionModern โŒ No โœ… Yes โœ… Yes โœ… Yes

Key Components

๐Ÿ”ง Core Interfaces

  • IAwsAccessor: Type-safe interface for AWS SDK private member access
  • AwsAccessorRegistry: Thread-safe ConcurrentDictionary<Type, IAwsAccessor> for runtime lookup

๐ŸŽญ Platform-Specific Implementations

  • SessionReflectionLegacy: Traditional reflection for legacy frameworks (#if NETSTANDARD2_0 || NET472)
  • SessionReflectionModern: UnsafeAccessor-based for modern frameworks (#if NET8_0_OR_GREATER)
  • SessionReflection: Facade that delegates to appropriate implementation

๐ŸŽจ Generated Code Pattern

[DynamicDependency("serviceMetadata", typeof(AmazonS3Client))]
internal sealed class AmazonS3Client_Accessor : IAwsAccessor
{
    [UnsafeAccessor(UnsafeAccessorKind.StaticField, Name = "serviceMetadata")]
    private static extern ref IServiceMetadata GetServiceMetadataField(AmazonS3Client? instance);
    
    // ... implementation
}

[ModuleInitializer]
internal static void RegisterS3Accessor()
{
    AwsAccessorRegistry.Register<AmazonS3Client>(new AmazonS3Client_Accessor());
}

๐Ÿ” Technical Details

Conditional Compilation Strategy

  • Legacy builds: Include only reflection-based implementation
  • Modern builds: Include only UnsafeAccessor-based implementation
  • Zero runtime overhead: No reflection code paths in AOT builds

AOT Safety Measures

  • [DynamicDependency] attributes preserve required private members from trimming
  • Fail-fast behavior when AWS SDK internal contracts change
  • IL warning enforcement as build errors for .NET 8+ targets

Source Generator Discovery

  1. Syntax Analysis: Find classes deriving from AmazonServiceClient
  2. Type Resolution: Map clients to configs via naming conventions (AmazonS3Client โ†’ AmazonS3Config)
  3. Code Generation: Emit accessor classes with appropriate UnsafeAccessor methods
  4. Registration: Generate ModuleInitializer for runtime registry population

โš ๏ธ Breaking Changes

For Library Consumers

  • SessionReflection implementation varies by target framework
  • Type-based overloads will be deprecated for .NET 8+ targets (planned)

Migration Path

  • Existing code continues to work unchanged
  • Generic method overloads (ExtractServiceMetadata<TClient>()) remain supported
  • Type-based overloads will show deprecation warnings in future versions

๐Ÿงช Testing Strategy

Planned Testing

  • [ ] Multi-framework compilation validation
  • [ ] Conditional compilation verification
  • [ ] AOT analyzer integration
  • [ ] AOT console application with PublishAot=true
  • [ ] Integration tests with real AWS services
  • [ ] Performance benchmarks
  • [ ] Memory usage analysis

๐Ÿ“ˆ Next Steps

  1. Complete Source Generator implementation
  2. Expand POC to multiple AWS services
  3. Create comprehensive test suite
  4. Performance optimization and benchmarking
  5. Documentation and migration guides

๐ŸŽฏ Goal: Enable LocalStack.Client to run in Native AOT scenarios while maintaining 100% backward compatibility with existing target frameworks and consumer code.

Blind-Striker avatar Jul 23 '25 08:07 Blind-Striker