[TrimmableTypeMap] Fix app initialization and startup#11252
[TrimmableTypeMap] Fix app initialization and startup#11252simonrozsival wants to merge 21 commits intomainfrom
Conversation
Initialize typemap data before AndroidRuntime construction, then register the trimmable Runtime.registerNatives bridge after JniRuntime.Current is available. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Emit TypeMapAssemblyTargetAttribute<T> with per-assembly anchors in aggregate mode while preserving the shared anchor in merged mode. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep GenerateJavaPeer=false peers as direct typemap entries, suppress inherited activation constructors for them, and split target-type lookup from generated-proxy lookup. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adjusts the trimmable typemap startup sequence and typemap metadata generation so that managed typemap data is available before AndroidRuntime construction, while native registrations that require JniRuntime.Current happen after the runtime is set.
Changes:
- Move trimmable typemap data initialization earlier in
JNIEnvInit.Initialize()and registermono.android.Runtime.registerNatives(Class)afterJniRuntime.SetCurrent(). - Update root typemap generation to emit
TypeMapAssemblyTargetAttribute<T>with per-assembly anchors in aggregate mode and a shared anchor in merged mode, with new metadata-level tests. - Refine scanning/model building and runtime lookup to treat
GenerateJavaPeer=falsepeers as direct typemap entries, suppress inherited activation ctor resolution for them, and split “target type” vs “proxy type” lookup paths.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.cs | Adds a Java.Interop-style activation ctor to support activation-ctor scanning scenarios in tests. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.cs | Adds coverage ensuring GenerateJavaPeer=false peers do not inherit activation ctors. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.cs | Ensures non-generated peers without activation ctors produce no proxy types/associations. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.cs | Adds tests validating per-assembly vs shared anchor behavior by decoding attribute/type spec metadata. |
| src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.cs | Splits native registration from initialization and adds separate caches for target-type and proxy lookup. |
| src/Mono.Android/Microsoft.Android.Runtime/SingleUniverseTypeMap.cs | Splits target-type enumeration from proxy-type enumeration and centralizes alias entry traversal. |
| src/Mono.Android/Microsoft.Android.Runtime/ITypeMapWithAliasing.cs | Updates interface to expose separate target/proxy enumeration methods. |
| src/Mono.Android/Microsoft.Android.Runtime/AggregateTypeMap.cs | Implements new interface shape across multiple universes. |
| src/Mono.Android/Android.Runtime/JNIEnvInit.cs | Adjusts initialization ordering and registers typemap native bridge after JniRuntime.Current exists. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs | Suppresses inherited activation ctor discovery for IsFromJniTypeSignature && DoNotGenerateAcw. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.cs | Emits TypeMapAssemblyTargetAttribute<T> using per-assembly anchors in aggregate mode. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs | Extracts proxy-creation predicate to keep direct typemap entries for non-generated peers. |
Separate shared-universe and per-assembly-universe TypeMapAssemblyTargetAttribute emission paths for clarity. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Limit the trimmable typemap scanner to Register/component peers for now and restore proxy-only runtime lookup semantics. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove the temporary NeedsProxy helper refactor and the extra blank line so this PR stays focused on functional changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Exclude Java.Interop JniTypeSignature ManagedPeer tests that are outside the current trimmable typemap scope and add equivalent Android [Register]-based coverage for dispose, finalization, nested dispose, and generic holder activation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Android app assemblies do not have a managed entry point, so remove the SDK default EntryPoint trimmer root and root the app assembly with RootMode=All for CoreCLR trimmable typemap builds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CoreCLRTrimmable is a test flavor, not an NUnit category. Since it runs on CoreCLR, keep the standard CoreCLRIgnore and NTLM exclusions while also excluding trimmable-specific categories. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move trimmable typemap assembly preparation out of _GenerateJavaStubs so packaging, compression, and register-attribute removal see the generated typemap assemblies even when Java stub generation is skipped. Update CoreCLR typemap store handling to depend on the prepared typemap assembly item groups. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Capture component attribute values needed by the trimmable typemap scanner, including content provider authorities, and normalize connector managed type names consistently. Keep scanner coverage for the component and connector metadata paths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Record invoker type associations on their generated proxies so trimmable typemap lookup can resolve invoker registered JNI names without generating separate proxy entries. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Prefer pregenerated trimmable typemap JNI names in the type manager and walk base types for managed-only subclasses that do not have their own Register attribute. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Register JNI natives through pregenerated JniNativeMethod entries and ldftn function pointers instead of generated delegate registration. Generate UCO forwarders with the legacy marshal-method wrapper shape and keep inherited activation pregenerated with direct activation constructor calls. Cover the direct registration, UCO wrapper, default UnmanagedCallersOnly, boolean ABI, and inherited activation IL shapes in generator tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restore JniTypeSignature peer discovery and alias ownership after merging main, keep intentional trimmable exclusions for replaced ManagedPeer coverage, and update focused generator/runtime cleanup changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Track emitted IL stack depth in PEAssemblyBuilder instead of using a fixed maxstack of 32, and keep a minimum maxstack of 8 with safety padding. Also keep CoreCLR trimmable test discovery trim-safe without broad assembly roots and validate MAUI CoreCLR trimmable startup. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove the computed maxstack generator changes from this startup-fixes branch so they can be reviewed in a separate PR based on this branch. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Consolidate repeated trimmable feature-switch guards and desugar fallback assertions, and use nullable-aware string helpers in the typemap model builder. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep the generator tests focused on metadata shape and exception-region structure, and rely on the trimmable CoreCLR device tests for runtime behavior instead of matching call tokens in emitted IL bytes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
simonrozsival
left a comment
There was a problem hiding this comment.
Issue counts: ❌ 0,
| /// Returns all types mapped to a JNI name, resolving alias holders. | ||
| /// For non-alias entries this yields a single type. For alias groups | ||
| /// it follows each alias key and yields the surviving target types. | ||
| /// Returns all proxy types mapped to a JNI name, resolving alias holders. |
There was a problem hiding this comment.
🤖 💡 Code organization — This comment now clarifies that the method returns generated proxy types rather than target types, but the method name is still the generic GetTypes. Since this interface is internal and all call sites are in this PR, consider renaming it to GetProxyTypes so the type-level distinction is visible at call sites too.
Rule: Method names must reflect behavior (Postmortem #4)
Summary
This fixes CoreCLR app startup with
_AndroidTypeMapImplementation=trimmablewhile keeping the trimmable typemap path trim-safe:JniTypeoverload formono/android/RuntimeJniNativeMethodrows and directldftnfunction pointersRootMode=AllrootsFollow-up PR for the non-trivial generated IL maxstack work: #11260.
Details
Runtime initialization
The runtime uses the UTF-8
JniTypeconstructor formono/android/Runtime, relying on the Java.Interop fix that makes the UTF-8 overload resolve through the app class loader correctly.Trim-safe test roots
The CoreCLR trimmable device-test project no longer uses broad
RootMode=Allroots by default for test-discovery assemblies. It now usesRootMode=Visiblefor the test assemblies and has a validation target that rejects broad roots in the default trimmable mode.Legacy broad test-discovery roots are still available behind
RootAssembliesForTrimmableTestDiscovery=trueif needed for comparison/debugging.The
StartupHookassembly is preserved narrowly viaTrimmerRoots.xmlinstead of rooting a whole assembly set, and Android test project references disable transitive project references to avoid duplicate Java.Interop references.Trimmable typemap packaging
_AddTrimmableTypeMapAssembliesToStorenow batches typemap assemblies per ABI, so CoreCLR trimmable packages include the generated typemap DLLs for every target ABI instead of only the last evaluated ABI.Test exclusions and coverage
The stale Java.Interop
JniTypeSignatureManagedPeer exclusions were removed after validating the tests now pass in the CoreCLR trimmable device-test run.The generator tests still verify metadata shape and exception-region structure, but no longer parse method-body IL bytes looking for specific call tokens. Runtime behavior is covered by the trimmable CoreCLR device-test lane.
Validation
./dotnet-local.sh build src/Mono.Android/Mono.Android.csproj -v:minimal -nr:falsedotnet test tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests.csproj -v minimal./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:AndroidPackageFormat=apk -p:RestoreConfigFile=/Users/simonrozsival/Projects/dotnet/android/NuGet.config -nr:false -v:minimalTrimmableTypeMapTypeManagerTestscases succeeded