[TrimmableTypeMap] Add array-typemap scaffolding (blocked on ILLink fix)#11238
Draft
simonrozsival wants to merge 2 commits intodev/simonrozsival/trimmable-typemap-javacastfrom
Draft
Conversation
Generator: for every per-peer TypeMap entry, also emit speculative `[L<jni>;` / `[[L<jni>;` / `[[[L<jni>;` entries pointing at the corresponding closed managed array type (`T[]`, `T[][]`, `T[][][]`). Emitted as 3-arg conditional attributes so the trimmer drops entries whose array target type is not live in the shipped app. Skips open generics, primitive JNI keyword keys (`Z`, `B`, …), and alias groups (deferred until a real-world need). Runtime: new `ITypeMapWithAliasing.TryGetType` raw lookup that bypasses `JavaPeerProxy` filtering — array entries point directly at the closed `Type` and have no proxy. New `TrimmableTypeMap.TryGetArrayType(Type elementType, out Type? arrayType)` walks down `elementType.IsArray` / `GetElementType()` to find the leaf type and array depth, resolves the leaf JNI encoding (primitive static dict OR `TryGetJniNameForManagedType` wrapped as `L<jni>;`), prepends `[` × (depth+1), and looks up the closed array `Type` via the raw typemap. Caller then uses AOT-safe `Array.CreateInstanceFromArrayType`. `JNIEnv.ArrayCreateInstance` swaps to the new flow on hit; throws `NotSupportedException` on miss under trimmable. Legacy non-trimmable fallback unchanged. Cleanup: deletes `JavaPeerContainerFactory<T>.CreateArray` and the private `CreateHigherRankArray` helper. The base abstract method goes too. Other (collection) factory methods stay for the follow-up PR. Tests: 13 new `ArrayEntries` unit tests in `TypeMapModelBuilderTests` covering ranks 1-3, conditional emission, open-generic skip, alias-group skip, and primitive-JNI-keyword skip. Existing count-asserting tests updated via a `NonArrayEntries` helper. Tracking: #11234 (sub-task — arrays only; collection factory removal follows in a separate PR). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reverts the runtime swap and factory-method removal from the previous
commit. Keeps the new `ITypeMapWithAliasing.TryGetType` raw lookup and
`TrimmableTypeMap.TryGetArrayType` helper as scaffolding for the
eventual ILLink-fixed approach.
The speculative `[L<jni>;` / `[[L<jni>;` / `[[[L<jni>;` TypeMap entries
crash ILLink with:
System.NotSupportedException: TypeDefinition cannot be resolved from
'Mono.Cecil.ArrayType' type
at Mono.Linker.LinkContext.Resolve(TypeReference typeReference)
at Mono.Linker.TypeMapHandler.RecordTypeMapEntry(...) (3-arg form)
at Mono.Linker.TypeMapHandler.MarkTypeMapAttribute(...) (2-arg form)
at Mono.Linker.TypeMapHandler.ProcessExternalTypeMapGroupSeen(...)
Both 2-arg and 3-arg TypeMap forms are affected — `MarkTypeMapAttribute`
calls `LinkContext.Resolve` on the `TargetType` slot (constructor arg
index 1) for any TypeMap, and Cecil's `ArrayType` is not a
`TypeDefinition`. There is no shape of `TypeMapAttribute` today that
accepts a closed array `Type`.
`EmitArrayEntries` is now a documented no-op.
`JavaPeerContainerFactory<T>.CreateArray` and
`CreateHigherRankArray` are restored.
`JNIEnv.ArrayCreateInstance` falls back to the legacy per-T factory
under trimmable.
Trimmable + CoreCLR lane after revert: 917 total, 0 errors, 3 failures
(pre-existing `TryGetJniNameForManagedType_*`, out of scope — same
baseline as #11225).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
Status
11.0.100-preview.5.26228.123) makes ILLink'sTypeMapHandlerstripTypeSpecificationwrappers (array, pointer, byref) beforeLinkContext.Resolve— closing dotnet/runtime#126177.This PR currently ships the runtime + generator scaffolding but leaves
EmitArrayEntriesas a no-op until our SDK bumps to a build with #126380. Validation against a nightly SDK confirms the approach works as designed (see "Validation" below).Tracking: #11234 (sub-task — arrays only).
What this enables (once flipped)
For every existing per-peer
TypeMap<JavaObject>("<jni>", typeof(<Proxy>), typeof(<Target>)), also emit ranks 1–3 array entries:Runtime:
TrimmableTypeMap.TryGetArrayType(elementType, out arrayType)walks downIsArray/GetElementType()to find the leaf type and array depth, resolves the leaf JNI encoding (primitive static dict ORTryGetJniNameForManagedType), and looks the array up via a new rawITypeMapWithAliasing.TryGetType.JNIEnv.ArrayCreateInstancethen callsArray.CreateInstanceFromArrayType(AOT-safe) — eliminating per-TJavaPeerContainerFactory<T>.CreateArrayinstantiations.What's in this PR
Generator/ModelBuilder.csEmitArrayEntries— documented no-op pending SDK bump that brings #126380. Body ready to flip on.Microsoft.Android.Runtime/ITypeMapWithAliasing.csbool TryGetType(string jniName, out Type? type)— raw lookup that bypassesJavaPeerProxyfiltering.Microsoft.Android.Runtime/SingleUniverseTypeMap.csTryGetType.Microsoft.Android.Runtime/AggregateTypeMap.csTryGetType(first-wins across universes).Microsoft.Android.Runtime/TrimmableTypeMap.csinternal bool TryGetArrayType(Type elementType, out Type? arrayType)implementing the leaf-walk + key-build + lookup flow.Android.Runtime/JNIEnv.csArrayCreateInstancefalls back to the legacy per-T factory under trimmable, with a TODO.Java.Interop/JavaPeerContainerFactory.csCreateArray/CreateHigherRankArrayretained, with a TODO.tests/.../TypeMapModelBuilderTests.csEmitArrayEntriesproduces no entries today.Validation
Existing dotnet/android lanes (current SDK, scaffolding state)
RunTestApplane on emulator: 917 total, 0 errors, 3 failures — the 3 failures are the pre-existingTryGetJniNameForManagedType_*tests called out as out-of-scope in [TrimmableTypeMap] JavaCast/JavaAs + container support #11225's PR description. No regression.End-to-end with nightly SDK including #126380
Tested with a minimal repro program against
11.0.100-preview.5.26228.123(which bundlesMicrosoft.NET.ILLink.Tasks 11.0.0-preview.5.26228.123):PublishTrimmed=trueoutput:So the trimmer:
This is exactly the trim-by-peer-reachability behavior we want. The 3× overhead is bounded by reachable peers only.
Path forward
Once dotnet/android's SDK picks up a build with #126380:
EmitArrayEntries(3-arg conditional, ranks 1–3, with the existing skip rules for open generics / primitive JNI keywords / alias groups).JNIEnv.ArrayCreateInstance(usesTryGetArrayType+Array.CreateInstanceFromArrayType— already wired in this PR).JavaPeerContainerFactory<T>.CreateArray/CreateHigherRankArray.The runtime helpers shipped here (
TryGetType,TryGetArrayType,TryGetPrimitiveJniEncoding) are the API the follow-up will call directly — already covered by unit tests viaBuild_DoesNotEmitArrayEntries_PendingILLinkFix.Out of scope
JavaList<T>/JavaCollection<T>/JavaDictionary<K,V>— separate PR.JavaPeerContainerFactoryentirely — separate PR after collections.[DAM(Constructors)]annotation onJavaPeerProxy<T>([TrimmableTypeMap] Address all trimming and AOT warnings #10794).