Skip to content

[TrimmableTypeMap] Remove ManagedPeer from trimmable type map apps#11480

Open
simonrozsival wants to merge 10 commits into
mainfrom
dev/simonrozsival/managedpeer-trimmable
Open

[TrimmableTypeMap] Remove ManagedPeer from trimmable type map apps#11480
simonrozsival wants to merge 10 commits into
mainfrom
dev/simonrozsival/managedpeer-trimmable

Conversation

@simonrozsival
Copy link
Copy Markdown
Member

@simonrozsival simonrozsival commented May 25, 2026

Depends on dotnet/java-interop#1434
Depends on #11503

Disables Java.Interop ManagedPeer native registration for trimmable type map builds and removes the Java net.dot.jni.ManagedPeer class from java_runtime_trimmable.jar. Trimmable type map apps register native methods through generated JCW static initializers, so the legacy ManagedPeer registration path is dead weight there.

This removes the ManagedPeer startup cost for CoreCLR+trimmable apps. In a dotnet new maui Release CoreCLR+trimmable app on Samsung A16, the change improved am start -W TotalTime by about 18 ms median / 17 ms average across 20 cold starts versus a temporary baseline, with all starts successful.

Validation:

  • ./dotnet-local.sh build src/java-runtime/java-runtime.csproj -v:minimal -nr:false
  • ./dotnet-local.sh build src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.csproj -v:minimal -nr:false
  • MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh test bin/TestDebug/net10.0/Xamarin.Android.Build.Tests.dll --filter "Name=TrimmableTypeMap_RuntimeArtifacts_ArePackagedInSdk|Name=Build_WithExportAndExportField_GeneratesJcwAndTypeMap" -v:minimal
  • Rebuilt/installed/launched Release CoreCLR+trimmable MAUI app on Samsung R58Y30HZ65V; launch succeeded with no fatal/crash logcat rows.
  • Confirmed final trimmable runtime jar/dex and APK dex strings do not contain Lnet/dot/jni/ManagedPeer;.

References #10795

Disable Java.Interop ManagedPeer native registration for trimmable type map builds and remove the Java ManagedPeer class from the trimmable runtime jar. The trimmable path registers native methods through generated JCW static initializers, so ManagedPeer is dead weight there.

Add coverage to ensure trimmable runtime artifacts, generated Java, and built APK dex files do not reference ManagedPeer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 25, 2026 09:24
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 removes the legacy Java.Interop ManagedPeer native-registration path from trimmable type map builds, where native methods are registered via generated JCW static initializers, reducing unnecessary startup work for CoreCLR+trimmable apps.

Changes:

  • Add runtimeconfig host option to disable ManagedPeer native registration for trimmable typemap builds.
  • Exclude net/dot/jni/ManagedPeer from java_runtime_trimmable.jar generation.
  • Extend trimmable typemap build tests to assert the jar/dex/apk outputs contain no ManagedPeer class/descriptor references.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs Adds assertions ensuring trimmable runtime artifacts and generated outputs don’t reference ManagedPeer (jar entry, dex string, generated Java, APK dex).
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets Writes a runtimeconfig host option disabling ManagedPeer native registration for trimmable typemap builds.
src/java-runtime/java-runtime.targets Removes ManagedPeer.java from the trimmable runtime jar source set.

simonrozsival and others added 7 commits May 25, 2026 12:08
Remove the Java.Interop runtimeconfig switch from the trimmable typemap targets. Android now disables the legacy ManagedPeer native registration path through JniRuntime.CreationOptions when the existing trimmable typemap runtime feature is enabled.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use Span.IndexOf for the test dex byte search and update the Java.Interop submodule with the ManagedPeer runtime-option review fixes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bring in the Java.Interop regression test that documents ManagedPeer is intentionally absent from the built-in simple-reference map.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Set Java.Interop.RuntimeFeature.ManagedPeerNativeRegistration=false for the trimmable typemap path and update the Java.Interop submodule to keep legacy ManagedPeer behavior enabled by default elsewhere.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bring in the Java.Interop coverage that verifies Java.Interop.RuntimeFeature.ManagedPeerNativeRegistration=false skips ManagedPeer.Init().

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update the Java.Interop dependency to remove test-only ManagedPeer production instrumentation while keeping the ManagedPeer runtime switch coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival changed the title Remove ManagedPeer from trimmable type map apps [TrimmableTypeMap] Remove ManagedPeer from trimmable type map apps May 25, 2026
@simonrozsival simonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels May 25, 2026
@simonrozsival simonrozsival marked this pull request as draft May 25, 2026 14:38
@simonrozsival
Copy link
Copy Markdown
Member Author

@copilot resolve the merge conflicts in this pull request

Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented May 28, 2026

@copilot resolve the merge conflicts in this pull request

Conflicts are resolved in commit 51229e7c4 (merged origin/main into this branch, resolved TrimmableTypeMapBuildTests.cs, and updated external/Java.Interop to the merged commit).

@simonrozsival simonrozsival marked this pull request as ready for review May 28, 2026 07:17
@simonrozsival
Copy link
Copy Markdown
Member Author

/review

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 28, 2026

Android PR Reviewer completed successfully!

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

✅ LGTM — Clean, focused change

Summary: Disables ManagedPeer native registration for trimmable type map builds via the Java.Interop.RuntimeFeature.ManagedPeerNativeRegistration runtime host config option, and excludes ManagedPeer.java from java_runtime_trimmable.jar/.dex. Test coverage is thorough — SDK artifact verification, generated JCW source checks, and APK dex binary scanning.

Issues found:

Severity Count
⚠️ Warning 1
💡 Suggestion 1

Positive callouts:

  • Good binary-level verification (ContainsAscii on dex files) — catches cases where the type descriptor could survive even if the .class is removed.
  • The AssertApkDexDoesNotContain helper is well-factored and reusable.
  • Removing the break from the JCW loop to assert all files don't reference ManagedPeer is the right call.
  • The 18ms startup improvement with benchmark data in the PR description is appreciated.

Note: This PR depends on dotnet/java-interop#1434 and #11503. Mergeability is blocked on those dependencies.

Generated by Android PR Reviewer for issue #11480 · ● 6.9M


var javaFiles = Directory.GetFiles (javaDir, "*.java", SearchOption.AllDirectories);
Assert.IsNotEmpty (javaFiles, "At least one trimmable JCW Java source file should be generated.");
Assert.IsNotEmpty (javaFiles, "At least one trimmable JCW Java source file should be generated.");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 ⚠️ Code organization — This line is a duplicate of line 550 — same assertion message and expression. Looks like an accidental copy-paste. Remove one of the two.

Rule: Remove unused code (Postmortem #58)

Comment on lines 358 to +360
foreach (var f in allJavaFiles) {
var text = File.ReadAllText (f);
if (text.Contains ("EchoString") && text.Contains ("InitialFoo")) {
StringAssert.DoesNotContain ("net.dot.jni.ManagedPeer", text,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 💡 TestingStringAssert.DoesNotContain will fail and abort the loop on the first file that contains the string, so you won't see failures from subsequent files. If the goal is to check all generated Java files (which seems to be why the break was removed), consider collecting violations first and asserting once at the end, e.g. with Assert.Multiple or by accumulating failures into a list. Current behavior is fine for catching the issue but may make debugging harder if multiple files are affected.

Rule: Test assertions must be specific

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

copilot `copilot-cli` or other AIs were used to author this trimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants