[TrimmableTypeMap] Fix app initialization and startup - #11252

Merged
simonrozsival merged 29 commits into
mainfrom
trimmable-typemap-startup-fixes
May 4, 2026
Merged

[TrimmableTypeMap] Fix app initialization and startup#11252
simonrozsival merged 29 commits into
mainfrom
trimmable-typemap-startup-fixes

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented Apr 30, 2026

Copy link
Copy Markdown
Member

Summary

Fixes CoreCLR app startup with _AndroidTypeMapImplementation=trimmable while keeping the trimmable typemap path trim-safe and avoiding broad test roots.

  • initialize and register trimmable typemap data without broad assembly rooting
  • use the UTF-8 JniType overload for mono/android/Runtime
  • preserve trimmable activation/proxy lookup without delegate-registration or reflection-activation fallbacks
  • emit pregenerated UCO native registration with JniNativeMethod rows and direct ldftn function pointers
  • package trimmable typemap assemblies for each CoreCLR ABI
  • avoid duplicate debug typemap entries
  • keep CoreCLRTrimmable runtime-test discovery working with narrow roots
  • avoid pulling standalone external Java.Interop into Androidized runtime tests
  • keep generator tests focused on metadata/exception-region shape instead of brittle emitted-IL call-token byte patterns

Follow-up PR for the non-trivial generated IL maxstack work: #11260.

Details

Runtime initialization

The runtime initialization path now uses the UTF-8 JniType constructor for mono/android/Runtime and registers trimmable typemap data early enough for CoreCLR startup.

Trimmable typemap runtime behavior

The trimmable typemap runtime path preserves activation and proxy lookup for registered peer types without falling back to reflection activation or delegate registration. The scanner also avoids treating JNI primitive keyword signatures as normal peer mappings.

TypeMap generation and packaging

The typemap generator now emits the raw metadata needed by the runtime path, including pregenerated UCO native registration data. CoreCLR trimmable packaging batches typemap assemblies per ABI so generated typemap DLLs are included for each target ABI.

Test roots and Java.Interop references

The CoreCLRTrimmable device-test project uses narrow visible roots plus explicit startup-hook roots instead of broad default RootMode=All roots. The Androidized Java.Interop test project compiles the GenericMarshaler helper directly so it binds against Android's platform Java.Interop, avoiding the standalone external Java.Interop project/reference in runtime tests.

Test exclusions and coverage

Runtime behavior is covered by the CoreCLRTrimmable device-test lane, including TrimmableTypeMapTypeManagerTests. Generator tests continue to validate metadata shape and exception-region structure without depending on exact emitted IL call-token byte sequences.

Validation

Note: do not pass -p:ExcludeCategories=... for the CoreCLRTrimmable RunTestApp command below. ExcludeCategories is appended inside Mono.Android.NET-Tests.csproj; setting it as a global property prevents the project from adding trimmable-specific exclusions such as NativeTypeMap:Export.

  • MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -c Release -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -nr:false -tl:off -v:minimal
    • passed
  • dotnet test tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests.csproj -v minimal
    • 430 passed, 0 failed
  • ./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter "FullyQualifiedName~TrimmableTypeMapBuildTests"
    • 5 passed, 0 failed
  • ANDROID_SERIAL=R58Y30HZ65V MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -t:RunTestApp -c Release -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -nr:false -tl:off -v:minimal
    • 887 total, 0 errors, 0 failures, 51 ignored
  • Clean local .NET MAUI app run:
    • Installed local MAUI Android/Tizen workload records with manifest updates disabled so the repo-local SDK can build UseMaui projects.
    • Removed bin/ and obj/ from /Users/simonrozsival/Projects/dotnet/playground/TestBlankMauiP5.
    • ANDROID_SERIAL=R58Y30HZ65V ./dotnet-local.sh build /Users/simonrozsival/Projects/dotnet/playground/TestBlankMauiP5/TestBlankMauiP5.csproj -t:Run -f net11.0-android -c Release -p:TargetFrameworks=net11.0-android -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -p:AndroidPackageFormat=apk -p:RestoreConfigFile=/Users/simonrozsival/Projects/dotnet/android/NuGet.config -nr:false -tl:off -v:minimal
    • passed; com.companyname.testblankmauip5/crc64f2a221357d608c26.MainActivity was installed and focused in the foreground on R58Y30HZ65V.

simonrozsivaland others added 3 commits April 30, 2026 10:28
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>
@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Apr 30, 2026
CopilotAI review requested due to automatic review settings April 30, 2026 08:33
@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Apr 30, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 register mono.android.Runtime.registerNatives(Class) after JniRuntime.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=false peers 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
FileDescription
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.csAdds a Java.Interop-style activation ctor to support activation-ctor scanning scenarios in tests.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.csAdds coverage ensuring GenerateJavaPeer=false peers do not inherit activation ctors.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.csEnsures non-generated peers without activation ctors produce no proxy types/associations.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.csAdds tests validating per-assembly vs shared anchor behavior by decoding attribute/type spec metadata.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.csSplits native registration from initialization and adds separate caches for target-type and proxy lookup.
src/Mono.Android/Microsoft.Android.Runtime/SingleUniverseTypeMap.csSplits target-type enumeration from proxy-type enumeration and centralizes alias entry traversal.
src/Mono.Android/Microsoft.Android.Runtime/ITypeMapWithAliasing.csUpdates interface to expose separate target/proxy enumeration methods.
src/Mono.Android/Microsoft.Android.Runtime/AggregateTypeMap.csImplements new interface shape across multiple universes.
src/Mono.Android/Android.Runtime/JNIEnvInit.csAdjusts initialization ordering and registers typemap native bridge after JniRuntime.Current exists.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.csSuppresses inherited activation ctor discovery for IsFromJniTypeSignature && DoNotGenerateAcw.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.csEmits TypeMapAssemblyTargetAttribute<T> using per-assembly anchors in aggregate mode.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.csExtracts proxy-creation predicate to keep direct typemap entries for non-generated peers.

@simonrozsivalsimonrozsival changed the title Fix trimmable typemap startup[TrimmableTypeMap] Fix app initialization and startupApr 30, 2026
simonrozsivaland others added 16 commits April 30, 2026 11:08
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>
simonrozsivaland others added 2 commits May 1, 2026 13:19
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>

@simonrozsivalsimonrozsival left a comment

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Needs Changes pending CI. I didn't find any blocking code correctness issues in the final diff, but the internal Xamarin.Android-PR check is still in progress, so this isn't merge-ready yet.

Issue counts: ❌ 0, ⚠️ 0, 💡 1. The trimmable runtime/device coverage and removal of brittle IL token assertions are good improvements.

simonrozsivaland others added 6 commits May 1, 2026 23:14
Keep the StartupHook linker descriptor active independently of the broad trimmable test discovery roots so linked Mono.Android.NET_Tests variants can still invoke StartupHook.Initialize().
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ensure duplicate Java-to-managed debug typemap entries use the selected managed template consistently, including the CoreCLR side table assembly and token metadata. Prefer Mono.Android for duplicate mappings so framework types such as java/lang/String surface as Java.Lang.String instead of test aliases.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the legacy RootAssembliesForTrimmableTestDiscovery escape hatch and its broad framework assembly roots. Keep only visible test assembly roots and narrow descriptors so CoreCLRTrimmable coverage does not mask trim-safety issues.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace DisableTransitiveProjectReferences with a targeted filter for the standalone external Java.Interop project output. This keeps transitive project references enabled while avoiding duplicate Java.Interop compile references in the Android test projects.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the custom _ValidateTrimmableTestRoots target from Mono.Android.NET-Tests. The project now relies on the explicit trimmer roots it declares without an extra local enforcement target.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Compile the GenericMarshaler helper directly into the Androidized Java.Interop test project so it binds against the platform Java.Interop assembly instead of pulling in the standalone external Java.Interop project.
Remove the temporary reference-filter targets from the runtime test projects now that the standalone project reference is gone.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@github-actions

github-actionsBot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — well-structured initialization fix

Summary: This PR correctly reorders trimmable typemap initialization to fix CoreCLR app startup. The changes are architecturally sound, well-tested, and consistent with existing patterns.

Key observations

Initialization ordering (core fix): Splitting TypeMapLoader.Initialize() (data loading, before runtime) from TrimmableTypeMap.RegisterNativeMethods() (JNI registration, after runtime) is the right architectural fix. The JniType("mono/android/Runtime"u8) switch is now safe because the runtime's ClassLoader is available at the point of native registration.

UCO forwarder try/catch: The emitted WaitForBridgeProcessing → try { callback } catch { UnhandledException } pattern correctly mirrors the legacy JNINativeWrapper.g.cs wrappers. Unconditional catch (vs. the legacy exception filter) is correct for [UnmanagedCallersOnly] on CoreCLR.

Debug typemap duplicate fix (TypeMappingDebugNativeAssemblyGeneratorCLR.cs): Good bug fix — the old code was using entry.ManagedName/entry.AssemblyName when it should have been using managedEntry.* after resolving duplicates.

Per-assembly anchors (RootTypeMapAssemblyGenerator.cs): The split into EmitSharedUniverseAssemblyTargetAttributes vs EmitPerAssemblyUniverseAssemblyTargetAttributes correctly ensures TypeMapAssemblyTargetAttribute<T>'s anchor T matches what TypeMapping.GetOrCreate*TypeMapping<T>() expects at runtime.

MSBuild target extraction (_PrepareTrimmableTypeMapAssemblies): Correctly fixes the incremental build item-group problem — _GenerateJavaStubs can be skipped, which would leave assembly items empty for downstream targets.

Issues by severity

SeverityCountDetails
💡 Suggestion1MSBuild: use ->Count() instead of != '' for item empty checks (line 68, 76 in CoreCLR.targets)

CI Status

  • license/cla: ✅ passed
  • dotnet-android: ⚠️action_required (may need approval/re-trigger)
  • Xamarin.Android-PR: not yet visible

Note: PR has merge conflicts (mergeable_state: dirty) — will need a rebase before merge.

Generated by Android PR Reviewer for issue #11252 · ● 15.3M

simonrozsivaland others added 2 commits May 2, 2026 20:12
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival added the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label May 4, 2026

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one minor comment.

Comment on lines -86 to +80
<TrimmerRootAssembly Include="StartupHook" RootMode="All" />
<TrimmerRootAssembly Include="Mono.Android.NET-Tests" RootMode="Visible" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootAssembly Include="Java.Interop-Tests" RootMode="Visible" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootDescriptor Include="TrimmerRoots.xml" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootDescriptor Include="StartupHookRoots.xml" Condition=" '$(StartupHookSupport)' == 'true' " />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be:

<TrimmerRootAssemblyInclude="StartupHook"RootMode="All"Condition=" '$(StartupHookSupport)' == 'true' " />

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this can be simplified. I will merge this PR now since it's green and open a follow-up PR.

@simonrozsival
simonrozsival merged commit 9a3fded into mainMay 4, 2026
3 checks passed
@simonrozsival
simonrozsival deleted the trimmable-typemap-startup-fixes branch May 4, 2026 15:17
jonathanpeppers pushed a commit that referenced this pull request May 26, 2026
Trim our trimmable-typemap test name exclusions down to just InvokeVirtualFromConstructorTests (the only one main keeps). All the JavaProxy* / JniPeerMembers / generic-handling exclusions were added during dogfooding before the trimmable typemap fixes (#11123, #11270-#11275, #11252, etc.) landed on main; they should pass now. If any still fail, CI will surface them and we can re-add individually.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 4, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot`copilot-cli` or other AIs were used to author thisready-to-reviewThis PR is ready to review/merge, I think any CI failures are just flaky (ignorable).trimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simonrozsival@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

[TrimmableTypeMap] Fix app initialization and startup - #11252

Merged
simonrozsival merged 29 commits into
mainfrom
trimmable-typemap-startup-fixes
May 4, 2026
Merged

[TrimmableTypeMap] Fix app initialization and startup#11252
simonrozsival merged 29 commits into
mainfrom
trimmable-typemap-startup-fixes

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented Apr 30, 2026

Copy link
Copy Markdown
Member

Summary

Fixes CoreCLR app startup with _AndroidTypeMapImplementation=trimmable while keeping the trimmable typemap path trim-safe and avoiding broad test roots.

  • initialize and register trimmable typemap data without broad assembly rooting
  • use the UTF-8 JniType overload for mono/android/Runtime
  • preserve trimmable activation/proxy lookup without delegate-registration or reflection-activation fallbacks
  • emit pregenerated UCO native registration with JniNativeMethod rows and direct ldftn function pointers
  • package trimmable typemap assemblies for each CoreCLR ABI
  • avoid duplicate debug typemap entries
  • keep CoreCLRTrimmable runtime-test discovery working with narrow roots
  • avoid pulling standalone external Java.Interop into Androidized runtime tests
  • keep generator tests focused on metadata/exception-region shape instead of brittle emitted-IL call-token byte patterns

Follow-up PR for the non-trivial generated IL maxstack work: #11260.

Details

Runtime initialization

The runtime initialization path now uses the UTF-8 JniType constructor for mono/android/Runtime and registers trimmable typemap data early enough for CoreCLR startup.

Trimmable typemap runtime behavior

The trimmable typemap runtime path preserves activation and proxy lookup for registered peer types without falling back to reflection activation or delegate registration. The scanner also avoids treating JNI primitive keyword signatures as normal peer mappings.

TypeMap generation and packaging

The typemap generator now emits the raw metadata needed by the runtime path, including pregenerated UCO native registration data. CoreCLR trimmable packaging batches typemap assemblies per ABI so generated typemap DLLs are included for each target ABI.

Test roots and Java.Interop references

The CoreCLRTrimmable device-test project uses narrow visible roots plus explicit startup-hook roots instead of broad default RootMode=All roots. The Androidized Java.Interop test project compiles the GenericMarshaler helper directly so it binds against Android's platform Java.Interop, avoiding the standalone external Java.Interop project/reference in runtime tests.

Test exclusions and coverage

Runtime behavior is covered by the CoreCLRTrimmable device-test lane, including TrimmableTypeMapTypeManagerTests. Generator tests continue to validate metadata shape and exception-region structure without depending on exact emitted IL call-token byte sequences.

Validation

Note: do not pass -p:ExcludeCategories=... for the CoreCLRTrimmable RunTestApp command below. ExcludeCategories is appended inside Mono.Android.NET-Tests.csproj; setting it as a global property prevents the project from adding trimmable-specific exclusions such as NativeTypeMap:Export.

  • MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -c Release -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -nr:false -tl:off -v:minimal
    • passed
  • dotnet test tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests.csproj -v minimal
    • 430 passed, 0 failed
  • ./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter "FullyQualifiedName~TrimmableTypeMapBuildTests"
    • 5 passed, 0 failed
  • ANDROID_SERIAL=R58Y30HZ65V MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -t:RunTestApp -c Release -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -nr:false -tl:off -v:minimal
    • 887 total, 0 errors, 0 failures, 51 ignored
  • Clean local .NET MAUI app run:
    • Installed local MAUI Android/Tizen workload records with manifest updates disabled so the repo-local SDK can build UseMaui projects.
    • Removed bin/ and obj/ from /Users/simonrozsival/Projects/dotnet/playground/TestBlankMauiP5.
    • ANDROID_SERIAL=R58Y30HZ65V ./dotnet-local.sh build /Users/simonrozsival/Projects/dotnet/playground/TestBlankMauiP5/TestBlankMauiP5.csproj -t:Run -f net11.0-android -c Release -p:TargetFrameworks=net11.0-android -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -p:AndroidPackageFormat=apk -p:RestoreConfigFile=/Users/simonrozsival/Projects/dotnet/android/NuGet.config -nr:false -tl:off -v:minimal
    • passed; com.companyname.testblankmauip5/crc64f2a221357d608c26.MainActivity was installed and focused in the foreground on R58Y30HZ65V.

simonrozsivaland others added 3 commits April 30, 2026 10:28
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>
@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Apr 30, 2026
CopilotAI review requested due to automatic review settings April 30, 2026 08:33
@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Apr 30, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 register mono.android.Runtime.registerNatives(Class) after JniRuntime.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=false peers 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
FileDescription
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.csAdds a Java.Interop-style activation ctor to support activation-ctor scanning scenarios in tests.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.csAdds coverage ensuring GenerateJavaPeer=false peers do not inherit activation ctors.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.csEnsures non-generated peers without activation ctors produce no proxy types/associations.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.csAdds tests validating per-assembly vs shared anchor behavior by decoding attribute/type spec metadata.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.csSplits native registration from initialization and adds separate caches for target-type and proxy lookup.
src/Mono.Android/Microsoft.Android.Runtime/SingleUniverseTypeMap.csSplits target-type enumeration from proxy-type enumeration and centralizes alias entry traversal.
src/Mono.Android/Microsoft.Android.Runtime/ITypeMapWithAliasing.csUpdates interface to expose separate target/proxy enumeration methods.
src/Mono.Android/Microsoft.Android.Runtime/AggregateTypeMap.csImplements new interface shape across multiple universes.
src/Mono.Android/Android.Runtime/JNIEnvInit.csAdjusts initialization ordering and registers typemap native bridge after JniRuntime.Current exists.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.csSuppresses inherited activation ctor discovery for IsFromJniTypeSignature && DoNotGenerateAcw.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.csEmits TypeMapAssemblyTargetAttribute<T> using per-assembly anchors in aggregate mode.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.csExtracts proxy-creation predicate to keep direct typemap entries for non-generated peers.

@simonrozsivalsimonrozsival changed the title Fix trimmable typemap startup[TrimmableTypeMap] Fix app initialization and startupApr 30, 2026
simonrozsivaland others added 16 commits April 30, 2026 11:08
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>
simonrozsivaland others added 2 commits May 1, 2026 13:19
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>

@simonrozsivalsimonrozsival left a comment

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Needs Changes pending CI. I didn't find any blocking code correctness issues in the final diff, but the internal Xamarin.Android-PR check is still in progress, so this isn't merge-ready yet.

Issue counts: ❌ 0, ⚠️ 0, 💡 1. The trimmable runtime/device coverage and removal of brittle IL token assertions are good improvements.

simonrozsivaland others added 6 commits May 1, 2026 23:14
Keep the StartupHook linker descriptor active independently of the broad trimmable test discovery roots so linked Mono.Android.NET_Tests variants can still invoke StartupHook.Initialize().
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ensure duplicate Java-to-managed debug typemap entries use the selected managed template consistently, including the CoreCLR side table assembly and token metadata. Prefer Mono.Android for duplicate mappings so framework types such as java/lang/String surface as Java.Lang.String instead of test aliases.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the legacy RootAssembliesForTrimmableTestDiscovery escape hatch and its broad framework assembly roots. Keep only visible test assembly roots and narrow descriptors so CoreCLRTrimmable coverage does not mask trim-safety issues.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace DisableTransitiveProjectReferences with a targeted filter for the standalone external Java.Interop project output. This keeps transitive project references enabled while avoiding duplicate Java.Interop compile references in the Android test projects.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the custom _ValidateTrimmableTestRoots target from Mono.Android.NET-Tests. The project now relies on the explicit trimmer roots it declares without an extra local enforcement target.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Compile the GenericMarshaler helper directly into the Androidized Java.Interop test project so it binds against the platform Java.Interop assembly instead of pulling in the standalone external Java.Interop project.
Remove the temporary reference-filter targets from the runtime test projects now that the standalone project reference is gone.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@github-actions

github-actionsBot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — well-structured initialization fix

Summary: This PR correctly reorders trimmable typemap initialization to fix CoreCLR app startup. The changes are architecturally sound, well-tested, and consistent with existing patterns.

Key observations

Initialization ordering (core fix): Splitting TypeMapLoader.Initialize() (data loading, before runtime) from TrimmableTypeMap.RegisterNativeMethods() (JNI registration, after runtime) is the right architectural fix. The JniType("mono/android/Runtime"u8) switch is now safe because the runtime's ClassLoader is available at the point of native registration.

UCO forwarder try/catch: The emitted WaitForBridgeProcessing → try { callback } catch { UnhandledException } pattern correctly mirrors the legacy JNINativeWrapper.g.cs wrappers. Unconditional catch (vs. the legacy exception filter) is correct for [UnmanagedCallersOnly] on CoreCLR.

Debug typemap duplicate fix (TypeMappingDebugNativeAssemblyGeneratorCLR.cs): Good bug fix — the old code was using entry.ManagedName/entry.AssemblyName when it should have been using managedEntry.* after resolving duplicates.

Per-assembly anchors (RootTypeMapAssemblyGenerator.cs): The split into EmitSharedUniverseAssemblyTargetAttributes vs EmitPerAssemblyUniverseAssemblyTargetAttributes correctly ensures TypeMapAssemblyTargetAttribute<T>'s anchor T matches what TypeMapping.GetOrCreate*TypeMapping<T>() expects at runtime.

MSBuild target extraction (_PrepareTrimmableTypeMapAssemblies): Correctly fixes the incremental build item-group problem — _GenerateJavaStubs can be skipped, which would leave assembly items empty for downstream targets.

Issues by severity

SeverityCountDetails
💡 Suggestion1MSBuild: use ->Count() instead of != '' for item empty checks (line 68, 76 in CoreCLR.targets)

CI Status

  • license/cla: ✅ passed
  • dotnet-android: ⚠️action_required (may need approval/re-trigger)
  • Xamarin.Android-PR: not yet visible

Note: PR has merge conflicts (mergeable_state: dirty) — will need a rebase before merge.

Generated by Android PR Reviewer for issue #11252 · ● 15.3M

simonrozsivaland others added 2 commits May 2, 2026 20:12
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival added the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label May 4, 2026

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one minor comment.

Comment on lines -86 to +80
<TrimmerRootAssembly Include="StartupHook" RootMode="All" />
<TrimmerRootAssembly Include="Mono.Android.NET-Tests" RootMode="Visible" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootAssembly Include="Java.Interop-Tests" RootMode="Visible" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootDescriptor Include="TrimmerRoots.xml" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootDescriptor Include="StartupHookRoots.xml" Condition=" '$(StartupHookSupport)' == 'true' " />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be:

<TrimmerRootAssemblyInclude="StartupHook"RootMode="All"Condition=" '$(StartupHookSupport)' == 'true' " />

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this can be simplified. I will merge this PR now since it's green and open a follow-up PR.

@simonrozsival
simonrozsival merged commit 9a3fded into mainMay 4, 2026
3 checks passed
@simonrozsival
simonrozsival deleted the trimmable-typemap-startup-fixes branch May 4, 2026 15:17
jonathanpeppers pushed a commit that referenced this pull request May 26, 2026
Trim our trimmable-typemap test name exclusions down to just InvokeVirtualFromConstructorTests (the only one main keeps). All the JavaProxy* / JniPeerMembers / generic-handling exclusions were added during dogfooding before the trimmable typemap fixes (#11123, #11270-#11275, #11252, etc.) landed on main; they should pass now. If any still fail, CI will surface them and we can re-add individually.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 4, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot`copilot-cli` or other AIs were used to author thisready-to-reviewThis PR is ready to review/merge, I think any CI failures are just flaky (ignorable).trimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simonrozsival@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[TrimmableTypeMap] Fix app initialization and startup - #11252

Merged
simonrozsival merged 29 commits into
mainfrom
trimmable-typemap-startup-fixes
May 4, 2026
Merged

[TrimmableTypeMap] Fix app initialization and startup#11252
simonrozsival merged 29 commits into
mainfrom
trimmable-typemap-startup-fixes

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented Apr 30, 2026

Copy link
Copy Markdown
Member

Summary

Fixes CoreCLR app startup with _AndroidTypeMapImplementation=trimmable while keeping the trimmable typemap path trim-safe and avoiding broad test roots.

  • initialize and register trimmable typemap data without broad assembly rooting
  • use the UTF-8 JniType overload for mono/android/Runtime
  • preserve trimmable activation/proxy lookup without delegate-registration or reflection-activation fallbacks
  • emit pregenerated UCO native registration with JniNativeMethod rows and direct ldftn function pointers
  • package trimmable typemap assemblies for each CoreCLR ABI
  • avoid duplicate debug typemap entries
  • keep CoreCLRTrimmable runtime-test discovery working with narrow roots
  • avoid pulling standalone external Java.Interop into Androidized runtime tests
  • keep generator tests focused on metadata/exception-region shape instead of brittle emitted-IL call-token byte patterns

Follow-up PR for the non-trivial generated IL maxstack work: #11260.

Details

Runtime initialization

The runtime initialization path now uses the UTF-8 JniType constructor for mono/android/Runtime and registers trimmable typemap data early enough for CoreCLR startup.

Trimmable typemap runtime behavior

The trimmable typemap runtime path preserves activation and proxy lookup for registered peer types without falling back to reflection activation or delegate registration. The scanner also avoids treating JNI primitive keyword signatures as normal peer mappings.

TypeMap generation and packaging

The typemap generator now emits the raw metadata needed by the runtime path, including pregenerated UCO native registration data. CoreCLR trimmable packaging batches typemap assemblies per ABI so generated typemap DLLs are included for each target ABI.

Test roots and Java.Interop references

The CoreCLRTrimmable device-test project uses narrow visible roots plus explicit startup-hook roots instead of broad default RootMode=All roots. The Androidized Java.Interop test project compiles the GenericMarshaler helper directly so it binds against Android's platform Java.Interop, avoiding the standalone external Java.Interop project/reference in runtime tests.

Test exclusions and coverage

Runtime behavior is covered by the CoreCLRTrimmable device-test lane, including TrimmableTypeMapTypeManagerTests. Generator tests continue to validate metadata shape and exception-region structure without depending on exact emitted IL call-token byte sequences.

Validation

Note: do not pass -p:ExcludeCategories=... for the CoreCLRTrimmable RunTestApp command below. ExcludeCategories is appended inside Mono.Android.NET-Tests.csproj; setting it as a global property prevents the project from adding trimmable-specific exclusions such as NativeTypeMap:Export.

  • MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -c Release -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -nr:false -tl:off -v:minimal
    • passed
  • dotnet test tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests.csproj -v minimal
    • 430 passed, 0 failed
  • ./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter "FullyQualifiedName~TrimmableTypeMapBuildTests"
    • 5 passed, 0 failed
  • ANDROID_SERIAL=R58Y30HZ65V MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -t:RunTestApp -c Release -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -nr:false -tl:off -v:minimal
    • 887 total, 0 errors, 0 failures, 51 ignored
  • Clean local .NET MAUI app run:
    • Installed local MAUI Android/Tizen workload records with manifest updates disabled so the repo-local SDK can build UseMaui projects.
    • Removed bin/ and obj/ from /Users/simonrozsival/Projects/dotnet/playground/TestBlankMauiP5.
    • ANDROID_SERIAL=R58Y30HZ65V ./dotnet-local.sh build /Users/simonrozsival/Projects/dotnet/playground/TestBlankMauiP5/TestBlankMauiP5.csproj -t:Run -f net11.0-android -c Release -p:TargetFrameworks=net11.0-android -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -p:AndroidPackageFormat=apk -p:RestoreConfigFile=/Users/simonrozsival/Projects/dotnet/android/NuGet.config -nr:false -tl:off -v:minimal
    • passed; com.companyname.testblankmauip5/crc64f2a221357d608c26.MainActivity was installed and focused in the foreground on R58Y30HZ65V.

simonrozsivaland others added 3 commits April 30, 2026 10:28
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>
@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Apr 30, 2026
CopilotAI review requested due to automatic review settings April 30, 2026 08:33
@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Apr 30, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 register mono.android.Runtime.registerNatives(Class) after JniRuntime.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=false peers 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
FileDescription
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.csAdds a Java.Interop-style activation ctor to support activation-ctor scanning scenarios in tests.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.csAdds coverage ensuring GenerateJavaPeer=false peers do not inherit activation ctors.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.csEnsures non-generated peers without activation ctors produce no proxy types/associations.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.csAdds tests validating per-assembly vs shared anchor behavior by decoding attribute/type spec metadata.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.csSplits native registration from initialization and adds separate caches for target-type and proxy lookup.
src/Mono.Android/Microsoft.Android.Runtime/SingleUniverseTypeMap.csSplits target-type enumeration from proxy-type enumeration and centralizes alias entry traversal.
src/Mono.Android/Microsoft.Android.Runtime/ITypeMapWithAliasing.csUpdates interface to expose separate target/proxy enumeration methods.
src/Mono.Android/Microsoft.Android.Runtime/AggregateTypeMap.csImplements new interface shape across multiple universes.
src/Mono.Android/Android.Runtime/JNIEnvInit.csAdjusts initialization ordering and registers typemap native bridge after JniRuntime.Current exists.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.csSuppresses inherited activation ctor discovery for IsFromJniTypeSignature && DoNotGenerateAcw.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.csEmits TypeMapAssemblyTargetAttribute<T> using per-assembly anchors in aggregate mode.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.csExtracts proxy-creation predicate to keep direct typemap entries for non-generated peers.

@simonrozsivalsimonrozsival changed the title Fix trimmable typemap startup[TrimmableTypeMap] Fix app initialization and startupApr 30, 2026
simonrozsivaland others added 16 commits April 30, 2026 11:08
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>
simonrozsivaland others added 2 commits May 1, 2026 13:19
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>

@simonrozsivalsimonrozsival left a comment

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Needs Changes pending CI. I didn't find any blocking code correctness issues in the final diff, but the internal Xamarin.Android-PR check is still in progress, so this isn't merge-ready yet.

Issue counts: ❌ 0, ⚠️ 0, 💡 1. The trimmable runtime/device coverage and removal of brittle IL token assertions are good improvements.

simonrozsivaland others added 6 commits May 1, 2026 23:14
Keep the StartupHook linker descriptor active independently of the broad trimmable test discovery roots so linked Mono.Android.NET_Tests variants can still invoke StartupHook.Initialize().
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ensure duplicate Java-to-managed debug typemap entries use the selected managed template consistently, including the CoreCLR side table assembly and token metadata. Prefer Mono.Android for duplicate mappings so framework types such as java/lang/String surface as Java.Lang.String instead of test aliases.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the legacy RootAssembliesForTrimmableTestDiscovery escape hatch and its broad framework assembly roots. Keep only visible test assembly roots and narrow descriptors so CoreCLRTrimmable coverage does not mask trim-safety issues.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace DisableTransitiveProjectReferences with a targeted filter for the standalone external Java.Interop project output. This keeps transitive project references enabled while avoiding duplicate Java.Interop compile references in the Android test projects.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the custom _ValidateTrimmableTestRoots target from Mono.Android.NET-Tests. The project now relies on the explicit trimmer roots it declares without an extra local enforcement target.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Compile the GenericMarshaler helper directly into the Androidized Java.Interop test project so it binds against the platform Java.Interop assembly instead of pulling in the standalone external Java.Interop project.
Remove the temporary reference-filter targets from the runtime test projects now that the standalone project reference is gone.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@github-actions

github-actionsBot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — well-structured initialization fix

Summary: This PR correctly reorders trimmable typemap initialization to fix CoreCLR app startup. The changes are architecturally sound, well-tested, and consistent with existing patterns.

Key observations

Initialization ordering (core fix): Splitting TypeMapLoader.Initialize() (data loading, before runtime) from TrimmableTypeMap.RegisterNativeMethods() (JNI registration, after runtime) is the right architectural fix. The JniType("mono/android/Runtime"u8) switch is now safe because the runtime's ClassLoader is available at the point of native registration.

UCO forwarder try/catch: The emitted WaitForBridgeProcessing → try { callback } catch { UnhandledException } pattern correctly mirrors the legacy JNINativeWrapper.g.cs wrappers. Unconditional catch (vs. the legacy exception filter) is correct for [UnmanagedCallersOnly] on CoreCLR.

Debug typemap duplicate fix (TypeMappingDebugNativeAssemblyGeneratorCLR.cs): Good bug fix — the old code was using entry.ManagedName/entry.AssemblyName when it should have been using managedEntry.* after resolving duplicates.

Per-assembly anchors (RootTypeMapAssemblyGenerator.cs): The split into EmitSharedUniverseAssemblyTargetAttributes vs EmitPerAssemblyUniverseAssemblyTargetAttributes correctly ensures TypeMapAssemblyTargetAttribute<T>'s anchor T matches what TypeMapping.GetOrCreate*TypeMapping<T>() expects at runtime.

MSBuild target extraction (_PrepareTrimmableTypeMapAssemblies): Correctly fixes the incremental build item-group problem — _GenerateJavaStubs can be skipped, which would leave assembly items empty for downstream targets.

Issues by severity

SeverityCountDetails
💡 Suggestion1MSBuild: use ->Count() instead of != '' for item empty checks (line 68, 76 in CoreCLR.targets)

CI Status

  • license/cla: ✅ passed
  • dotnet-android: ⚠️action_required (may need approval/re-trigger)
  • Xamarin.Android-PR: not yet visible

Note: PR has merge conflicts (mergeable_state: dirty) — will need a rebase before merge.

Generated by Android PR Reviewer for issue #11252 · ● 15.3M

simonrozsivaland others added 2 commits May 2, 2026 20:12
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival added the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label May 4, 2026

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one minor comment.

Comment on lines -86 to +80
<TrimmerRootAssembly Include="StartupHook" RootMode="All" />
<TrimmerRootAssembly Include="Mono.Android.NET-Tests" RootMode="Visible" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootAssembly Include="Java.Interop-Tests" RootMode="Visible" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootDescriptor Include="TrimmerRoots.xml" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootDescriptor Include="StartupHookRoots.xml" Condition=" '$(StartupHookSupport)' == 'true' " />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be:

<TrimmerRootAssemblyInclude="StartupHook"RootMode="All"Condition=" '$(StartupHookSupport)' == 'true' " />

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this can be simplified. I will merge this PR now since it's green and open a follow-up PR.

@simonrozsival
simonrozsival merged commit 9a3fded into mainMay 4, 2026
3 checks passed
@simonrozsival
simonrozsival deleted the trimmable-typemap-startup-fixes branch May 4, 2026 15:17
jonathanpeppers pushed a commit that referenced this pull request May 26, 2026
Trim our trimmable-typemap test name exclusions down to just InvokeVirtualFromConstructorTests (the only one main keeps). All the JavaProxy* / JniPeerMembers / generic-handling exclusions were added during dogfooding before the trimmable typemap fixes (#11123, #11270-#11275, #11252, etc.) landed on main; they should pass now. If any still fail, CI will surface them and we can re-add individually.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 4, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot`copilot-cli` or other AIs were used to author thisready-to-reviewThis PR is ready to review/merge, I think any CI failures are just flaky (ignorable).trimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simonrozsival@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[TrimmableTypeMap] Fix app initialization and startup - #11252

Merged
simonrozsival merged 29 commits into
mainfrom
trimmable-typemap-startup-fixes
May 4, 2026
Merged

[TrimmableTypeMap] Fix app initialization and startup#11252
simonrozsival merged 29 commits into
mainfrom
trimmable-typemap-startup-fixes

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented Apr 30, 2026

Copy link
Copy Markdown
Member

Summary

Fixes CoreCLR app startup with _AndroidTypeMapImplementation=trimmable while keeping the trimmable typemap path trim-safe and avoiding broad test roots.

  • initialize and register trimmable typemap data without broad assembly rooting
  • use the UTF-8 JniType overload for mono/android/Runtime
  • preserve trimmable activation/proxy lookup without delegate-registration or reflection-activation fallbacks
  • emit pregenerated UCO native registration with JniNativeMethod rows and direct ldftn function pointers
  • package trimmable typemap assemblies for each CoreCLR ABI
  • avoid duplicate debug typemap entries
  • keep CoreCLRTrimmable runtime-test discovery working with narrow roots
  • avoid pulling standalone external Java.Interop into Androidized runtime tests
  • keep generator tests focused on metadata/exception-region shape instead of brittle emitted-IL call-token byte patterns

Follow-up PR for the non-trivial generated IL maxstack work: #11260.

Details

Runtime initialization

The runtime initialization path now uses the UTF-8 JniType constructor for mono/android/Runtime and registers trimmable typemap data early enough for CoreCLR startup.

Trimmable typemap runtime behavior

The trimmable typemap runtime path preserves activation and proxy lookup for registered peer types without falling back to reflection activation or delegate registration. The scanner also avoids treating JNI primitive keyword signatures as normal peer mappings.

TypeMap generation and packaging

The typemap generator now emits the raw metadata needed by the runtime path, including pregenerated UCO native registration data. CoreCLR trimmable packaging batches typemap assemblies per ABI so generated typemap DLLs are included for each target ABI.

Test roots and Java.Interop references

The CoreCLRTrimmable device-test project uses narrow visible roots plus explicit startup-hook roots instead of broad default RootMode=All roots. The Androidized Java.Interop test project compiles the GenericMarshaler helper directly so it binds against Android's platform Java.Interop, avoiding the standalone external Java.Interop project/reference in runtime tests.

Test exclusions and coverage

Runtime behavior is covered by the CoreCLRTrimmable device-test lane, including TrimmableTypeMapTypeManagerTests. Generator tests continue to validate metadata shape and exception-region structure without depending on exact emitted IL call-token byte sequences.

Validation

Note: do not pass -p:ExcludeCategories=... for the CoreCLRTrimmable RunTestApp command below. ExcludeCategories is appended inside Mono.Android.NET-Tests.csproj; setting it as a global property prevents the project from adding trimmable-specific exclusions such as NativeTypeMap:Export.

  • MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -c Release -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -nr:false -tl:off -v:minimal
    • passed
  • dotnet test tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests.csproj -v minimal
    • 430 passed, 0 failed
  • ./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter "FullyQualifiedName~TrimmableTypeMapBuildTests"
    • 5 passed, 0 failed
  • ANDROID_SERIAL=R58Y30HZ65V MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -t:RunTestApp -c Release -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -nr:false -tl:off -v:minimal
    • 887 total, 0 errors, 0 failures, 51 ignored
  • Clean local .NET MAUI app run:
    • Installed local MAUI Android/Tizen workload records with manifest updates disabled so the repo-local SDK can build UseMaui projects.
    • Removed bin/ and obj/ from /Users/simonrozsival/Projects/dotnet/playground/TestBlankMauiP5.
    • ANDROID_SERIAL=R58Y30HZ65V ./dotnet-local.sh build /Users/simonrozsival/Projects/dotnet/playground/TestBlankMauiP5/TestBlankMauiP5.csproj -t:Run -f net11.0-android -c Release -p:TargetFrameworks=net11.0-android -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -p:AndroidPackageFormat=apk -p:RestoreConfigFile=/Users/simonrozsival/Projects/dotnet/android/NuGet.config -nr:false -tl:off -v:minimal
    • passed; com.companyname.testblankmauip5/crc64f2a221357d608c26.MainActivity was installed and focused in the foreground on R58Y30HZ65V.

simonrozsivaland others added 3 commits April 30, 2026 10:28
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>
@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Apr 30, 2026
CopilotAI review requested due to automatic review settings April 30, 2026 08:33
@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Apr 30, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 register mono.android.Runtime.registerNatives(Class) after JniRuntime.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=false peers 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
FileDescription
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.csAdds a Java.Interop-style activation ctor to support activation-ctor scanning scenarios in tests.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.csAdds coverage ensuring GenerateJavaPeer=false peers do not inherit activation ctors.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.csEnsures non-generated peers without activation ctors produce no proxy types/associations.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.csAdds tests validating per-assembly vs shared anchor behavior by decoding attribute/type spec metadata.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.csSplits native registration from initialization and adds separate caches for target-type and proxy lookup.
src/Mono.Android/Microsoft.Android.Runtime/SingleUniverseTypeMap.csSplits target-type enumeration from proxy-type enumeration and centralizes alias entry traversal.
src/Mono.Android/Microsoft.Android.Runtime/ITypeMapWithAliasing.csUpdates interface to expose separate target/proxy enumeration methods.
src/Mono.Android/Microsoft.Android.Runtime/AggregateTypeMap.csImplements new interface shape across multiple universes.
src/Mono.Android/Android.Runtime/JNIEnvInit.csAdjusts initialization ordering and registers typemap native bridge after JniRuntime.Current exists.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.csSuppresses inherited activation ctor discovery for IsFromJniTypeSignature && DoNotGenerateAcw.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.csEmits TypeMapAssemblyTargetAttribute<T> using per-assembly anchors in aggregate mode.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.csExtracts proxy-creation predicate to keep direct typemap entries for non-generated peers.

@simonrozsivalsimonrozsival changed the title Fix trimmable typemap startup[TrimmableTypeMap] Fix app initialization and startupApr 30, 2026
simonrozsivaland others added 16 commits April 30, 2026 11:08
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>
simonrozsivaland others added 2 commits May 1, 2026 13:19
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>

@simonrozsivalsimonrozsival left a comment

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Needs Changes pending CI. I didn't find any blocking code correctness issues in the final diff, but the internal Xamarin.Android-PR check is still in progress, so this isn't merge-ready yet.

Issue counts: ❌ 0, ⚠️ 0, 💡 1. The trimmable runtime/device coverage and removal of brittle IL token assertions are good improvements.

simonrozsivaland others added 6 commits May 1, 2026 23:14
Keep the StartupHook linker descriptor active independently of the broad trimmable test discovery roots so linked Mono.Android.NET_Tests variants can still invoke StartupHook.Initialize().
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ensure duplicate Java-to-managed debug typemap entries use the selected managed template consistently, including the CoreCLR side table assembly and token metadata. Prefer Mono.Android for duplicate mappings so framework types such as java/lang/String surface as Java.Lang.String instead of test aliases.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the legacy RootAssembliesForTrimmableTestDiscovery escape hatch and its broad framework assembly roots. Keep only visible test assembly roots and narrow descriptors so CoreCLRTrimmable coverage does not mask trim-safety issues.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace DisableTransitiveProjectReferences with a targeted filter for the standalone external Java.Interop project output. This keeps transitive project references enabled while avoiding duplicate Java.Interop compile references in the Android test projects.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the custom _ValidateTrimmableTestRoots target from Mono.Android.NET-Tests. The project now relies on the explicit trimmer roots it declares without an extra local enforcement target.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Compile the GenericMarshaler helper directly into the Androidized Java.Interop test project so it binds against the platform Java.Interop assembly instead of pulling in the standalone external Java.Interop project.
Remove the temporary reference-filter targets from the runtime test projects now that the standalone project reference is gone.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@github-actions

github-actionsBot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — well-structured initialization fix

Summary: This PR correctly reorders trimmable typemap initialization to fix CoreCLR app startup. The changes are architecturally sound, well-tested, and consistent with existing patterns.

Key observations

Initialization ordering (core fix): Splitting TypeMapLoader.Initialize() (data loading, before runtime) from TrimmableTypeMap.RegisterNativeMethods() (JNI registration, after runtime) is the right architectural fix. The JniType("mono/android/Runtime"u8) switch is now safe because the runtime's ClassLoader is available at the point of native registration.

UCO forwarder try/catch: The emitted WaitForBridgeProcessing → try { callback } catch { UnhandledException } pattern correctly mirrors the legacy JNINativeWrapper.g.cs wrappers. Unconditional catch (vs. the legacy exception filter) is correct for [UnmanagedCallersOnly] on CoreCLR.

Debug typemap duplicate fix (TypeMappingDebugNativeAssemblyGeneratorCLR.cs): Good bug fix — the old code was using entry.ManagedName/entry.AssemblyName when it should have been using managedEntry.* after resolving duplicates.

Per-assembly anchors (RootTypeMapAssemblyGenerator.cs): The split into EmitSharedUniverseAssemblyTargetAttributes vs EmitPerAssemblyUniverseAssemblyTargetAttributes correctly ensures TypeMapAssemblyTargetAttribute<T>'s anchor T matches what TypeMapping.GetOrCreate*TypeMapping<T>() expects at runtime.

MSBuild target extraction (_PrepareTrimmableTypeMapAssemblies): Correctly fixes the incremental build item-group problem — _GenerateJavaStubs can be skipped, which would leave assembly items empty for downstream targets.

Issues by severity

SeverityCountDetails
💡 Suggestion1MSBuild: use ->Count() instead of != '' for item empty checks (line 68, 76 in CoreCLR.targets)

CI Status

  • license/cla: ✅ passed
  • dotnet-android: ⚠️action_required (may need approval/re-trigger)
  • Xamarin.Android-PR: not yet visible

Note: PR has merge conflicts (mergeable_state: dirty) — will need a rebase before merge.

Generated by Android PR Reviewer for issue #11252 · ● 15.3M

simonrozsivaland others added 2 commits May 2, 2026 20:12
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival added the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label May 4, 2026

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one minor comment.

Comment on lines -86 to +80
<TrimmerRootAssembly Include="StartupHook" RootMode="All" />
<TrimmerRootAssembly Include="Mono.Android.NET-Tests" RootMode="Visible" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootAssembly Include="Java.Interop-Tests" RootMode="Visible" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootDescriptor Include="TrimmerRoots.xml" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootDescriptor Include="StartupHookRoots.xml" Condition=" '$(StartupHookSupport)' == 'true' " />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be:

<TrimmerRootAssemblyInclude="StartupHook"RootMode="All"Condition=" '$(StartupHookSupport)' == 'true' " />

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this can be simplified. I will merge this PR now since it's green and open a follow-up PR.

@simonrozsival
simonrozsival merged commit 9a3fded into mainMay 4, 2026
3 checks passed
@simonrozsival
simonrozsival deleted the trimmable-typemap-startup-fixes branch May 4, 2026 15:17
jonathanpeppers pushed a commit that referenced this pull request May 26, 2026
Trim our trimmable-typemap test name exclusions down to just InvokeVirtualFromConstructorTests (the only one main keeps). All the JavaProxy* / JniPeerMembers / generic-handling exclusions were added during dogfooding before the trimmable typemap fixes (#11123, #11270-#11275, #11252, etc.) landed on main; they should pass now. If any still fail, CI will surface them and we can re-add individually.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 4, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot`copilot-cli` or other AIs were used to author thisready-to-reviewThis PR is ready to review/merge, I think any CI failures are just flaky (ignorable).trimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simonrozsival@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

[TrimmableTypeMap] Fix app initialization and startup - #11252

Merged
simonrozsival merged 29 commits into
mainfrom
trimmable-typemap-startup-fixes
May 4, 2026
Merged

[TrimmableTypeMap] Fix app initialization and startup#11252
simonrozsival merged 29 commits into
mainfrom
trimmable-typemap-startup-fixes

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented Apr 30, 2026

Copy link
Copy Markdown
Member

Summary

Fixes CoreCLR app startup with _AndroidTypeMapImplementation=trimmable while keeping the trimmable typemap path trim-safe and avoiding broad test roots.

  • initialize and register trimmable typemap data without broad assembly rooting
  • use the UTF-8 JniType overload for mono/android/Runtime
  • preserve trimmable activation/proxy lookup without delegate-registration or reflection-activation fallbacks
  • emit pregenerated UCO native registration with JniNativeMethod rows and direct ldftn function pointers
  • package trimmable typemap assemblies for each CoreCLR ABI
  • avoid duplicate debug typemap entries
  • keep CoreCLRTrimmable runtime-test discovery working with narrow roots
  • avoid pulling standalone external Java.Interop into Androidized runtime tests
  • keep generator tests focused on metadata/exception-region shape instead of brittle emitted-IL call-token byte patterns

Follow-up PR for the non-trivial generated IL maxstack work: #11260.

Details

Runtime initialization

The runtime initialization path now uses the UTF-8 JniType constructor for mono/android/Runtime and registers trimmable typemap data early enough for CoreCLR startup.

Trimmable typemap runtime behavior

The trimmable typemap runtime path preserves activation and proxy lookup for registered peer types without falling back to reflection activation or delegate registration. The scanner also avoids treating JNI primitive keyword signatures as normal peer mappings.

TypeMap generation and packaging

The typemap generator now emits the raw metadata needed by the runtime path, including pregenerated UCO native registration data. CoreCLR trimmable packaging batches typemap assemblies per ABI so generated typemap DLLs are included for each target ABI.

Test roots and Java.Interop references

The CoreCLRTrimmable device-test project uses narrow visible roots plus explicit startup-hook roots instead of broad default RootMode=All roots. The Androidized Java.Interop test project compiles the GenericMarshaler helper directly so it binds against Android's platform Java.Interop, avoiding the standalone external Java.Interop project/reference in runtime tests.

Test exclusions and coverage

Runtime behavior is covered by the CoreCLRTrimmable device-test lane, including TrimmableTypeMapTypeManagerTests. Generator tests continue to validate metadata shape and exception-region structure without depending on exact emitted IL call-token byte sequences.

Validation

Note: do not pass -p:ExcludeCategories=... for the CoreCLRTrimmable RunTestApp command below. ExcludeCategories is appended inside Mono.Android.NET-Tests.csproj; setting it as a global property prevents the project from adding trimmable-specific exclusions such as NativeTypeMap:Export.

  • MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -c Release -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -nr:false -tl:off -v:minimal
    • passed
  • dotnet test tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests.csproj -v minimal
    • 430 passed, 0 failed
  • ./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter "FullyQualifiedName~TrimmableTypeMapBuildTests"
    • 5 passed, 0 failed
  • ANDROID_SERIAL=R58Y30HZ65V MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -t:RunTestApp -c Release -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -nr:false -tl:off -v:minimal
    • 887 total, 0 errors, 0 failures, 51 ignored
  • Clean local .NET MAUI app run:
    • Installed local MAUI Android/Tizen workload records with manifest updates disabled so the repo-local SDK can build UseMaui projects.
    • Removed bin/ and obj/ from /Users/simonrozsival/Projects/dotnet/playground/TestBlankMauiP5.
    • ANDROID_SERIAL=R58Y30HZ65V ./dotnet-local.sh build /Users/simonrozsival/Projects/dotnet/playground/TestBlankMauiP5/TestBlankMauiP5.csproj -t:Run -f net11.0-android -c Release -p:TargetFrameworks=net11.0-android -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -p:AndroidPackageFormat=apk -p:RestoreConfigFile=/Users/simonrozsival/Projects/dotnet/android/NuGet.config -nr:false -tl:off -v:minimal
    • passed; com.companyname.testblankmauip5/crc64f2a221357d608c26.MainActivity was installed and focused in the foreground on R58Y30HZ65V.

simonrozsivaland others added 3 commits April 30, 2026 10:28
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>
@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Apr 30, 2026
CopilotAI review requested due to automatic review settings April 30, 2026 08:33
@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Apr 30, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 register mono.android.Runtime.registerNatives(Class) after JniRuntime.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=false peers 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
FileDescription
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.csAdds a Java.Interop-style activation ctor to support activation-ctor scanning scenarios in tests.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.csAdds coverage ensuring GenerateJavaPeer=false peers do not inherit activation ctors.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.csEnsures non-generated peers without activation ctors produce no proxy types/associations.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.csAdds tests validating per-assembly vs shared anchor behavior by decoding attribute/type spec metadata.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.csSplits native registration from initialization and adds separate caches for target-type and proxy lookup.
src/Mono.Android/Microsoft.Android.Runtime/SingleUniverseTypeMap.csSplits target-type enumeration from proxy-type enumeration and centralizes alias entry traversal.
src/Mono.Android/Microsoft.Android.Runtime/ITypeMapWithAliasing.csUpdates interface to expose separate target/proxy enumeration methods.
src/Mono.Android/Microsoft.Android.Runtime/AggregateTypeMap.csImplements new interface shape across multiple universes.
src/Mono.Android/Android.Runtime/JNIEnvInit.csAdjusts initialization ordering and registers typemap native bridge after JniRuntime.Current exists.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.csSuppresses inherited activation ctor discovery for IsFromJniTypeSignature && DoNotGenerateAcw.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.csEmits TypeMapAssemblyTargetAttribute<T> using per-assembly anchors in aggregate mode.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.csExtracts proxy-creation predicate to keep direct typemap entries for non-generated peers.

@simonrozsivalsimonrozsival changed the title Fix trimmable typemap startup[TrimmableTypeMap] Fix app initialization and startupApr 30, 2026
simonrozsivaland others added 16 commits April 30, 2026 11:08
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>
simonrozsivaland others added 2 commits May 1, 2026 13:19
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>

@simonrozsivalsimonrozsival left a comment

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Needs Changes pending CI. I didn't find any blocking code correctness issues in the final diff, but the internal Xamarin.Android-PR check is still in progress, so this isn't merge-ready yet.

Issue counts: ❌ 0, ⚠️ 0, 💡 1. The trimmable runtime/device coverage and removal of brittle IL token assertions are good improvements.

simonrozsivaland others added 6 commits May 1, 2026 23:14
Keep the StartupHook linker descriptor active independently of the broad trimmable test discovery roots so linked Mono.Android.NET_Tests variants can still invoke StartupHook.Initialize().
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ensure duplicate Java-to-managed debug typemap entries use the selected managed template consistently, including the CoreCLR side table assembly and token metadata. Prefer Mono.Android for duplicate mappings so framework types such as java/lang/String surface as Java.Lang.String instead of test aliases.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the legacy RootAssembliesForTrimmableTestDiscovery escape hatch and its broad framework assembly roots. Keep only visible test assembly roots and narrow descriptors so CoreCLRTrimmable coverage does not mask trim-safety issues.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace DisableTransitiveProjectReferences with a targeted filter for the standalone external Java.Interop project output. This keeps transitive project references enabled while avoiding duplicate Java.Interop compile references in the Android test projects.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the custom _ValidateTrimmableTestRoots target from Mono.Android.NET-Tests. The project now relies on the explicit trimmer roots it declares without an extra local enforcement target.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Compile the GenericMarshaler helper directly into the Androidized Java.Interop test project so it binds against the platform Java.Interop assembly instead of pulling in the standalone external Java.Interop project.
Remove the temporary reference-filter targets from the runtime test projects now that the standalone project reference is gone.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@github-actions

github-actionsBot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — well-structured initialization fix

Summary: This PR correctly reorders trimmable typemap initialization to fix CoreCLR app startup. The changes are architecturally sound, well-tested, and consistent with existing patterns.

Key observations

Initialization ordering (core fix): Splitting TypeMapLoader.Initialize() (data loading, before runtime) from TrimmableTypeMap.RegisterNativeMethods() (JNI registration, after runtime) is the right architectural fix. The JniType("mono/android/Runtime"u8) switch is now safe because the runtime's ClassLoader is available at the point of native registration.

UCO forwarder try/catch: The emitted WaitForBridgeProcessing → try { callback } catch { UnhandledException } pattern correctly mirrors the legacy JNINativeWrapper.g.cs wrappers. Unconditional catch (vs. the legacy exception filter) is correct for [UnmanagedCallersOnly] on CoreCLR.

Debug typemap duplicate fix (TypeMappingDebugNativeAssemblyGeneratorCLR.cs): Good bug fix — the old code was using entry.ManagedName/entry.AssemblyName when it should have been using managedEntry.* after resolving duplicates.

Per-assembly anchors (RootTypeMapAssemblyGenerator.cs): The split into EmitSharedUniverseAssemblyTargetAttributes vs EmitPerAssemblyUniverseAssemblyTargetAttributes correctly ensures TypeMapAssemblyTargetAttribute<T>'s anchor T matches what TypeMapping.GetOrCreate*TypeMapping<T>() expects at runtime.

MSBuild target extraction (_PrepareTrimmableTypeMapAssemblies): Correctly fixes the incremental build item-group problem — _GenerateJavaStubs can be skipped, which would leave assembly items empty for downstream targets.

Issues by severity

SeverityCountDetails
💡 Suggestion1MSBuild: use ->Count() instead of != '' for item empty checks (line 68, 76 in CoreCLR.targets)

CI Status

  • license/cla: ✅ passed
  • dotnet-android: ⚠️action_required (may need approval/re-trigger)
  • Xamarin.Android-PR: not yet visible

Note: PR has merge conflicts (mergeable_state: dirty) — will need a rebase before merge.

Generated by Android PR Reviewer for issue #11252 · ● 15.3M

simonrozsivaland others added 2 commits May 2, 2026 20:12
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival added the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label May 4, 2026

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one minor comment.

Comment on lines -86 to +80
<TrimmerRootAssembly Include="StartupHook" RootMode="All" />
<TrimmerRootAssembly Include="Mono.Android.NET-Tests" RootMode="Visible" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootAssembly Include="Java.Interop-Tests" RootMode="Visible" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootDescriptor Include="TrimmerRoots.xml" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootDescriptor Include="StartupHookRoots.xml" Condition=" '$(StartupHookSupport)' == 'true' " />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be:

<TrimmerRootAssemblyInclude="StartupHook"RootMode="All"Condition=" '$(StartupHookSupport)' == 'true' " />

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this can be simplified. I will merge this PR now since it's green and open a follow-up PR.

@simonrozsival
simonrozsival merged commit 9a3fded into mainMay 4, 2026
3 checks passed
@simonrozsival
simonrozsival deleted the trimmable-typemap-startup-fixes branch May 4, 2026 15:17
jonathanpeppers pushed a commit that referenced this pull request May 26, 2026
Trim our trimmable-typemap test name exclusions down to just InvokeVirtualFromConstructorTests (the only one main keeps). All the JavaProxy* / JniPeerMembers / generic-handling exclusions were added during dogfooding before the trimmable typemap fixes (#11123, #11270-#11275, #11252, etc.) landed on main; they should pass now. If any still fail, CI will surface them and we can re-add individually.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 4, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot`copilot-cli` or other AIs were used to author thisready-to-reviewThis PR is ready to review/merge, I think any CI failures are just flaky (ignorable).trimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simonrozsival@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[TrimmableTypeMap] Fix app initialization and startup - #11252

Merged
simonrozsival merged 29 commits into
mainfrom
trimmable-typemap-startup-fixes
May 4, 2026
Merged

[TrimmableTypeMap] Fix app initialization and startup#11252
simonrozsival merged 29 commits into
mainfrom
trimmable-typemap-startup-fixes

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented Apr 30, 2026

Copy link
Copy Markdown
Member

Summary

Fixes CoreCLR app startup with _AndroidTypeMapImplementation=trimmable while keeping the trimmable typemap path trim-safe and avoiding broad test roots.

  • initialize and register trimmable typemap data without broad assembly rooting
  • use the UTF-8 JniType overload for mono/android/Runtime
  • preserve trimmable activation/proxy lookup without delegate-registration or reflection-activation fallbacks
  • emit pregenerated UCO native registration with JniNativeMethod rows and direct ldftn function pointers
  • package trimmable typemap assemblies for each CoreCLR ABI
  • avoid duplicate debug typemap entries
  • keep CoreCLRTrimmable runtime-test discovery working with narrow roots
  • avoid pulling standalone external Java.Interop into Androidized runtime tests
  • keep generator tests focused on metadata/exception-region shape instead of brittle emitted-IL call-token byte patterns

Follow-up PR for the non-trivial generated IL maxstack work: #11260.

Details

Runtime initialization

The runtime initialization path now uses the UTF-8 JniType constructor for mono/android/Runtime and registers trimmable typemap data early enough for CoreCLR startup.

Trimmable typemap runtime behavior

The trimmable typemap runtime path preserves activation and proxy lookup for registered peer types without falling back to reflection activation or delegate registration. The scanner also avoids treating JNI primitive keyword signatures as normal peer mappings.

TypeMap generation and packaging

The typemap generator now emits the raw metadata needed by the runtime path, including pregenerated UCO native registration data. CoreCLR trimmable packaging batches typemap assemblies per ABI so generated typemap DLLs are included for each target ABI.

Test roots and Java.Interop references

The CoreCLRTrimmable device-test project uses narrow visible roots plus explicit startup-hook roots instead of broad default RootMode=All roots. The Androidized Java.Interop test project compiles the GenericMarshaler helper directly so it binds against Android's platform Java.Interop, avoiding the standalone external Java.Interop project/reference in runtime tests.

Test exclusions and coverage

Runtime behavior is covered by the CoreCLRTrimmable device-test lane, including TrimmableTypeMapTypeManagerTests. Generator tests continue to validate metadata shape and exception-region structure without depending on exact emitted IL call-token byte sequences.

Validation

Note: do not pass -p:ExcludeCategories=... for the CoreCLRTrimmable RunTestApp command below. ExcludeCategories is appended inside Mono.Android.NET-Tests.csproj; setting it as a global property prevents the project from adding trimmable-specific exclusions such as NativeTypeMap:Export.

  • MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -c Release -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -nr:false -tl:off -v:minimal
    • passed
  • dotnet test tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests.csproj -v minimal
    • 430 passed, 0 failed
  • ./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter "FullyQualifiedName~TrimmableTypeMapBuildTests"
    • 5 passed, 0 failed
  • ANDROID_SERIAL=R58Y30HZ65V MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -t:RunTestApp -c Release -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -nr:false -tl:off -v:minimal
    • 887 total, 0 errors, 0 failures, 51 ignored
  • Clean local .NET MAUI app run:
    • Installed local MAUI Android/Tizen workload records with manifest updates disabled so the repo-local SDK can build UseMaui projects.
    • Removed bin/ and obj/ from /Users/simonrozsival/Projects/dotnet/playground/TestBlankMauiP5.
    • ANDROID_SERIAL=R58Y30HZ65V ./dotnet-local.sh build /Users/simonrozsival/Projects/dotnet/playground/TestBlankMauiP5/TestBlankMauiP5.csproj -t:Run -f net11.0-android -c Release -p:TargetFrameworks=net11.0-android -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -p:AndroidPackageFormat=apk -p:RestoreConfigFile=/Users/simonrozsival/Projects/dotnet/android/NuGet.config -nr:false -tl:off -v:minimal
    • passed; com.companyname.testblankmauip5/crc64f2a221357d608c26.MainActivity was installed and focused in the foreground on R58Y30HZ65V.

simonrozsivaland others added 3 commits April 30, 2026 10:28
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>
@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Apr 30, 2026
CopilotAI review requested due to automatic review settings April 30, 2026 08:33
@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Apr 30, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 register mono.android.Runtime.registerNatives(Class) after JniRuntime.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=false peers 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
FileDescription
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.csAdds a Java.Interop-style activation ctor to support activation-ctor scanning scenarios in tests.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.csAdds coverage ensuring GenerateJavaPeer=false peers do not inherit activation ctors.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.csEnsures non-generated peers without activation ctors produce no proxy types/associations.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.csAdds tests validating per-assembly vs shared anchor behavior by decoding attribute/type spec metadata.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.csSplits native registration from initialization and adds separate caches for target-type and proxy lookup.
src/Mono.Android/Microsoft.Android.Runtime/SingleUniverseTypeMap.csSplits target-type enumeration from proxy-type enumeration and centralizes alias entry traversal.
src/Mono.Android/Microsoft.Android.Runtime/ITypeMapWithAliasing.csUpdates interface to expose separate target/proxy enumeration methods.
src/Mono.Android/Microsoft.Android.Runtime/AggregateTypeMap.csImplements new interface shape across multiple universes.
src/Mono.Android/Android.Runtime/JNIEnvInit.csAdjusts initialization ordering and registers typemap native bridge after JniRuntime.Current exists.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.csSuppresses inherited activation ctor discovery for IsFromJniTypeSignature && DoNotGenerateAcw.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.csEmits TypeMapAssemblyTargetAttribute<T> using per-assembly anchors in aggregate mode.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.csExtracts proxy-creation predicate to keep direct typemap entries for non-generated peers.

@simonrozsivalsimonrozsival changed the title Fix trimmable typemap startup[TrimmableTypeMap] Fix app initialization and startupApr 30, 2026
simonrozsivaland others added 16 commits April 30, 2026 11:08
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>
simonrozsivaland others added 2 commits May 1, 2026 13:19
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>

@simonrozsivalsimonrozsival left a comment

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Needs Changes pending CI. I didn't find any blocking code correctness issues in the final diff, but the internal Xamarin.Android-PR check is still in progress, so this isn't merge-ready yet.

Issue counts: ❌ 0, ⚠️ 0, 💡 1. The trimmable runtime/device coverage and removal of brittle IL token assertions are good improvements.

simonrozsivaland others added 6 commits May 1, 2026 23:14
Keep the StartupHook linker descriptor active independently of the broad trimmable test discovery roots so linked Mono.Android.NET_Tests variants can still invoke StartupHook.Initialize().
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ensure duplicate Java-to-managed debug typemap entries use the selected managed template consistently, including the CoreCLR side table assembly and token metadata. Prefer Mono.Android for duplicate mappings so framework types such as java/lang/String surface as Java.Lang.String instead of test aliases.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the legacy RootAssembliesForTrimmableTestDiscovery escape hatch and its broad framework assembly roots. Keep only visible test assembly roots and narrow descriptors so CoreCLRTrimmable coverage does not mask trim-safety issues.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace DisableTransitiveProjectReferences with a targeted filter for the standalone external Java.Interop project output. This keeps transitive project references enabled while avoiding duplicate Java.Interop compile references in the Android test projects.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the custom _ValidateTrimmableTestRoots target from Mono.Android.NET-Tests. The project now relies on the explicit trimmer roots it declares without an extra local enforcement target.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Compile the GenericMarshaler helper directly into the Androidized Java.Interop test project so it binds against the platform Java.Interop assembly instead of pulling in the standalone external Java.Interop project.
Remove the temporary reference-filter targets from the runtime test projects now that the standalone project reference is gone.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@github-actions

github-actionsBot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — well-structured initialization fix

Summary: This PR correctly reorders trimmable typemap initialization to fix CoreCLR app startup. The changes are architecturally sound, well-tested, and consistent with existing patterns.

Key observations

Initialization ordering (core fix): Splitting TypeMapLoader.Initialize() (data loading, before runtime) from TrimmableTypeMap.RegisterNativeMethods() (JNI registration, after runtime) is the right architectural fix. The JniType("mono/android/Runtime"u8) switch is now safe because the runtime's ClassLoader is available at the point of native registration.

UCO forwarder try/catch: The emitted WaitForBridgeProcessing → try { callback } catch { UnhandledException } pattern correctly mirrors the legacy JNINativeWrapper.g.cs wrappers. Unconditional catch (vs. the legacy exception filter) is correct for [UnmanagedCallersOnly] on CoreCLR.

Debug typemap duplicate fix (TypeMappingDebugNativeAssemblyGeneratorCLR.cs): Good bug fix — the old code was using entry.ManagedName/entry.AssemblyName when it should have been using managedEntry.* after resolving duplicates.

Per-assembly anchors (RootTypeMapAssemblyGenerator.cs): The split into EmitSharedUniverseAssemblyTargetAttributes vs EmitPerAssemblyUniverseAssemblyTargetAttributes correctly ensures TypeMapAssemblyTargetAttribute<T>'s anchor T matches what TypeMapping.GetOrCreate*TypeMapping<T>() expects at runtime.

MSBuild target extraction (_PrepareTrimmableTypeMapAssemblies): Correctly fixes the incremental build item-group problem — _GenerateJavaStubs can be skipped, which would leave assembly items empty for downstream targets.

Issues by severity

SeverityCountDetails
💡 Suggestion1MSBuild: use ->Count() instead of != '' for item empty checks (line 68, 76 in CoreCLR.targets)

CI Status

  • license/cla: ✅ passed
  • dotnet-android: ⚠️action_required (may need approval/re-trigger)
  • Xamarin.Android-PR: not yet visible

Note: PR has merge conflicts (mergeable_state: dirty) — will need a rebase before merge.

Generated by Android PR Reviewer for issue #11252 · ● 15.3M

simonrozsivaland others added 2 commits May 2, 2026 20:12
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival added the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label May 4, 2026

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one minor comment.

Comment on lines -86 to +80
<TrimmerRootAssembly Include="StartupHook" RootMode="All" />
<TrimmerRootAssembly Include="Mono.Android.NET-Tests" RootMode="Visible" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootAssembly Include="Java.Interop-Tests" RootMode="Visible" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootDescriptor Include="TrimmerRoots.xml" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootDescriptor Include="StartupHookRoots.xml" Condition=" '$(StartupHookSupport)' == 'true' " />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be:

<TrimmerRootAssemblyInclude="StartupHook"RootMode="All"Condition=" '$(StartupHookSupport)' == 'true' " />

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this can be simplified. I will merge this PR now since it's green and open a follow-up PR.

@simonrozsival
simonrozsival merged commit 9a3fded into mainMay 4, 2026
3 checks passed
@simonrozsival
simonrozsival deleted the trimmable-typemap-startup-fixes branch May 4, 2026 15:17
jonathanpeppers pushed a commit that referenced this pull request May 26, 2026
Trim our trimmable-typemap test name exclusions down to just InvokeVirtualFromConstructorTests (the only one main keeps). All the JavaProxy* / JniPeerMembers / generic-handling exclusions were added during dogfooding before the trimmable typemap fixes (#11123, #11270-#11275, #11252, etc.) landed on main; they should pass now. If any still fail, CI will surface them and we can re-add individually.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 4, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot`copilot-cli` or other AIs were used to author thisready-to-reviewThis PR is ready to review/merge, I think any CI failures are just flaky (ignorable).trimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simonrozsival@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[TrimmableTypeMap] Fix app initialization and startup - #11252

Merged
simonrozsival merged 29 commits into
mainfrom
trimmable-typemap-startup-fixes
May 4, 2026
Merged

[TrimmableTypeMap] Fix app initialization and startup#11252
simonrozsival merged 29 commits into
mainfrom
trimmable-typemap-startup-fixes

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented Apr 30, 2026

Copy link
Copy Markdown
Member

Summary

Fixes CoreCLR app startup with _AndroidTypeMapImplementation=trimmable while keeping the trimmable typemap path trim-safe and avoiding broad test roots.

  • initialize and register trimmable typemap data without broad assembly rooting
  • use the UTF-8 JniType overload for mono/android/Runtime
  • preserve trimmable activation/proxy lookup without delegate-registration or reflection-activation fallbacks
  • emit pregenerated UCO native registration with JniNativeMethod rows and direct ldftn function pointers
  • package trimmable typemap assemblies for each CoreCLR ABI
  • avoid duplicate debug typemap entries
  • keep CoreCLRTrimmable runtime-test discovery working with narrow roots
  • avoid pulling standalone external Java.Interop into Androidized runtime tests
  • keep generator tests focused on metadata/exception-region shape instead of brittle emitted-IL call-token byte patterns

Follow-up PR for the non-trivial generated IL maxstack work: #11260.

Details

Runtime initialization

The runtime initialization path now uses the UTF-8 JniType constructor for mono/android/Runtime and registers trimmable typemap data early enough for CoreCLR startup.

Trimmable typemap runtime behavior

The trimmable typemap runtime path preserves activation and proxy lookup for registered peer types without falling back to reflection activation or delegate registration. The scanner also avoids treating JNI primitive keyword signatures as normal peer mappings.

TypeMap generation and packaging

The typemap generator now emits the raw metadata needed by the runtime path, including pregenerated UCO native registration data. CoreCLR trimmable packaging batches typemap assemblies per ABI so generated typemap DLLs are included for each target ABI.

Test roots and Java.Interop references

The CoreCLRTrimmable device-test project uses narrow visible roots plus explicit startup-hook roots instead of broad default RootMode=All roots. The Androidized Java.Interop test project compiles the GenericMarshaler helper directly so it binds against Android's platform Java.Interop, avoiding the standalone external Java.Interop project/reference in runtime tests.

Test exclusions and coverage

Runtime behavior is covered by the CoreCLRTrimmable device-test lane, including TrimmableTypeMapTypeManagerTests. Generator tests continue to validate metadata shape and exception-region structure without depending on exact emitted IL call-token byte sequences.

Validation

Note: do not pass -p:ExcludeCategories=... for the CoreCLRTrimmable RunTestApp command below. ExcludeCategories is appended inside Mono.Android.NET-Tests.csproj; setting it as a global property prevents the project from adding trimmable-specific exclusions such as NativeTypeMap:Export.

  • MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -c Release -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -nr:false -tl:off -v:minimal
    • passed
  • dotnet test tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests.csproj -v minimal
    • 430 passed, 0 failed
  • ./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter "FullyQualifiedName~TrimmableTypeMapBuildTests"
    • 5 passed, 0 failed
  • ANDROID_SERIAL=R58Y30HZ65V MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -t:RunTestApp -c Release -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -nr:false -tl:off -v:minimal
    • 887 total, 0 errors, 0 failures, 51 ignored
  • Clean local .NET MAUI app run:
    • Installed local MAUI Android/Tizen workload records with manifest updates disabled so the repo-local SDK can build UseMaui projects.
    • Removed bin/ and obj/ from /Users/simonrozsival/Projects/dotnet/playground/TestBlankMauiP5.
    • ANDROID_SERIAL=R58Y30HZ65V ./dotnet-local.sh build /Users/simonrozsival/Projects/dotnet/playground/TestBlankMauiP5/TestBlankMauiP5.csproj -t:Run -f net11.0-android -c Release -p:TargetFrameworks=net11.0-android -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -p:AndroidPackageFormat=apk -p:RestoreConfigFile=/Users/simonrozsival/Projects/dotnet/android/NuGet.config -nr:false -tl:off -v:minimal
    • passed; com.companyname.testblankmauip5/crc64f2a221357d608c26.MainActivity was installed and focused in the foreground on R58Y30HZ65V.

simonrozsivaland others added 3 commits April 30, 2026 10:28
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>
@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Apr 30, 2026
CopilotAI review requested due to automatic review settings April 30, 2026 08:33
@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Apr 30, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 register mono.android.Runtime.registerNatives(Class) after JniRuntime.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=false peers 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
FileDescription
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.csAdds a Java.Interop-style activation ctor to support activation-ctor scanning scenarios in tests.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.csAdds coverage ensuring GenerateJavaPeer=false peers do not inherit activation ctors.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.csEnsures non-generated peers without activation ctors produce no proxy types/associations.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.csAdds tests validating per-assembly vs shared anchor behavior by decoding attribute/type spec metadata.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.csSplits native registration from initialization and adds separate caches for target-type and proxy lookup.
src/Mono.Android/Microsoft.Android.Runtime/SingleUniverseTypeMap.csSplits target-type enumeration from proxy-type enumeration and centralizes alias entry traversal.
src/Mono.Android/Microsoft.Android.Runtime/ITypeMapWithAliasing.csUpdates interface to expose separate target/proxy enumeration methods.
src/Mono.Android/Microsoft.Android.Runtime/AggregateTypeMap.csImplements new interface shape across multiple universes.
src/Mono.Android/Android.Runtime/JNIEnvInit.csAdjusts initialization ordering and registers typemap native bridge after JniRuntime.Current exists.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.csSuppresses inherited activation ctor discovery for IsFromJniTypeSignature && DoNotGenerateAcw.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.csEmits TypeMapAssemblyTargetAttribute<T> using per-assembly anchors in aggregate mode.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.csExtracts proxy-creation predicate to keep direct typemap entries for non-generated peers.

@simonrozsivalsimonrozsival changed the title Fix trimmable typemap startup[TrimmableTypeMap] Fix app initialization and startupApr 30, 2026
simonrozsivaland others added 16 commits April 30, 2026 11:08
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>
simonrozsivaland others added 2 commits May 1, 2026 13:19
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>

@simonrozsivalsimonrozsival left a comment

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Needs Changes pending CI. I didn't find any blocking code correctness issues in the final diff, but the internal Xamarin.Android-PR check is still in progress, so this isn't merge-ready yet.

Issue counts: ❌ 0, ⚠️ 0, 💡 1. The trimmable runtime/device coverage and removal of brittle IL token assertions are good improvements.

simonrozsivaland others added 6 commits May 1, 2026 23:14
Keep the StartupHook linker descriptor active independently of the broad trimmable test discovery roots so linked Mono.Android.NET_Tests variants can still invoke StartupHook.Initialize().
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ensure duplicate Java-to-managed debug typemap entries use the selected managed template consistently, including the CoreCLR side table assembly and token metadata. Prefer Mono.Android for duplicate mappings so framework types such as java/lang/String surface as Java.Lang.String instead of test aliases.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the legacy RootAssembliesForTrimmableTestDiscovery escape hatch and its broad framework assembly roots. Keep only visible test assembly roots and narrow descriptors so CoreCLRTrimmable coverage does not mask trim-safety issues.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace DisableTransitiveProjectReferences with a targeted filter for the standalone external Java.Interop project output. This keeps transitive project references enabled while avoiding duplicate Java.Interop compile references in the Android test projects.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the custom _ValidateTrimmableTestRoots target from Mono.Android.NET-Tests. The project now relies on the explicit trimmer roots it declares without an extra local enforcement target.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Compile the GenericMarshaler helper directly into the Androidized Java.Interop test project so it binds against the platform Java.Interop assembly instead of pulling in the standalone external Java.Interop project.
Remove the temporary reference-filter targets from the runtime test projects now that the standalone project reference is gone.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@github-actions

github-actionsBot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — well-structured initialization fix

Summary: This PR correctly reorders trimmable typemap initialization to fix CoreCLR app startup. The changes are architecturally sound, well-tested, and consistent with existing patterns.

Key observations

Initialization ordering (core fix): Splitting TypeMapLoader.Initialize() (data loading, before runtime) from TrimmableTypeMap.RegisterNativeMethods() (JNI registration, after runtime) is the right architectural fix. The JniType("mono/android/Runtime"u8) switch is now safe because the runtime's ClassLoader is available at the point of native registration.

UCO forwarder try/catch: The emitted WaitForBridgeProcessing → try { callback } catch { UnhandledException } pattern correctly mirrors the legacy JNINativeWrapper.g.cs wrappers. Unconditional catch (vs. the legacy exception filter) is correct for [UnmanagedCallersOnly] on CoreCLR.

Debug typemap duplicate fix (TypeMappingDebugNativeAssemblyGeneratorCLR.cs): Good bug fix — the old code was using entry.ManagedName/entry.AssemblyName when it should have been using managedEntry.* after resolving duplicates.

Per-assembly anchors (RootTypeMapAssemblyGenerator.cs): The split into EmitSharedUniverseAssemblyTargetAttributes vs EmitPerAssemblyUniverseAssemblyTargetAttributes correctly ensures TypeMapAssemblyTargetAttribute<T>'s anchor T matches what TypeMapping.GetOrCreate*TypeMapping<T>() expects at runtime.

MSBuild target extraction (_PrepareTrimmableTypeMapAssemblies): Correctly fixes the incremental build item-group problem — _GenerateJavaStubs can be skipped, which would leave assembly items empty for downstream targets.

Issues by severity

SeverityCountDetails
💡 Suggestion1MSBuild: use ->Count() instead of != '' for item empty checks (line 68, 76 in CoreCLR.targets)

CI Status

  • license/cla: ✅ passed
  • dotnet-android: ⚠️action_required (may need approval/re-trigger)
  • Xamarin.Android-PR: not yet visible

Note: PR has merge conflicts (mergeable_state: dirty) — will need a rebase before merge.

Generated by Android PR Reviewer for issue #11252 · ● 15.3M

simonrozsivaland others added 2 commits May 2, 2026 20:12
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival added the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label May 4, 2026

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one minor comment.

Comment on lines -86 to +80
<TrimmerRootAssembly Include="StartupHook" RootMode="All" />
<TrimmerRootAssembly Include="Mono.Android.NET-Tests" RootMode="Visible" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootAssembly Include="Java.Interop-Tests" RootMode="Visible" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootDescriptor Include="TrimmerRoots.xml" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootDescriptor Include="StartupHookRoots.xml" Condition=" '$(StartupHookSupport)' == 'true' " />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be:

<TrimmerRootAssemblyInclude="StartupHook"RootMode="All"Condition=" '$(StartupHookSupport)' == 'true' " />

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this can be simplified. I will merge this PR now since it's green and open a follow-up PR.

@simonrozsival
simonrozsival merged commit 9a3fded into mainMay 4, 2026
3 checks passed
@simonrozsival
simonrozsival deleted the trimmable-typemap-startup-fixes branch May 4, 2026 15:17
jonathanpeppers pushed a commit that referenced this pull request May 26, 2026
Trim our trimmable-typemap test name exclusions down to just InvokeVirtualFromConstructorTests (the only one main keeps). All the JavaProxy* / JniPeerMembers / generic-handling exclusions were added during dogfooding before the trimmable typemap fixes (#11123, #11270-#11275, #11252, etc.) landed on main; they should pass now. If any still fail, CI will surface them and we can re-add individually.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 4, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot`copilot-cli` or other AIs were used to author thisready-to-reviewThis PR is ready to review/merge, I think any CI failures are just flaky (ignorable).trimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simonrozsival@jonathanpeppers
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

[TrimmableTypeMap] Fix app initialization and startup - #11252

Merged
simonrozsival merged 29 commits into
mainfrom
trimmable-typemap-startup-fixes
May 4, 2026
Merged

[TrimmableTypeMap] Fix app initialization and startup#11252
simonrozsival merged 29 commits into
mainfrom
trimmable-typemap-startup-fixes

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented Apr 30, 2026

Copy link
Copy Markdown
Member

Summary

Fixes CoreCLR app startup with _AndroidTypeMapImplementation=trimmable while keeping the trimmable typemap path trim-safe and avoiding broad test roots.

  • initialize and register trimmable typemap data without broad assembly rooting
  • use the UTF-8 JniType overload for mono/android/Runtime
  • preserve trimmable activation/proxy lookup without delegate-registration or reflection-activation fallbacks
  • emit pregenerated UCO native registration with JniNativeMethod rows and direct ldftn function pointers
  • package trimmable typemap assemblies for each CoreCLR ABI
  • avoid duplicate debug typemap entries
  • keep CoreCLRTrimmable runtime-test discovery working with narrow roots
  • avoid pulling standalone external Java.Interop into Androidized runtime tests
  • keep generator tests focused on metadata/exception-region shape instead of brittle emitted-IL call-token byte patterns

Follow-up PR for the non-trivial generated IL maxstack work: #11260.

Details

Runtime initialization

The runtime initialization path now uses the UTF-8 JniType constructor for mono/android/Runtime and registers trimmable typemap data early enough for CoreCLR startup.

Trimmable typemap runtime behavior

The trimmable typemap runtime path preserves activation and proxy lookup for registered peer types without falling back to reflection activation or delegate registration. The scanner also avoids treating JNI primitive keyword signatures as normal peer mappings.

TypeMap generation and packaging

The typemap generator now emits the raw metadata needed by the runtime path, including pregenerated UCO native registration data. CoreCLR trimmable packaging batches typemap assemblies per ABI so generated typemap DLLs are included for each target ABI.

Test roots and Java.Interop references

The CoreCLRTrimmable device-test project uses narrow visible roots plus explicit startup-hook roots instead of broad default RootMode=All roots. The Androidized Java.Interop test project compiles the GenericMarshaler helper directly so it binds against Android's platform Java.Interop, avoiding the standalone external Java.Interop project/reference in runtime tests.

Test exclusions and coverage

Runtime behavior is covered by the CoreCLRTrimmable device-test lane, including TrimmableTypeMapTypeManagerTests. Generator tests continue to validate metadata shape and exception-region structure without depending on exact emitted IL call-token byte sequences.

Validation

Note: do not pass -p:ExcludeCategories=... for the CoreCLRTrimmable RunTestApp command below. ExcludeCategories is appended inside Mono.Android.NET-Tests.csproj; setting it as a global property prevents the project from adding trimmable-specific exclusions such as NativeTypeMap:Export.

  • MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -c Release -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -nr:false -tl:off -v:minimal
    • passed
  • dotnet test tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests.csproj -v minimal
    • 430 passed, 0 failed
  • ./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter "FullyQualifiedName~TrimmableTypeMapBuildTests"
    • 5 passed, 0 failed
  • ANDROID_SERIAL=R58Y30HZ65V MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -t:RunTestApp -c Release -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -nr:false -tl:off -v:minimal
    • 887 total, 0 errors, 0 failures, 51 ignored
  • Clean local .NET MAUI app run:
    • Installed local MAUI Android/Tizen workload records with manifest updates disabled so the repo-local SDK can build UseMaui projects.
    • Removed bin/ and obj/ from /Users/simonrozsival/Projects/dotnet/playground/TestBlankMauiP5.
    • ANDROID_SERIAL=R58Y30HZ65V ./dotnet-local.sh build /Users/simonrozsival/Projects/dotnet/playground/TestBlankMauiP5/TestBlankMauiP5.csproj -t:Run -f net11.0-android -c Release -p:TargetFrameworks=net11.0-android -p:UseMonoRuntime=false -p:_AndroidTypeMapImplementation=trimmable -p:AndroidPackageFormat=apk -p:RestoreConfigFile=/Users/simonrozsival/Projects/dotnet/android/NuGet.config -nr:false -tl:off -v:minimal
    • passed; com.companyname.testblankmauip5/crc64f2a221357d608c26.MainActivity was installed and focused in the foreground on R58Y30HZ65V.

simonrozsivaland others added 3 commits April 30, 2026 10:28
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>
@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Apr 30, 2026
CopilotAI review requested due to automatic review settings April 30, 2026 08:33
@simonrozsivalsimonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Apr 30, 2026

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 register mono.android.Runtime.registerNatives(Class) after JniRuntime.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=false peers 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
FileDescription
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.csAdds a Java.Interop-style activation ctor to support activation-ctor scanning scenarios in tests.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.csAdds coverage ensuring GenerateJavaPeer=false peers do not inherit activation ctors.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.csEnsures non-generated peers without activation ctors produce no proxy types/associations.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.csAdds tests validating per-assembly vs shared anchor behavior by decoding attribute/type spec metadata.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.csSplits native registration from initialization and adds separate caches for target-type and proxy lookup.
src/Mono.Android/Microsoft.Android.Runtime/SingleUniverseTypeMap.csSplits target-type enumeration from proxy-type enumeration and centralizes alias entry traversal.
src/Mono.Android/Microsoft.Android.Runtime/ITypeMapWithAliasing.csUpdates interface to expose separate target/proxy enumeration methods.
src/Mono.Android/Microsoft.Android.Runtime/AggregateTypeMap.csImplements new interface shape across multiple universes.
src/Mono.Android/Android.Runtime/JNIEnvInit.csAdjusts initialization ordering and registers typemap native bridge after JniRuntime.Current exists.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.csSuppresses inherited activation ctor discovery for IsFromJniTypeSignature && DoNotGenerateAcw.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.csEmits TypeMapAssemblyTargetAttribute<T> using per-assembly anchors in aggregate mode.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.csExtracts proxy-creation predicate to keep direct typemap entries for non-generated peers.

@simonrozsivalsimonrozsival changed the title Fix trimmable typemap startup[TrimmableTypeMap] Fix app initialization and startupApr 30, 2026
simonrozsivaland others added 16 commits April 30, 2026 11:08
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>
simonrozsivaland others added 2 commits May 1, 2026 13:19
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>

@simonrozsivalsimonrozsival left a comment

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Needs Changes pending CI. I didn't find any blocking code correctness issues in the final diff, but the internal Xamarin.Android-PR check is still in progress, so this isn't merge-ready yet.

Issue counts: ❌ 0, ⚠️ 0, 💡 1. The trimmable runtime/device coverage and removal of brittle IL token assertions are good improvements.

simonrozsivaland others added 6 commits May 1, 2026 23:14
Keep the StartupHook linker descriptor active independently of the broad trimmable test discovery roots so linked Mono.Android.NET_Tests variants can still invoke StartupHook.Initialize().
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ensure duplicate Java-to-managed debug typemap entries use the selected managed template consistently, including the CoreCLR side table assembly and token metadata. Prefer Mono.Android for duplicate mappings so framework types such as java/lang/String surface as Java.Lang.String instead of test aliases.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the legacy RootAssembliesForTrimmableTestDiscovery escape hatch and its broad framework assembly roots. Keep only visible test assembly roots and narrow descriptors so CoreCLRTrimmable coverage does not mask trim-safety issues.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace DisableTransitiveProjectReferences with a targeted filter for the standalone external Java.Interop project output. This keeps transitive project references enabled while avoiding duplicate Java.Interop compile references in the Android test projects.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the custom _ValidateTrimmableTestRoots target from Mono.Android.NET-Tests. The project now relies on the explicit trimmer roots it declares without an extra local enforcement target.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Compile the GenericMarshaler helper directly into the Androidized Java.Interop test project so it binds against the platform Java.Interop assembly instead of pulling in the standalone external Java.Interop project.
Remove the temporary reference-filter targets from the runtime test projects now that the standalone project reference is gone.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@github-actions

github-actionsBot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — well-structured initialization fix

Summary: This PR correctly reorders trimmable typemap initialization to fix CoreCLR app startup. The changes are architecturally sound, well-tested, and consistent with existing patterns.

Key observations

Initialization ordering (core fix): Splitting TypeMapLoader.Initialize() (data loading, before runtime) from TrimmableTypeMap.RegisterNativeMethods() (JNI registration, after runtime) is the right architectural fix. The JniType("mono/android/Runtime"u8) switch is now safe because the runtime's ClassLoader is available at the point of native registration.

UCO forwarder try/catch: The emitted WaitForBridgeProcessing → try { callback } catch { UnhandledException } pattern correctly mirrors the legacy JNINativeWrapper.g.cs wrappers. Unconditional catch (vs. the legacy exception filter) is correct for [UnmanagedCallersOnly] on CoreCLR.

Debug typemap duplicate fix (TypeMappingDebugNativeAssemblyGeneratorCLR.cs): Good bug fix — the old code was using entry.ManagedName/entry.AssemblyName when it should have been using managedEntry.* after resolving duplicates.

Per-assembly anchors (RootTypeMapAssemblyGenerator.cs): The split into EmitSharedUniverseAssemblyTargetAttributes vs EmitPerAssemblyUniverseAssemblyTargetAttributes correctly ensures TypeMapAssemblyTargetAttribute<T>'s anchor T matches what TypeMapping.GetOrCreate*TypeMapping<T>() expects at runtime.

MSBuild target extraction (_PrepareTrimmableTypeMapAssemblies): Correctly fixes the incremental build item-group problem — _GenerateJavaStubs can be skipped, which would leave assembly items empty for downstream targets.

Issues by severity

SeverityCountDetails
💡 Suggestion1MSBuild: use ->Count() instead of != '' for item empty checks (line 68, 76 in CoreCLR.targets)

CI Status

  • license/cla: ✅ passed
  • dotnet-android: ⚠️action_required (may need approval/re-trigger)
  • Xamarin.Android-PR: not yet visible

Note: PR has merge conflicts (mergeable_state: dirty) — will need a rebase before merge.

Generated by Android PR Reviewer for issue #11252 · ● 15.3M

simonrozsivaland others added 2 commits May 2, 2026 20:12
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsivalsimonrozsival added the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label May 4, 2026

@jonathanpeppersjonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one minor comment.

Comment on lines -86 to +80
<TrimmerRootAssembly Include="StartupHook" RootMode="All" />
<TrimmerRootAssembly Include="Mono.Android.NET-Tests" RootMode="Visible" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootAssembly Include="Java.Interop-Tests" RootMode="Visible" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootDescriptor Include="TrimmerRoots.xml" Condition=" '$(_AndroidTypeMapImplementation)' == 'trimmable' " />
<TrimmerRootDescriptor Include="StartupHookRoots.xml" Condition=" '$(StartupHookSupport)' == 'true' " />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be:

<TrimmerRootAssemblyInclude="StartupHook"RootMode="All"Condition=" '$(StartupHookSupport)' == 'true' " />

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this can be simplified. I will merge this PR now since it's green and open a follow-up PR.

@simonrozsival
simonrozsival merged commit 9a3fded into mainMay 4, 2026
3 checks passed
@simonrozsival
simonrozsival deleted the trimmable-typemap-startup-fixes branch May 4, 2026 15:17
jonathanpeppers pushed a commit that referenced this pull request May 26, 2026
Trim our trimmable-typemap test name exclusions down to just InvokeVirtualFromConstructorTests (the only one main keeps). All the JavaProxy* / JniPeerMembers / generic-handling exclusions were added during dogfooding before the trimmable typemap fixes (#11123, #11270-#11275, #11252, etc.) landed on main; they should pass now. If any still fail, CI will surface them and we can re-add individually.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 4, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot`copilot-cli` or other AIs were used to author thisready-to-reviewThis PR is ready to review/merge, I think any CI failures are just flaky (ignorable).trimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simonrozsival@jonathanpeppers