[TrimmableTypeMap] Implement alias support in codegen and runtime#11122
[TrimmableTypeMap] Implement alias support in codegen and runtime#11122simonrozsival merged 9 commits intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Implements end-to-end alias support for the Trimmable TypeMap pipeline (codegen + runtime), enabling multiple managed types to share a single JNI name via an alias-holder indirection and indexed alias keys.
Changes:
- Add
IJavaPeerAliasesand generate alias-holder proxy types + indexed TypeMap entries +TypeMapAssociationrecords for alias groups. - Update runtime lookup/activation paths (
TrimmableTypeMap*) to expand alias holders into their concrete alias entries. - Add/expand unit tests and fixture types covering 2-way/3-way aliases and emission round-trips.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.cs | Adds fixture types that intentionally share a JNI name to form a 3-way alias group. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.cs | Updates/extends model-builder tests for indexed alias entries, alias holders, and associations. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs | Adds a round-trip emission test asserting alias-related typemap artifacts are present in emitted metadata. |
| src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.cs | Switches to enumerating all types for a JNI name (to support alias expansion). |
| src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.cs | Adds alias-aware type enumeration and alias resolution for proxy lookup and activation. |
| src/Mono.Android/Java.Interop/JavaPeerProxy.cs | Introduces IJavaPeerAliases used by generated alias-holder proxy types. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs | Emits alias-holder types and self-applies proxy/holder attributes for AOT-safe GetCustomAttribute instantiation. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs | Generates alias holders, base-entry indirection, indexed alias keys, and associations for duplicate JNI-name groups. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/Model/TypeMapAssemblyData.cs | Extends the model to carry alias-holder emission data. |
9e8ae03 to
64d3228
Compare
…1103) Add alias holder approach for typemap aliases: when multiple .NET types map to the same JNI name, generate an alias holder class extending JavaPeerProxy + IJavaPeerAliases with explicit alias key list. Build-time: - IJavaPeerAliases interface with string[] Aliases property - AliasHolderData in the codegen model - ModelBuilder generates alias holder, [0]-based indexed entries, TypeMapAssociation links to holder - Emitter emits alias holder class with TargetType/CreateInstance throwing NotSupportedException, Aliases property returning key array - Self-application (AddCustomAttribute) for proxy and alias holder types Runtime: - GetProxyForManagedType: if (proxy is IJavaPeerAliases) -> iterate exact keys from Aliases property (zero cost for non-alias types) - GetAllTypesForJniName: detect alias holder, enumerate listed keys - ActivateInstance: same alias holder detection Tests: - 3 alias fixture types (3-way alias group) - Model builder tests for alias holders, 3-way aliases, mixed activation - Fixture scanner and integration tests Fixes: #11103 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rarchy walking - Rename ResolveAlias -> GetProxyFromAliases, use IsAssignableFrom - GetProxyForJavaType: return sentinel for alias holders (not real proxies) - TryGetProxyFromHierarchy: resolve alias groups when targetType is available - Add GetAliasesForJniName helper - Fix NotSupportedException IL: push string arg before newobj Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Don't extend JavaPeerProxy for alias holders — this keeps the fast path (GetCustomAttribute<JavaPeerProxy>()) clean with zero alias checks. - Replace IJavaPeerAliases interface with JavaPeerAliasesAttribute - Alias holder is now a plain class (extends Object, not JavaPeerProxy) - [JavaPeerAliases] attribute encodes alias keys in the metadata blob - No ctor/CreateInstance/TargetType methods on alias holder (not needed) - Runtime: only checks JavaPeerAliasesAttribute when JavaPeerProxy is null Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace TryGetTargetType(string, out Type?) with TryGetTargetTypes(string, out Type[]?) — returns all surviving target types for alias groups, false when no mapping or all trimmed - GetProxyFromAliases: exact type equality (==) not IsAssignableFrom - TryGetProxyFromHierarchy: resolve aliases even without targetType (returns first surviving proxy) - TypeManager: single TryGetTargetTypes call, no alias awareness - Remove dead methods: GetTargetTypesForAliasedJniName, GetAliasedTargetTypes - Add GetFirstProxyFromAliases for targetType-less resolution - 3 new integration tests: - alias holder extends Object not JavaPeerProxy - JavaPeerAliasesAttribute deserializes to correct keys - proxy types have self-applied attribute (MethodDef ctor) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A single-proxy-per-JNI-name cache violates the principle that one JNI name can map to multiple types. Replace with GetProxyForJniClass(string, Type?) which handles both direct entries and alias groups in a single call, with no per-JNI-name caching. - Remove _peerProxyCache (cached single proxy per JNI name) - Remove GetProxyForJavaType (returned single proxy) - Remove GetAliasesForJniName (no longer needed as separate method) - Add GetProxyForJniClass(className, targetType) that resolves direct proxies or alias groups based on targetType - Simplify TryGetProxyFromHierarchy to single GetProxyForJniClass call Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use GetProxiesForJniName to resolve all proxies (including aliases) and register natives for each ACW proxy. Previously, alias holders returned null from GetCustomAttribute<JavaPeerProxy>() and native registration was silently skipped for aliased types. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Commit 64d3228 accidentally bumped external/Java.Interop and external/xamarin-android-tools submodule pointers. The newer xamarin-android-tools commit removed the 'log' parameter from Files.ExtractAll, causing CI build failures: ResolveLibraryProjectImports.cs(300,13): error CS1739: The best overload for 'ExtractAll' does not have a parameter named 'log' Reset both submodules back to origin/main. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
348a100 to
2b159e5
Compare
|
/review |
|
✅ Android PR Reviewer completed successfully! |
There was a problem hiding this comment.
⚠️ Needs Verification
The alias support implementation is well-structured — the JavaPeerAliasesAttribute / alias holder pattern cleanly separates alias groups from the fast non-alias path, and the runtime handles both paths correctly with appropriate caching.
Summary
| Severity | Count |
|---|---|
| 2 | |
| 💡 Suggestion | 3 |
Key findings
-
⚠️ Fragile handle resolution —EmitJavaPeerAliasesAttributere-derives theTypeDefinitionHandlefromGetRowCountinstead of using the handle returned byAddTypeDefinition. Capture and pass it explicitly. -
⚠️ UCO/callback boolean encoding unified —EncodeClrTypeForCallbackwas removed, and callback MemberRefs now use the samebyteencoding as UCO wrappers. Previous tests specifically verified that callbacks usedsbytefor boolean to match MCWn_*method signatures. Needs confirmation that the MCW signatures were also updated. -
💡 Generic alias matching —
GetProxyForJniClassuses exactTypeequality for alias groups, which misses open generic → closed generic matches. The fallback path handles this correctly, but generic aliases always take the slower path.
Positive callouts
- Clean separation of alias holders from proxy types — alias holders extend
Object(notJavaPeerProxy), keepingGetCustomAttribute<JavaPeerProxy>()as a zero-cost filter for non-alias entries. - Good test coverage: model builder tests, assembly generator tests, and fixture-based alias tests all verify the new codegen.
- The self-application fix (using
MethodDefinitionHandleinstead ofMemberReferenceHandlefor the proxy's own custom attribute) is a nice improvement. - Thorough
OnRegisterNativesupdate to register natives for ALL proxies in an alias group, not just the first.
Note on scope
This PR also includes significant TestRunner infrastructure changes (removing DryRun, NoExclusions, changing filter/count mechanics) that appear orthogonal to the trimmable typemap alias feature. Consider splitting those into a separate PR for cleaner review history.
Generated by Android PR Reviewer for issue #11122 · ● 21.1M
… alias matching - EmitAliasHolderType: capture TypeDefinitionHandle from AddTypeDefinition and pass it explicitly to EmitJavaPeerAliasesAttribute instead of re-deriving via GetRowCount(TableIndex.TypeDef) - GetProxyForJniClass/GetProxyFromAliases: use TargetTypeMatches() to handle open generic → closed generic matching Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
d9bbcac to
f4a3a69
Compare
|
Test failure unrelated ( |
Summary
Implements trimmable typemap alias support end-to-end, covering both generated metadata/code and runtime lookup/activation.
Fixes #11103
Part of #10788
Changes
New:
IJavaPeerAliasesinterface (JavaPeerProxy.cs)string[] Aliases { get; }— lists the exact TypeMap keys for an alias groupBuild-time (ModelBuilder + Emitter)
For alias groups (≥2 .NET types sharing a JNI name, e.g.,
JavaCollection/JavaCollection<T>):JavaPeerProxy+IJavaPeerAliases[0]-based indexed entry:"jni/Name[0]","jni/Name[1]", ...TypeMapAssociationlinks each type to the alias holder (keeps holder alive when any alias survives trimming)TargetType/CreateInstancethrowNotSupportedException(alias holder is never used for peer creation)AddCustomAttributeontypeDefHandle) for all proxy types — required forGetCustomAttribute<JavaPeerProxy>()to work at runtimeRuntime (
TrimmableTypeMap.cs)GetProxyForManagedType():if (proxy is IJavaPeerAliases aliases)→ iterate exact keys fromAliasespropertyGetAllTypesForJniName(): detect alias holder, enumerate listed keysActivateInstance(): same alias holder detectionischeck that's false, no dictionary probingGenerated output for an alias group
Tests
331 passing (+9 new):
JavaCollection/JavaCollection<T>pattern)IJavaPeerAliasesin emitted PE