[TrimmableTypeMap] Fix trimmable primitive and alias arrays#11985
[TrimmableTypeMap] Fix trimmable primitive and alias arrays#11985simonrozsival wants to merge 1 commit into
Conversation
Emit trimmable array proxy entries for primitive aliases used by Mono.Android JNIEnv APIs and for alias-group peers such as Java.Lang.Object. Update array proxy metadata to support primitive proxies without Java.Interop concrete wrapper types and improve the NativeAOT missing-proxy diagnostic. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/review |
|
✅ Android PR Reviewer completed successfully! |
There was a problem hiding this comment.
Pull request overview
This PR fixes TrimmableTypeMap array-proxy emission and diagnostics, primarily to ensure NativeAOT can resolve array proxies for primitive aliases and for alias-group peers (multiple managed peers sharing a JNI name) without skipping needed entries.
Changes:
- Emit per-managed-type array entries for alias groups (instead of skipping the entire group).
- Extend primitive array proxy synthesis to include Mono.Android primitive aliases (byte/ushort/uint/ulong) and allow “zero concrete wrapper types” for alias proxies.
- Improve the NativeAOT “missing array proxy” exception by including leaf element type and computed rank.
Show a summary per file
| File | Description |
|---|---|
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.cs | Updates/extends tests to cover alias-group array entries and primitive-alias array proxy emission (including “no concrete wrapper” cases). |
| src/Mono.Android/Android.Runtime/JNIEnv.cs | Improves NativeAOT missing-array-proxy diagnostic to include leaf element type + rank. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs | Adjusts emitted array-proxy type references to support 0..N concrete wrapper array types. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs | Changes array entry emission to operate per peer within alias groups; expands primitive proxy modeling to include aliases and 0..N concrete wrappers. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/Model/TypeMapAssemblyData.cs | Updates primitive array proxy metadata model to a list of concrete array wrapper types (default empty). |
Copilot's findings
- Files reviewed: 5/5 changed files
- Comments generated: 0
There was a problem hiding this comment.
🤖 Android PR Review — ⚠️ Needs Changes (pending CI only)
Independent read of the change. This closes two NativeAOT array-proxy gaps in the trimmable typemap:
- Unsigned primitive aliases — adds
Byte,UInt16,UInt32,UInt64proxies that reuse the JNI keyword (B/S/I/J) of their signed counterparts, sobyte[]/ushort[]/uint[]/ulong[]can be created at runtime instead of throwingNotSupportedException. - Alias-group arrays —
EmitArrayEntriesno longer skips whole alias groups; it emits per managed type. Because array map keys are managed-type-qualified (not JNI-keyed), each peer gets a distinct key, so the original "duplicate JNI array keys" deferral is genuinely resolved. - Model shape —
PrimitiveArrayProxyData.ConcreteArrayType→ConcreteArrayTypes(0..N), letting unsigned aliases associate zero concrete wrappers without stealingJavaSByteArray/JavaInt16Array/... from the signed variant. - Diagnostic — the "no array proxy entry" message now reports the leaf element type and rank.
Correctness — verified end-to-end:
- ✅ The diagnostic is now accurate:
TryGetArrayProxykeys on the leaf type withrankIndex = additionalRank-1 + nesting, andSingleUniverseTypeMapindexesarrayMapsByRank[rankIndex](0-based →__ArrayMapRank{N}1-based). The newGetArrayRankreturnsrankIndex+1, so both the leaf type and the printed rank line up with the actual lookup. - ✅ The empty-
ConcreteArrayTypesdesign correctly avoids double-associating a concrete wrapper to both the signed and unsigned proxy (the tests explicitly assertJavaSByteArraymaps toSByte, notByte). - ✅ Alias-group keys are distinct per managed type → no duplicate
TypeMap/TypeMapAssociationentries; theGenerateArrayEntries/IsGenericDefinitionguards are preserved in the extractedEmitArrayEntriesForPeer. - ✅ Both parallel emitters (
ModelBuilder.GetArrayTypeReferencesandTypeMapAssemblyEmitter.GetArrayProxyTypes) were updated identically, andJNIEnv.GetArrayRank's newout leafElementTypeis purely additive.
Tests: Strong coverage — 36 = 12 × 3 primitive count, per-managed-type alias emission (java/lang/Object group), and a full-pipeline round-trip proving the unsigned aliases emit proxies without a concrete wrapper association. 👍
CI: dotnet-android (Java.Interop Tests Windows - .NET) — failed on the latest run (buildId 1494399) at external\Java.Interop\tools\java-source-utils\java-source-utils.targets(8,5): error MSB3073 — a Gradle jar build failure. This PR touches neither Java source-utils nor any .gradle/.targets in that tool, and the previous full pipeline run (buildId 1494242) was entirely green, so this is almost certainly a transient/infrastructure failure. A re-run should clear it — but per review policy the PR isn't mergeable until CI is green.
Inline comments: 2 💡 documentation-only suggestions — nothing blocking on the code itself.
Clean, well-scoped fix with excellent test coverage. The only actionable item is getting the (unrelated) Windows leg green. 👏
Generated by Android PR Reviewer for #11985 · 252.8 AIC · ⌖ 28.5 AIC · ⊞ 6.8K
Comment /review to run again
| .Where (e => e.MapKey.StartsWith ("System.", StringComparison.Ordinal) && e.AnchorRank is not null) | ||
| .ToList (); | ||
| Assert.Equal (24, primitiveEntries.Count); // 8 primitive keywords × 3 ranks | ||
| Assert.Equal (36, primitiveEntries.Count); |
There was a problem hiding this comment.
🤖 💡 Testing — The prior assertion self-documented its count (// 8 primitive keywords × 3 ranks); the bump to 36 dropped that note. Restoring // 12 primitive keywords × 3 ranks lets a reader validate the expected total at a glance instead of reverse-engineering where 36 comes from.
Rule: Test assertions should be self-documenting
| new ("D", "Double", "System.Double", "Java.Interop.JavaDoubleArray"), | ||
| new ("Z", "Boolean", "System.Boolean", ["Java.Interop.JavaBooleanArray"]), | ||
| new ("B", "SByte", "System.SByte", ["Java.Interop.JavaSByteArray"]), | ||
| new ("B", "Byte", "System.Byte", []), |
There was a problem hiding this comment.
🤖 💡 Documentation — The empty concrete-array-type list on the four unsigned aliases (Byte, UInt16, UInt32, UInt64) is deliberate but non-obvious: each shares its JNI keyword (B/S/I/J) with the signed variant, which owns the sole Java.Interop.Java*Array wrapper (e.g. JavaSByteArray), so the alias must not re-associate that concrete wrapper. A one-line comment on the table would keep the [] from reading like an oversight to a future maintainer.
Rule: Comments explain "why", not "what"
Fix trimmable typemap array proxy emission for NativeAOT array lookups.
Changes:
Validation: