Repository navigation
AOT: core module-serializer table (#8626, part 1) - #8645
Merged
Aaronontheweb merged 5 commits intoSep 28, 2026
Merged
Aaronontheweb merged 5 commits into
Aaronontheweb merged 5 commits into
Conversation
Serialization now resolves a HOCON serializer or binding row that misses core's built-in tables through the owning module's ModuleSerializers table before falling back to reflection (switch on) or NotBuiltIn (switch off). Modules load lazily by assembly simple name through a DynamicallyAccessedMembers-annotated literal, are cached per assembly, and count as absent on version skew. Matching is strict on full name plus assembly. The first-party map is empty; later PRs add Remote, the Cluster family and Persistence.
- Unwrap nested TargetInvocationException/TypeInitializationException, so a missing member in a module table's static initializer counts as skew. - Read Serializers and BoundTypes inside the guarded load and precompute a per-module lookup keyed by stripped full name. - Accept a bare Akka.dll type name, as Type.GetType from Akka.dll does. - Tests: static-initializer skew, bare Akka.dll name, System.String row with no module serializer row still throws.
- Precompute an IsAkka flag per entry instead of passing the Type to Accepts. - FindModuleBoundType: plain loop, owner module first, no LINQ allocation. - ModuleSerializer doc: null skips the alias as a safety net.
This was referenced Sep 25, 2026
…odule-serializer-table
Aaronontheweb
commented
Sep 28, 2026
Aaronontheweb
left a comment
Member
Author
There was a problem hiding this comment.
LGTM - this just adds the infrastructure for supporting AOT-compatible built-in serializer loading. The actual work on a per-module basis will happen downstream.
| internal sealed class ModuleSerializerTable | ||
| { | ||
| /// <summary>The process-wide table behind the public <see cref="Serialization"/> constructor.</summary> | ||
| internal static readonly ModuleSerializerTable Default = new(new Dictionary<string, Func<ModuleSerializers?>> |
Member
Author
There was a problem hiding this comment.
this is getting populated in a follow-up PR
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First PR of the #8626 stack. It adds the core half of the module serializer table. No module ships a table yet; the Remote, Cluster family and Persistence PRs each add one.
What changes
ModuleSerializers(internal, abstract): a module lists its serializers asModuleSerializer(Type, Func<ExtendedActorSystem, Config, Serializer>)and its bound types asIReadOnlyList<Type>.ModuleSerializerTable(internal): maps an assembly simple name to a lambda that callsLoad("<literal type name>").Loadtakes a[DynamicallyAccessedMembers(PublicParameterlessConstructor)]string, the same pattern asCreateProviderin AOT: resolve built-in actor ref providers from constant type names so the trimmer preserves them #8599. Each module gets its own lambda and literal, so the trimmer keeps each table type. The map is empty in this PR.ConcurrentDictionary, as a lookup keyed by stripped full name that is built once when the module loads, so each row is a dictionary hit.TypeLoadException,MissingMemberException,FileNotFoundExceptionorFileLoadException, unwrapped through any depth ofTargetInvocationException/TypeInitializationException) makes the module count as absent. Both lists are read inside that guard, so a missing member in the table's constructor, static initializer or list getters is covered.Serializationconstructor, per row: built-in table, then module table, then reflection (switch on) or SerializationSetup/NotBuiltIn(switch off). With the switch on, the module arm swaps reflection for direct construction that gives the sameTypeand serializer class, so JIT behavior does not change.Type.GetTypeaccepts when called from Akka.dll: full type name plus assembly simple name, case-insensitive. A bare name matches only Akka.dll and framework types (so"Akka.Actor.Identify"works with the switch off as it does with it on, per AOT M1-G: one matching rule for every built-in type name #8613); a bare name for a type in another module is rejected, as reflection rejects it. A framework type (System.String) also matches any of the existingFrameworkAssemblyNames."System.String","Akka.Actor.Identify, Akka","Google.Protobuf.IMessage, Google.Protobuf") resolve against the modules this config's serializer rows loaded. Two modules that list the same name list the sameType, so the order does not matter.Serialization(ExtendedActorSystem, ModuleSerializerTable)for tests. The public ctor usesModuleSerializerTable.Default.No public API change and no ledger entry.
Tests
ModuleSerializersSpec(inDynamicTypeLoadingCollection) uses a fake module keyed asAkka.Tests:System.String,Akka.Actor.Identify, Akka) resolve, with one probe;System.String) tobyteswins;NotBuiltIn;"Akka.Actor.PoisonPill") that a loaded module binds resolves, matching reflection;"System.String" = bytesstill throwsNotBuiltInwith the switch off, and nothing is probed;MissingMethodException, one whose static initializer does, and a table type that does not exist all count as absent: reflection works with the switch on, and the switch-off error is the ordinaryNotBuiltIn;Validation
dotnet build -c Release -warnaserrorfor Akka and Akka.Tests[canary] OKwith the same 4 IL warnings; the rooted publish passesCheckAotWarningswith no new baseline entries