localstack-dotnet-client
localstack-dotnet-client copied to clipboard
Native AOT Support via Source Generator + UnsafeAccessor Architecture
๐ 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
IAwsAccessorinterface for type-safe AWS SDK private member access - [x] Build
AwsAccessorRegistryfor thread-safe accessor lookup in .NET 8+ builds - [x] Add
SessionReflectionModernusing UnsafeAccessor pattern (zero reflection) - [x] Preserve
SessionReflectionLegacyfor .NET Framework/Standard 2.0 compatibility - [x] Refactor
SessionReflectionas platform-specific facade with conditional compilation - [x] Enable AOT analyzers (EnableTrimAnalyzer, EnableSingleFileAnalyzer, EnableAotAnalyzer) for .NET 8+ targets
- [x] Add proof-of-concept
AmazonS3ClientAccessordemonstrating generated accessor pattern - [x] Configure projects for
IsAotCompatible=trueto 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-safeConcurrentDictionary<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
-
Syntax Analysis: Find classes deriving from
AmazonServiceClient -
Type Resolution: Map clients to configs via naming conventions (
AmazonS3ClientโAmazonS3Config) - Code Generation: Emit accessor classes with appropriate UnsafeAccessor methods
- 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
- Complete Source Generator implementation
- Expand POC to multiple AWS services
- Create comprehensive test suite
- Performance optimization and benchmarking
- 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.