Uh oh!
There was an error while loading. Please reload this page.
[TrimmableTypeMap] Improve array handling - #11238
Conversation
5b32352 to
956a999Comparesimonrozsival
commented
Apr 29, 2026
/review |
✅ Android PR Reviewer completed successfully! |
There was a problem hiding this comment.
✅ LGTM — Clean design, minor doc/style nits
Well-architected PR. The runtime fork (CoreCLR → Array.CreateInstance directly, NativeAOT → per-rank typemap + Array.CreateInstanceFromArrayType) is sound and avoids unnecessary typemap lookups on CoreCLR while remaining AOT-safe on NativeAOT.
What looks good:
- IL emission for both aggregate (Debug) and single-universe (Release) paths is correct — stack discipline, local variable encoding, and member reference blobs all check out.
- Thorough edge-case handling in
EmitArrayEntries: primitives skipped (dedicated JNI handlers exist), open generics rejected (typeof(T<>[])invalid), alias groups deferred. TryGetArrayType's unwrap loop correctly handles nested szarrays and rejects multi-dim arrays.- 14+ new generator unit tests cover the key scenarios including PE round-trip verification.
- Sentinel types in
ArrayMapAnchors.csmirror the established__TypeMapAnchorpattern nicely. MaxSupportedArrayRank = 8matches the 8 anchor types, with the MSBuild default of 3 for NativeAOT being practical for typical apps.
Minor nits (3 inline 💡):
| Sev | Category | File |
|---|---|---|
| 💡 | Formatting | Array.Empty<T>() → [] (2 instances) |
| 💡 | Documentation | EmitArrayEntries doc comment mentions [L<jni>;-shaped keys but keys are bare element names |
| 💡 | Documentation | Stale 2-arg Initialize references in comments |
No correctness, safety, or performance issues found.
Generated by Android PR Reviewer for issue #11238 · ● 9.8M
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Pull request overview
Refactors trimmable typemap array creation to avoid per-T factory IL bloat by routing array construction through JNIEnv.ArrayCreateInstance, using Array.CreateInstance when dynamic code is supported and a per-rank array typemap under NativeAOT.
Changes:
- Add generator support for per-rank array
TypeMapentries (with rank anchors) and plumbMaxArrayRankthrough the MSBuild task + targets. - Update the runtime typemap initialization and lookup to support array-type resolution under NativeAOT, and update
JNIEnv.ArrayCreateInstanceto use the new runtime fork. - Remove
JavaPeerContainerFactory<T>array creation APIs and update tests to cover the new generator/root-assembly behavior.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.cs | Adds unit tests validating array-entry model emission (ranks, keys, skip rules, blob round-trip). |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.cs | Adds tests for merged-universe root assembly generation when arrays are enabled. |
| src/Xamarin.Android.Build.Tasks/Tasks/GenerateTrimmableTypeMap.cs | Adds MaxArrayRank task input and forwards it into typemap generation. |
| src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets | Introduces $(_AndroidTrimmableTypeMapMaxArrayRank) defaults and passes it to the task. |
| src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.cs | Extends initialization to accept per-rank array dictionaries and adds TryGetArrayType lookup used by NativeAOT. |
| src/Mono.Android/Microsoft.Android.Runtime/ArrayMapAnchors.cs | Adds shared __ArrayMapRank{N} anchor types in Mono.Android for rank grouping. |
| src/Mono.Android/Java.Interop/JavaPeerContainerFactory.cs | Removes array creation APIs from the container factory surface (arrays move to JNIEnv). |
| src/Mono.Android/Android.Runtime/JNIEnv.cs | Implements the runtime fork for array creation (dynamic-code vs NativeAOT typemap). |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/TrimmableTypeMapGenerator.cs | Adds MaxSupportedArrayRank validation and threads maxArrayRank through generation. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyGenerator.cs | Passes maxArrayRank into ModelBuilder.Build. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs | Emits rank anchor refs and supports rank-anchored 3-arg TypeMap<TGroup> constructor emission. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.cs | Emits root-loader initialization that passes array rank dictionaries (or null) into TrimmableTypeMap.Initialize. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs | Emits per-rank array entries into the typemap model when enabled. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/Model/TypeMapAssemblyData.cs | Adds MaxArrayRank and per-entry AnchorRank metadata for array mapping. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
simonrozsival
commented
May 4, 2026
@copilot fix this branch based on this CI build failure analysis: |
Improve CreatePeer under the trimmable typemap to match legacy JavaCast/JavaAs contracts: - Bad-cast disambiguation: distinguish incompatible Java types (return null → InvalidCastException) from missing typemap entries (ArgumentException) and generator gaps (NotSupportedException). - Closed-generic activation: when the proxy targets an open generic (e.g. JavaList<>), activate the closed targetType (e.g. JavaList<int>) via reflection using the (IntPtr, JniHandleOwnership) ctor. The [DynamicallyAccessedMembers(Constructors)] annotation on targetType guarantees the trimmer preserves the ctor metadata. - Type resolution: map IJavaPeerable/object/Exception to concrete peer types before proxy lookup, mirroring the legacy GetPeerType behavior. - TargetTypeMatches: restructure so open-generic proxies match only closed instantiations of their definition. - FindClass safety: catch ClassNotFoundException in TryGetProxyFromTargetType for types not present in the APK. - Don't synthesize activation from inherited ctors: match legacy GetConstructor() which doesn't find inherited constructors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add trimmable-typemap variant of GetThis.java that uses mono.android.Runtime.register instead of ManagedPeer.registerNativeMembers. Java.Interop-Tests.targets swaps variants based on $(_AndroidTypeMapImplementation). Re-enabled tests: - JavaCast_BadInterfaceCast (bad-cast disambiguation) - JavaCast_BaseToGenericWrapper (closed-generic activation) - JavaCast_CheckForManagedSubclasses (bad-cast disambiguation) - JavaCast_InvalidTypeCastThrows (bad-cast disambiguation) - JavaAs_Exceptions (inherited ctor fix) - DisposeAccessesThis (trimmable GetThis.java) - CreateGenericValue (ArrayRank scanner fix) Updated remaining exclusion comments with accurate root-cause descriptions. Removed resolved TODO comments from test files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
For every non-aliased, non-generic peer (excluding JNI primitive-keyword
keys), the generator can now emit three speculative `TypeMap<T>` entries
keyed by the **element JNI name** and anchored to per-typemap-assembly
`__ArrayMapRank{1,2,3}` sentinel TypeDefs:
[assembly: TypeMap<__ArrayMapRank1>("java/lang/String", typeof(string[]), typeof(string[]))]
[assembly: TypeMap<__ArrayMapRank2>("java/lang/String", typeof(string[][]), typeof(string[][]))]
[assembly: TypeMap<__ArrayMapRank3>("java/lang/String", typeof(string[][][]), typeof(string[][][]))]
The trim target is the closed array type itself, so ILC's per-shape
conditional drops entries when the array shape is never constructed —
validated on disjoint-set NativeAOT tests for both reference and value
type element peers.
* `Generator/Model/TypeMapAssemblyData.cs`:
* `TypeMapAssemblyData.RankSentinels` (nullable `RankSentinelNames`)
— when set, the emitter generates the three sentinel TypeDefs.
* `TypeMapAttributeData.AnchorRank` (nullable int) — when set,
overrides the model-level default anchor with the rank-{value}
sentinel from the same assembly.
* `Generator/TypeMapAssemblyEmitter.cs`:
* `EmitRankSentinels` mirrors the existing `__TypeMapAnchor` pattern.
* Per-anchor `TypeMap<TGroup>` 3-arg ctor refs are now built and
cached lazily by anchor handle (`GetOrAddTypeMapAttr3ArgCtorRef`).
* `EmitTypeMapAttribute` resolves rank-anchored entries to the local
sentinel handle.
* `Generator/ModelBuilder.cs`:
* `Build` takes a new `bool emitArrayEntries` (default false).
When true, sets `RankSentinels` and routes each peer through
`EmitArrayEntries`, which produces the per-rank trio.
* Skip rules: open-generic peers, JNI primitive-keyword keys, alias
groups (would produce duplicate keys; deferred).
* `tests/.../TypeMapModelBuilderTests.cs`: 14 new tests covering the
default-off behavior, default sentinel names, per-rank-trio emission,
element-only key, closed-array trim target, conditional-only entries,
open-generic / alias / primitive-keyword skip rules, multiple-peer
isolation, and PE blob round-trip (sentinels emitted, attribute
blobs survive).
Tracking: #11234 Phase 2 (arrays-only, container types stay untouched).
Runtime wiring + MSBuild flag follow in subsequent commits.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>`JNIEnv.ArrayCreateInstance` now branches on `RuntimeFeature.IsDynamicCodeSupported`: * CoreCLR / Mono (true) — `Array.CreateInstance(elementType, length)`. No typemap roundtrip; supports unlimited array rank. * NativeAOT (false) — typemap lookup → AOT-safe `Array.CreateInstanceFromArrayType`. Capped at the emitted ranks (1–3); miss throws `NotSupportedException` with diagnostic. The runtime fork lets us avoid emitting (and paying for) speculative array TypeMap entries on CoreCLR-only builds, where the runtime type loader can construct any `T[]` dynamically anyway. * `ITypeMapWithAliasing.TryGetArrayType(string jniElementTypeName, int rank, out Type? arrayType)` — new abstraction for the per-rank array dictionary lookup. `SingleUniverseTypeMap` carries three nullable `IReadOnlyDictionary<string, Type>?` fields (rank 1, 2, 3) populated at `TrimmableTypeMap.Initialize` time; `AggregateTypeMap` does first-wins iteration. * `TrimmableTypeMap.TryGetArrayType(Type elementType, out Type?)` — walks down `elementType.IsArray` / `GetElementType()` to find the leaf type and array depth, resolves the leaf JNI element name (primitive static dict OR `TryGetJniNameForManagedType` wrapped), and delegates the (jni, rank+1) lookup to the interface. * `TrimmableTypeMap.Initialize` gains 5-arg overloads (single + aggregate) accepting the per-rank dicts. Existing 2-arg overloads stay as wrappers passing null per-rank dicts so older generated assemblies keep working. * `RootTypeMapAssemblyGenerator`: the generated `TypeMapLoader.Initialize` IL now branches on the new `emitArrayEntries` flag. When true, it collects per-assembly `__ArrayMapRank{1,2,3}` sentinels via `TypeMapping.GetOrCreateExternalTypeMapping<__ArrayMapRank{N}>()` and passes the resulting dicts to the 5-arg `TrimmableTypeMap.Initialize`. Aggregate (Debug) path is fully implemented; merged-universe (Release) path throws at generation time with a clear message — wiring the shared-universe array sentinels is a small follow-up. * `GenerateTrimmableTypeMap` MSBuild task: new `EmitArrayEntries` property forwarded through `TrimmableTypeMapGenerator.Execute` and `RootTypeMapAssemblyGenerator.Generate`. SDK target sets it to `$(PublishAot)`. * `JavaPeerContainerFactory<T>.CreateArray` and `CreateHigherRankArray` deleted. Container methods (`CreateList`, `CreateCollection`, `CreateDictionary*`) stay untouched — those are tracked separately in #11234. Validation: * 445 / 445 generator unit tests pass. * Trimmable + CoreCLR `RunTestApp` lane on emulator: **917 total / 0 errors / 3 failures** (pre-existing `TryGetJniNameForManagedType_*`, called out as out-of-scope in #11225). No regression. * The NativeAOT branch path is gated on dotnet/runtime#126380 (ships in .NET 11 nightly preview.5+); validated with the playground repro separately. Tracking: #11234 Phase 2 (arrays only). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* JNIEnv.ArrayCreateInstance:
* Drop the IL3050 #pragma — IsDynamicCodeSupported acts as a
[FeatureGuard] so the trimmer dead-codes the dynamic branch under
PublishAot without needing a manual suppression.
* Drop the 'Mono' mention from the comment — the trimmable typemap
is a CoreCLR-only feature.
* SingleUniverseTypeMap._arrayMapsByRank:
* Switched from a 1-indexed array of fixed length 4 (with [0] unused)
to a 0-indexed variable-length array (length determined by what the
generator emitted). TryGetArrayType uses (rank - 1) for indexing
and bounds-checks to the actual length.
* Variable rank count throughout:
* RankSentinelNames: replaced the fixed (Rank1, Rank2, Rank3) record
with a name list + Count, generated via CreateDefault(maxRank).
* TypeMapAssemblyEmitter: _rankAnchorHandles becomes a 0-indexed
variable-length array sized to model.RankSentinels.Count.
* ModelBuilder.Build: replaced 'bool emitArrayEntries' with
'int maxArrayRank' (0 = disabled). EmitArrayEntries takes the max
and loops 1..maxArrayRank instead of using a const.
* RootTypeMapAssemblyGenerator: 'int maxArrayRank' parameter; the
generated TypeMapLoader.Initialize now builds a single jagged
'IReadOnlyDictionary<string, Type>?[][]' (per-universe per-rank)
instead of fixed-rank-count locals. Rank loop is unrolled.
* GenerateTrimmableTypeMap MSBuild task: 'int MaxArrayRank' property.
* New $(_AndroidTrimmableTypeMapMaxArrayRank) MSBuild property —
defaults to 3 under PublishAot=true, 0 otherwise. Users can override
to support unlimited rank under NativeAOT (limited to what they're
willing to pay in attribute metadata).
* TrimmableTypeMap.Initialize:
* Single overload: '(typeMap, proxyMap, IReadOnlyDictionary<string,
Type>?[]? arrayMapsByRank)' — 0-indexed by (rank - 1).
* Aggregate overload: '(typeMaps[], proxyMaps[],
IReadOnlyDictionary<string, Type>?[]?[]? perUniverseArrayMaps)' —
jagged, indexed first by universe, then by (rank - 1).
* Existing 2-arg overloads retained as wrappers passing null.
* Tests: updated BuildModelWithArrays to take maxArrayRank (default 3).
Replaced 'RankSentinels.Rank{1,2,3}' assertions with Names[i] indexing.
Added Build_EmitArrayEntries_HonoursMaxArrayRank test that verifies
emission with maxArrayRank=1 and =5.
Validation:
* 446 / 446 generator unit tests pass (445 baseline + 1 new for the
MaxArrayRank parametrization).
* Trimmable + CoreCLR RunTestApp lane on emulator: 917 / 0 errors / 3
pre-existing failures. No regression.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>Tightens XML doc comments and inline notes added in this PR — keeps the intent but drops walls of explanation that were repeating what the code already says. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replaces the NotSupportedException stub in RootTypeMapAssemblyGenerator
with a fully functional shared-universe + array entries IL emit path,
covering the Release build configuration when array typemap entries
are enabled (`_AndroidTrimmableTypeMapMaxArrayRank > 0`).
Each per-assembly typemap DLL still owns its own `__ArrayMapRank{N}`
sentinel TypeDefs, so each rank's entries are split across N per-asm
dicts. The new `CompositeStringTypeReadOnlyDictionary` wraps an array
of source dicts and routes `TryGetValue` first-hit, letting the existing
single-universe-with-arrays `TrimmableTypeMap.Initialize` overload
consume them as a single per-rank dict each.
Generated IL (shared + arrays):
var arrayMapsByRank = new IReadOnlyDictionary<string, Type>?[maxRank];
for each rank r:
var sources = new IReadOnlyDictionary<string, Type>?[asmCount];
sources[i] = TypeMapping.GetOrCreateExternalTypeMapping<PerAsm[i].__ArrayMapRank_r>();
arrayMapsByRank[r-1] = new CompositeStringTypeReadOnlyDictionary(sources);
TrimmableTypeMap.Initialize(
TypeMapping.GetOrCreateExternalTypeMapping<Java.Lang.Object>(),
TypeMapping.GetOrCreateProxyTypeMapping<Java.Lang.Object>(),
arrayMapsByRank);
Also extends `IgnoresAccessChecksTo` to cover per-asm DLLs in shared
mode when arrays are enabled (root needs to reach their internal sealed
`__ArrayMapRank{N}` types).
Adds 4 generator unit tests; all 450 tests pass. Validated default
Release CoreCLR trimmable lane: 917/0/3 (matches baseline).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>Removes obvious redundant inline comments and trims a couple of doc summaries; behavior unchanged. 450/450 unit tests still pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds an IsSZArray guard inside the leaf walk so multi-dimensional arrays (e.g. byte[,], byte[,][]) bail out cleanly rather than walking through GetElementType() as if they were jagged szarray chains. JNI only supports single-dim zero-based arrays so any multi-dim element type reaching this method indicates a caller bug — return false rather than silently producing a misleading rank-N lookup. In practice this is unreachable from MCW-generated marshaling (JNI arrays are always szarrays) so behavior is unchanged for real callers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replaces TypeMapAssemblyData.RankSentinels (a RankSentinelNames wrapper
class storing a list of derived-from-rank strings) with a plain int
MaxArrayRank field. The sentinel names are always `__ArrayMapRank{N}`
by convention — both the per-asm emitter and the runtime/root loader
hard-code that pattern — so the wrapper added no value beyond rank
storage.
Removes ~35 LOC and a public-ish helper class. 450/450 unit tests pass.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>Folds the multi-source first-hit logic into SingleUniverseTypeMap by making its rank dict storage jagged (`[rank-1][source]`). The composite class was just a thin wrapper around a list of dicts that did first-hit TryGetValue — exactly what the type map's own array lookup needs to do. Native jagged storage avoids the wrapper allocation, removes one type, and eliminates a member-ref + newobj from the generated IL per rank. Initialize signature changes: the single-universe-with-arrays overload now takes `IReadOnlyDictionary<string,Type>?[]?[]?` (jagged) instead of `IReadOnlyDictionary<string,Type>?[]?`. The aggregate path wraps each universe's 1-source rank dicts into 1-element-inner-jagged at construction time so SingleUniverseTypeMap's storage shape is uniform. Mono.Android Release builds clean. 449/449 unit tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Defines `Microsoft.Android.Runtime.__ArrayMapRank1` ... `__ArrayMapRank8` as internal sealed types in Mono.Android. Per-asm typemap DLLs no longer emit their own rank sentinel TypeDefs — they reference the shared anchors instead. Per rank, every per-asm DLL contributes to the SAME TypeMap group, so `TypeMapping.GetOrCreateExternalTypeMapping<__ArrayMapRankN>()` returns ONE merged dict spanning all per-asm DLLs. Knock-on simplifications: - `SingleUniverseTypeMap` and `AggregateTypeMap` no longer hold rank dicts; the rank lookup table lives directly on `TrimmableTypeMap` (singleton) since it's logically global, not per-universe. - `ITypeMapWithAliasing.TryGetArrayType` removed — no longer needed. - `Initialize(typeMaps[], proxyMaps[], perUniverseArrayMaps[][])` collapses to `Initialize(typeMaps[], proxyMaps[], arrayMapsByRank[])` (1D, same shape as the single-universe overload). Both modes pass the SAME array-shape — the only thing that varies is whether the main typemap is single (shared) or N-element (aggregate). - Root assembly IL: shared and aggregate paths share an `EmitBuildArrayMapsByRank` helper that emits `maxArrayRank` calls to `GetOrCreateExternalTypeMapping<Mono.Android.__ArrayMapRankN>()` directly. Shared mode no longer needs IgnoresAccessChecksTo on per-asm DLLs. - The supported maximum is `MaxSupportedArrayRank = 8`. The generator throws `ArgumentOutOfRangeException` if `_AndroidTrimmableTypeMapMaxArrayRank` exceeds it (with a message pointing at the Mono.Android types to add for more). Net -82 LOC. Mono.Android Release builds clean. 449/449 unit tests pass. Device test (Release CoreCLR trimmable): 917 / 0 errors / 3 pre-existing failures — matches baseline. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drops the unreachable elementType null check (param is non-nullable under #nullable enable), collapses the redundant rank/index arithmetic (rankIndex = elementDepth = rank - 1), and folds the JNI name resolution into a single ternary. Behavior unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Collapses 4 IL emit paths × 4 MemberRef helpers in the root assembly generator down to 2 of each. Both paths always emit the 3-arg `Initialize` (with arrayMapsByRank); when maxArrayRank=0 the new `EmitArrayMapsByRankOrNull` helper just emits `ldnull`. Also drops the matching no-array `Initialize` C# overloads from `TrimmableTypeMap` since nothing calls them — the generated root assembly always picks the 3-arg shape now. The internal callers can pass `arrayMapsByRank: null`. Net -157 LOC. 449/449 unit tests pass; Mono.Android Release builds clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1. EmitTypeMapLoader: only build the Initialize MemberRef for the path actually taken (was building both shared and aggregate variants unconditionally — added a stray MemberRef row to every assembly). 2. ModelBuilder.Brackets: replace LINQ Enumerable.Repeat fallback for rank>=4 with a straightforward StringBuilder loop. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drops a redundant `if (maxArrayRank > 0)` guard (the default is 0 already and the parameter is validated non-negative above) and inlines `moduleName`. Behavior unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The == default check on _rankAnchorHandles[i] was redundant — since EmitRankSentinels populates all slots up to MaxArrayRank, the length check is sufficient. The error message also referenced 'TypeDef' when we now emit TypeRefs (shared anchors live in Mono.Android). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restore TrimmableTypeMap Initialize compatibility overloads, add primitive-leaf array fallback, and clarify array typemap documentation and diagnostics. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rt.Single Agent-Logs-Url: https://github.com/dotnet/android/sessions/51b7a15a-d4ad-4309-98ee-1487a03f3b99 Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
682020a to
d7ab389CompareRemove the accidental JniTypeManagerTests.GetType exclusion added during rebase conflict resolution, and re-enable JavaObjectTest.DisposeAccessesThis now that the trimmable GetThis fixture is present. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Uh oh!
There was an error while loading. Please reload this page.
…ttribute Resolve merge conflicts: - TypeMapAssemblyEmitter.cs: keep ExportMethodDispatchEmitter field + new array handling fields from main - FixtureTestBase.cs: take refactored ILContainsOpcodeToken helper from main - NUnitInstrumentation.cs: remove stale test exclusions fixed in main (#11238, #11273) Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Summary
Improves array creation for the CoreCLR trimmable typemap path by removing the per-
Tarray factory methods fromJavaPeerContainerFactory<T>and routing array construction throughJNIEnv.ArrayCreateInstance.JNIEnv.ArrayCreateInstancenow uses a runtime fork:Array.CreateInstance(elementType, length)directly.Array.CreateInstanceFromArrayType(arrayType, length).This avoids per-peer array-creation IL bloat on CoreCLR while keeping an AOT-safe path for NativeAOT.
Tracking: #11234 Phase 2 arrays-only. Container types (
JavaList<T>,JavaCollection<T>,JavaDictionary<K,V>) remain out of scope.Generator changes
When
$(_AndroidTrimmableTypeMapMaxArrayRank)is greater than 0, the per-assembly typemap generator emits speculative arrayTypeMapentries for each non-aliased, non-generic, non-primitive-key peer:Keys are bare element JNI names. Rank is encoded by the shared
__ArrayMapRank{N}anchor group, so runtime lookup avoids constructing JNI array signature strings.The MSBuild default is:
3for NativeAOT (PublishAot=true), where dynamic code is unavailable and array creation uses the typemap path.0otherwise, where dynamic code can useArray.CreateInstancedirectly.Merged-universe and aggregate root typemap generation both support array entries. When no array entries are emitted, generated loaders pass
nullforarrayMapsByRank.Runtime changes
TrimmableTypeMap.Initializehas 3-arg overloads that accept per-rank array maps, and keeps the previous 2-arg overloads as compatibility wrappers that passnull.TrimmableTypeMap.TryGetArrayType(Type elementType, out Type? arrayType)walks jagged array element types to find the leaf type and rank, then uses the per-rank__ArrayMapRank{N}dictionary keyed by the leaf JNI name. Primitive leaf arrays intentionally do not require primitive-keyed rank-map entries; they fall back to constructing the next primitive array type directly.JNIEnv.ArrayCreateInstancereports missing non-primitive array mappings with the relevant__ArrayMapRank{N}group and_AndroidTrimmableTypeMapMaxArrayRankguidance.Validation
dotnet build src/Microsoft.Android.Sdk.TrimmableTypeMap/Microsoft.Android.Sdk.TrimmableTypeMap.csproj -v:minimal -nologo --no-restoredotnet test tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests.csproj --no-restore -v:minimal -nologo -consoleloggerparameters:NoSummary— 449 passeddotnet msbuild src/Mono.Android/Mono.Android.csproj /restore /p:DotNetToolPath=$(which dotnet) -v:minimal -nologo -consoleloggerparameters:NoSummary./bin/Release/dotnet/dotnet test tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests.csproj -v minimal --no-restore— 454 passedMSBUILDDISABLENODEREUSE=1 ANDROID_SERIAL=emulator-5554 ./dotnet-local.sh build -t:RunTestApp tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -c Release -p:_AndroidTypeMapImplementation=trimmable -p:UseMonoRuntime=false -p:AdbTarget="-s emulator-5554" -nr:false— Passed: 838, Failed: 0, Skipped: 50, Total: 888. ConfirmedJavaObjectTest.DisposeAccessesThisandJniTypeManagerTests.GetTypeexecuted successfully.Related issues