[tests] Root StartupHook test assembly directly - #11280

Merged
jonathanpeppers merged 1 commit into
mainfrom
trimmable-typemap-startup-hook-root-assembly
May 4, 2026
Merged

[tests] Root StartupHook test assembly directly#11280
jonathanpeppers merged 1 commit into
mainfrom
trimmable-typemap-startup-hook-root-assembly

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented May 4, 2026

Copy link
Copy Markdown
Member

Stacked on #11252 via temporary base branch pr-11252-startup-fixes-base.

Follow-up to #11252 (comment).

Replaces the dedicated StartupHookRoots.xml descriptor with a conditional TrimmerRootAssembly item for StartupHook. Once #11252 lands, this PR should be retargeted to main and should still contain only the StartupHook root simplification.

Validation:

MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -c Release -p:_AndroidTypeMapImplementation=trimmable -p:UseMonoRuntime=false -nr:false

CopilotAI review requested due to automatic review settings May 4, 2026 15:24

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 tightens and simplifies trimming roots for the on-device runtime test app (notably rooting the StartupHook test assembly directly), while also extending the trimmable typemap/runtime/test coverage and improving typemap build/packaging plumbing for CoreCLR.

Changes:

  • Simplify CoreCLRTrimmable test rooting: use TrimmerRootAssembly for StartupHook and switch test assembly roots to RootMode="Visible" under the trimmable typemap path.
  • Improve trimmable typemap runtime behavior and tests (e.g., cached simple-reference lookup; additional disposal/finalization scenarios; scanner edge cases).
  • Adjust typemap generation/emission and MSBuild targets to better support incremental builds and CoreCLR per-ABI packaging, plus fix typemap debug duplicate handling.

Reviewed changes

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

Show a summary per file
FileDescription
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.csTweaks excluded test list for CoreCLRTrimmable runtime runs.
tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csprojUpdates category exclusions and replaces/adjusts trimmer roots (including StartupHook).
tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/TrimmableTypeMapTypeManagerTests.csRefactors/extends trimmable typemap runtime tests (incl. disposal/finalization coverage).
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop-Tests.NET.csprojCompiles a Java.Interop GenericMarshaler helper source directly instead of referencing the external project.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.csAdds fixture types for nested invoker edge-case coverage.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.EdgeCases.csAdds regression test for connector nested invoker naming.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.csAdds content-provider authorities test and minor comment cleanup.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.csUpdates expectations around invoker associations in the model.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.csExpands metadata-shape tests for UCO wrappers/ctors and activation ctor referencing.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TrimmableTypeMapGeneratorTests.csUpdates cross-assembly alias merge tests to match current ownership rules.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.csAdds validation for merged vs aggregate anchoring behavior in target attributes.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/FixtureTestBase.csRemoves IL-token scanning helper no longer needed by generator tests.
src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.csFixes Java→managed debug mapping emission to use the correct duplicate template entry.
src/Xamarin.Android.Build.Tasks/Utilities/TypeMapCecilAdapter.csPrefers Mono.Android as the template for debug duplicates when present.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targetsMoves typemap assembly item population into a dedicated pre-target for incremental correctness.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.CoreCLR.targetsRefactors CoreCLR typemap-to-store addition to batch across ABIs and depend on the new prepare target.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.csAdds cached/simple-reference resolution that falls back to base-type chain when needed.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.csSplits native registration into an explicit post-runtime-init step; adds single-run guard.
src/Mono.Android/Microsoft.Android.Runtime/ITypeMapWithAliasing.csClarifies interface doc comment around returned types.
src/Mono.Android/Android.Runtime/JNIEnvInit.csInitializes typemap data earlier and registers typemap natives after JniRuntime.SetCurrent.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.csNormalizes connector-managed type names consistently (nested type separator handling).
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/AssemblyIndex.csCaptures ContentProvider authorities (including ctor array form) into component metadata.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.csUpdates emitted UCO wrapper bodies and references internal runtime helpers for exception bridging behavior.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.csEmits target attributes differently for merged vs per-assembly universes (anchor selection).
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/PEAssemblyBuilder.csIncreases default maxstack for emitted bodies with locals to avoid verifier issues.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.csEnsures invoker types get managed→proxy associations where needed; minor string helper update.

Comment on lines +85 to +103
internal static unsafe void RegisterNativeMethods ()
{
// Use the `string` overload of `JniType` deliberately. Its underlying
// `JniEnvironment.Types.TryFindClass(string, bool)` tries raw JNI `FindClass`
// first and, if that fails, falls back to `Class.forName(name, true, info.Runtime.ClassLoader)`,
// which resolves via the runtime's app ClassLoader — the same one that loads
// `mono.android.Runtime` from the APK.
// The `ReadOnlySpan<byte>` overload (see external/Java.Interop/src/Java.Interop/Java.Interop/JniEnvironment.Types.cs)
// only calls raw JNI `FindClass`, which resolves via the system ClassLoader on
// Android and returns a different `Class` instance from the one JCWs reference.
// Registering natives on that other instance is silently wrong.
using var runtimeClass = new JniType ("mono/android/Runtime");
fixed (byte* name = "registerNatives"u8, sig = "(Ljava/lang/Class;)V"u8) {
var onRegisterNatives = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr, void>)&OnRegisterNatives;
var method = new JniNativeMethod (name, sig, onRegisterNatives);
JniEnvironment.Types.RegisterNatives (runtimeClass.PeerReference, [method]);
lock (s_initLock) {
if (s_nativeMethodsRegistered) {
throw new InvalidOperationException ("TrimmableTypeMap native methods have already been registered.");
}

if (s_instance is null) {
throw new InvalidOperationException (
"TrimmableTypeMap has not been initialized. Ensure RuntimeFeature.TrimmableTypeMap is enabled and the JNI runtime is initialized.");
}

using var runtimeClass = new JniType ("mono/android/Runtime"u8);
fixed (byte* name = "registerNatives"u8, sig = "(Ljava/lang/Class;)V"u8) {
var onRegisterNatives = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr, void>)&OnRegisterNatives;
var method = new JniNativeMethod (name, sig, onRegisterNatives);
JniEnvironment.Types.RegisterNatives (runtimeClass.PeerReference, [method]);
}
s_nativeMethodsRegistered = true;
@simonrozsival
simonrozsival changed the base branch from main to pr-11252-startup-fixes-baseMay 4, 2026 15:38
@simonrozsival
simonrozsivalforce-pushed the trimmable-typemap-startup-hook-root-assembly branch from e8a15d3 to 04969c5CompareMay 4, 2026 15:41
@simonrozsival
simonrozsival changed the base branch from pr-11252-startup-fixes-base to mainMay 4, 2026 17:44
Replace the dedicated StartupHookRoots.xml descriptor with a conditional TrimmerRootAssembly item, matching the rest of the test project roots and avoiding a single-purpose descriptor file.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsivalforce-pushed the trimmable-typemap-startup-hook-root-assembly branch from 5dc9e05 to f8749eaCompareMay 4, 2026 18:31
@jonathanpeppers
jonathanpeppers merged commit baa5bff into mainMay 4, 2026
3 checks passed
@jonathanpeppers
jonathanpeppers deleted the trimmable-typemap-startup-hook-root-assembly branch May 4, 2026 20:49
@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

None yet

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 \u003cpre\u003e\u003ccode\u003e 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

[tests] Root StartupHook test assembly directly - #11280

Merged
jonathanpeppers merged 1 commit into
mainfrom
trimmable-typemap-startup-hook-root-assembly
May 4, 2026
Merged

[tests] Root StartupHook test assembly directly#11280
jonathanpeppers merged 1 commit into
mainfrom
trimmable-typemap-startup-hook-root-assembly

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented May 4, 2026

Copy link
Copy Markdown
Member

Stacked on #11252 via temporary base branch pr-11252-startup-fixes-base.

Follow-up to #11252 (comment).

Replaces the dedicated StartupHookRoots.xml descriptor with a conditional TrimmerRootAssembly item for StartupHook. Once #11252 lands, this PR should be retargeted to main and should still contain only the StartupHook root simplification.

Validation:

MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -c Release -p:_AndroidTypeMapImplementation=trimmable -p:UseMonoRuntime=false -nr:false

CopilotAI review requested due to automatic review settings May 4, 2026 15:24

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 tightens and simplifies trimming roots for the on-device runtime test app (notably rooting the StartupHook test assembly directly), while also extending the trimmable typemap/runtime/test coverage and improving typemap build/packaging plumbing for CoreCLR.

Changes:

  • Simplify CoreCLRTrimmable test rooting: use TrimmerRootAssembly for StartupHook and switch test assembly roots to RootMode="Visible" under the trimmable typemap path.
  • Improve trimmable typemap runtime behavior and tests (e.g., cached simple-reference lookup; additional disposal/finalization scenarios; scanner edge cases).
  • Adjust typemap generation/emission and MSBuild targets to better support incremental builds and CoreCLR per-ABI packaging, plus fix typemap debug duplicate handling.

Reviewed changes

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

Show a summary per file
FileDescription
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.csTweaks excluded test list for CoreCLRTrimmable runtime runs.
tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csprojUpdates category exclusions and replaces/adjusts trimmer roots (including StartupHook).
tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/TrimmableTypeMapTypeManagerTests.csRefactors/extends trimmable typemap runtime tests (incl. disposal/finalization coverage).
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop-Tests.NET.csprojCompiles a Java.Interop GenericMarshaler helper source directly instead of referencing the external project.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.csAdds fixture types for nested invoker edge-case coverage.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.EdgeCases.csAdds regression test for connector nested invoker naming.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.csAdds content-provider authorities test and minor comment cleanup.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.csUpdates expectations around invoker associations in the model.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.csExpands metadata-shape tests for UCO wrappers/ctors and activation ctor referencing.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TrimmableTypeMapGeneratorTests.csUpdates cross-assembly alias merge tests to match current ownership rules.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.csAdds validation for merged vs aggregate anchoring behavior in target attributes.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/FixtureTestBase.csRemoves IL-token scanning helper no longer needed by generator tests.
src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.csFixes Java→managed debug mapping emission to use the correct duplicate template entry.
src/Xamarin.Android.Build.Tasks/Utilities/TypeMapCecilAdapter.csPrefers Mono.Android as the template for debug duplicates when present.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targetsMoves typemap assembly item population into a dedicated pre-target for incremental correctness.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.CoreCLR.targetsRefactors CoreCLR typemap-to-store addition to batch across ABIs and depend on the new prepare target.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.csAdds cached/simple-reference resolution that falls back to base-type chain when needed.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.csSplits native registration into an explicit post-runtime-init step; adds single-run guard.
src/Mono.Android/Microsoft.Android.Runtime/ITypeMapWithAliasing.csClarifies interface doc comment around returned types.
src/Mono.Android/Android.Runtime/JNIEnvInit.csInitializes typemap data earlier and registers typemap natives after JniRuntime.SetCurrent.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.csNormalizes connector-managed type names consistently (nested type separator handling).
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/AssemblyIndex.csCaptures ContentProvider authorities (including ctor array form) into component metadata.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.csUpdates emitted UCO wrapper bodies and references internal runtime helpers for exception bridging behavior.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.csEmits target attributes differently for merged vs per-assembly universes (anchor selection).
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/PEAssemblyBuilder.csIncreases default maxstack for emitted bodies with locals to avoid verifier issues.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.csEnsures invoker types get managed→proxy associations where needed; minor string helper update.

Comment on lines +85 to +103
internal static unsafe void RegisterNativeMethods ()
{
// Use the `string` overload of `JniType` deliberately. Its underlying
// `JniEnvironment.Types.TryFindClass(string, bool)` tries raw JNI `FindClass`
// first and, if that fails, falls back to `Class.forName(name, true, info.Runtime.ClassLoader)`,
// which resolves via the runtime's app ClassLoader — the same one that loads
// `mono.android.Runtime` from the APK.
// The `ReadOnlySpan<byte>` overload (see external/Java.Interop/src/Java.Interop/Java.Interop/JniEnvironment.Types.cs)
// only calls raw JNI `FindClass`, which resolves via the system ClassLoader on
// Android and returns a different `Class` instance from the one JCWs reference.
// Registering natives on that other instance is silently wrong.
using var runtimeClass = new JniType ("mono/android/Runtime");
fixed (byte* name = "registerNatives"u8, sig = "(Ljava/lang/Class;)V"u8) {
var onRegisterNatives = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr, void>)&OnRegisterNatives;
var method = new JniNativeMethod (name, sig, onRegisterNatives);
JniEnvironment.Types.RegisterNatives (runtimeClass.PeerReference, [method]);
lock (s_initLock) {
if (s_nativeMethodsRegistered) {
throw new InvalidOperationException ("TrimmableTypeMap native methods have already been registered.");
}

if (s_instance is null) {
throw new InvalidOperationException (
"TrimmableTypeMap has not been initialized. Ensure RuntimeFeature.TrimmableTypeMap is enabled and the JNI runtime is initialized.");
}

using var runtimeClass = new JniType ("mono/android/Runtime"u8);
fixed (byte* name = "registerNatives"u8, sig = "(Ljava/lang/Class;)V"u8) {
var onRegisterNatives = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr, void>)&OnRegisterNatives;
var method = new JniNativeMethod (name, sig, onRegisterNatives);
JniEnvironment.Types.RegisterNatives (runtimeClass.PeerReference, [method]);
}
s_nativeMethodsRegistered = true;
@simonrozsival
simonrozsival changed the base branch from main to pr-11252-startup-fixes-baseMay 4, 2026 15:38
@simonrozsival
simonrozsivalforce-pushed the trimmable-typemap-startup-hook-root-assembly branch from e8a15d3 to 04969c5CompareMay 4, 2026 15:41
@simonrozsival
simonrozsival changed the base branch from pr-11252-startup-fixes-base to mainMay 4, 2026 17:44
Replace the dedicated StartupHookRoots.xml descriptor with a conditional TrimmerRootAssembly item, matching the rest of the test project roots and avoiding a single-purpose descriptor file.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsivalforce-pushed the trimmable-typemap-startup-hook-root-assembly branch from 5dc9e05 to f8749eaCompareMay 4, 2026 18:31
@jonathanpeppers
jonathanpeppers merged commit baa5bff into mainMay 4, 2026
3 checks passed
@jonathanpeppers
jonathanpeppers deleted the trimmable-typemap-startup-hook-root-assembly branch May 4, 2026 20:49
@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

None yet

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

[tests] Root StartupHook test assembly directly - #11280

Merged
jonathanpeppers merged 1 commit into
mainfrom
trimmable-typemap-startup-hook-root-assembly
May 4, 2026
Merged

[tests] Root StartupHook test assembly directly#11280
jonathanpeppers merged 1 commit into
mainfrom
trimmable-typemap-startup-hook-root-assembly

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented May 4, 2026

Copy link
Copy Markdown
Member

Stacked on #11252 via temporary base branch pr-11252-startup-fixes-base.

Follow-up to #11252 (comment).

Replaces the dedicated StartupHookRoots.xml descriptor with a conditional TrimmerRootAssembly item for StartupHook. Once #11252 lands, this PR should be retargeted to main and should still contain only the StartupHook root simplification.

Validation:

MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -c Release -p:_AndroidTypeMapImplementation=trimmable -p:UseMonoRuntime=false -nr:false

CopilotAI review requested due to automatic review settings May 4, 2026 15:24

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 tightens and simplifies trimming roots for the on-device runtime test app (notably rooting the StartupHook test assembly directly), while also extending the trimmable typemap/runtime/test coverage and improving typemap build/packaging plumbing for CoreCLR.

Changes:

  • Simplify CoreCLRTrimmable test rooting: use TrimmerRootAssembly for StartupHook and switch test assembly roots to RootMode="Visible" under the trimmable typemap path.
  • Improve trimmable typemap runtime behavior and tests (e.g., cached simple-reference lookup; additional disposal/finalization scenarios; scanner edge cases).
  • Adjust typemap generation/emission and MSBuild targets to better support incremental builds and CoreCLR per-ABI packaging, plus fix typemap debug duplicate handling.

Reviewed changes

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

Show a summary per file
FileDescription
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.csTweaks excluded test list for CoreCLRTrimmable runtime runs.
tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csprojUpdates category exclusions and replaces/adjusts trimmer roots (including StartupHook).
tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/TrimmableTypeMapTypeManagerTests.csRefactors/extends trimmable typemap runtime tests (incl. disposal/finalization coverage).
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop-Tests.NET.csprojCompiles a Java.Interop GenericMarshaler helper source directly instead of referencing the external project.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.csAdds fixture types for nested invoker edge-case coverage.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.EdgeCases.csAdds regression test for connector nested invoker naming.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.csAdds content-provider authorities test and minor comment cleanup.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.csUpdates expectations around invoker associations in the model.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.csExpands metadata-shape tests for UCO wrappers/ctors and activation ctor referencing.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TrimmableTypeMapGeneratorTests.csUpdates cross-assembly alias merge tests to match current ownership rules.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.csAdds validation for merged vs aggregate anchoring behavior in target attributes.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/FixtureTestBase.csRemoves IL-token scanning helper no longer needed by generator tests.
src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.csFixes Java→managed debug mapping emission to use the correct duplicate template entry.
src/Xamarin.Android.Build.Tasks/Utilities/TypeMapCecilAdapter.csPrefers Mono.Android as the template for debug duplicates when present.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targetsMoves typemap assembly item population into a dedicated pre-target for incremental correctness.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.CoreCLR.targetsRefactors CoreCLR typemap-to-store addition to batch across ABIs and depend on the new prepare target.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.csAdds cached/simple-reference resolution that falls back to base-type chain when needed.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.csSplits native registration into an explicit post-runtime-init step; adds single-run guard.
src/Mono.Android/Microsoft.Android.Runtime/ITypeMapWithAliasing.csClarifies interface doc comment around returned types.
src/Mono.Android/Android.Runtime/JNIEnvInit.csInitializes typemap data earlier and registers typemap natives after JniRuntime.SetCurrent.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.csNormalizes connector-managed type names consistently (nested type separator handling).
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/AssemblyIndex.csCaptures ContentProvider authorities (including ctor array form) into component metadata.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.csUpdates emitted UCO wrapper bodies and references internal runtime helpers for exception bridging behavior.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.csEmits target attributes differently for merged vs per-assembly universes (anchor selection).
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/PEAssemblyBuilder.csIncreases default maxstack for emitted bodies with locals to avoid verifier issues.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.csEnsures invoker types get managed→proxy associations where needed; minor string helper update.

Comment on lines +85 to +103
internal static unsafe void RegisterNativeMethods ()
{
// Use the `string` overload of `JniType` deliberately. Its underlying
// `JniEnvironment.Types.TryFindClass(string, bool)` tries raw JNI `FindClass`
// first and, if that fails, falls back to `Class.forName(name, true, info.Runtime.ClassLoader)`,
// which resolves via the runtime's app ClassLoader — the same one that loads
// `mono.android.Runtime` from the APK.
// The `ReadOnlySpan<byte>` overload (see external/Java.Interop/src/Java.Interop/Java.Interop/JniEnvironment.Types.cs)
// only calls raw JNI `FindClass`, which resolves via the system ClassLoader on
// Android and returns a different `Class` instance from the one JCWs reference.
// Registering natives on that other instance is silently wrong.
using var runtimeClass = new JniType ("mono/android/Runtime");
fixed (byte* name = "registerNatives"u8, sig = "(Ljava/lang/Class;)V"u8) {
var onRegisterNatives = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr, void>)&OnRegisterNatives;
var method = new JniNativeMethod (name, sig, onRegisterNatives);
JniEnvironment.Types.RegisterNatives (runtimeClass.PeerReference, [method]);
lock (s_initLock) {
if (s_nativeMethodsRegistered) {
throw new InvalidOperationException ("TrimmableTypeMap native methods have already been registered.");
}

if (s_instance is null) {
throw new InvalidOperationException (
"TrimmableTypeMap has not been initialized. Ensure RuntimeFeature.TrimmableTypeMap is enabled and the JNI runtime is initialized.");
}

using var runtimeClass = new JniType ("mono/android/Runtime"u8);
fixed (byte* name = "registerNatives"u8, sig = "(Ljava/lang/Class;)V"u8) {
var onRegisterNatives = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr, void>)&OnRegisterNatives;
var method = new JniNativeMethod (name, sig, onRegisterNatives);
JniEnvironment.Types.RegisterNatives (runtimeClass.PeerReference, [method]);
}
s_nativeMethodsRegistered = true;
@simonrozsival
simonrozsival changed the base branch from main to pr-11252-startup-fixes-baseMay 4, 2026 15:38
@simonrozsival
simonrozsivalforce-pushed the trimmable-typemap-startup-hook-root-assembly branch from e8a15d3 to 04969c5CompareMay 4, 2026 15:41
@simonrozsival
simonrozsival changed the base branch from pr-11252-startup-fixes-base to mainMay 4, 2026 17:44
Replace the dedicated StartupHookRoots.xml descriptor with a conditional TrimmerRootAssembly item, matching the rest of the test project roots and avoiding a single-purpose descriptor file.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsivalforce-pushed the trimmable-typemap-startup-hook-root-assembly branch from 5dc9e05 to f8749eaCompareMay 4, 2026 18:31
@jonathanpeppers
jonathanpeppers merged commit baa5bff into mainMay 4, 2026
3 checks passed
@jonathanpeppers
jonathanpeppers deleted the trimmable-typemap-startup-hook-root-assembly branch May 4, 2026 20:49
@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

None yet

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 \u003e 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

[tests] Root StartupHook test assembly directly - #11280

Merged
jonathanpeppers merged 1 commit into
mainfrom
trimmable-typemap-startup-hook-root-assembly
May 4, 2026
Merged

[tests] Root StartupHook test assembly directly#11280
jonathanpeppers merged 1 commit into
mainfrom
trimmable-typemap-startup-hook-root-assembly

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented May 4, 2026

Copy link
Copy Markdown
Member

Stacked on #11252 via temporary base branch pr-11252-startup-fixes-base.

Follow-up to #11252 (comment).

Replaces the dedicated StartupHookRoots.xml descriptor with a conditional TrimmerRootAssembly item for StartupHook. Once #11252 lands, this PR should be retargeted to main and should still contain only the StartupHook root simplification.

Validation:

MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -c Release -p:_AndroidTypeMapImplementation=trimmable -p:UseMonoRuntime=false -nr:false

CopilotAI review requested due to automatic review settings May 4, 2026 15:24

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 tightens and simplifies trimming roots for the on-device runtime test app (notably rooting the StartupHook test assembly directly), while also extending the trimmable typemap/runtime/test coverage and improving typemap build/packaging plumbing for CoreCLR.

Changes:

  • Simplify CoreCLRTrimmable test rooting: use TrimmerRootAssembly for StartupHook and switch test assembly roots to RootMode="Visible" under the trimmable typemap path.
  • Improve trimmable typemap runtime behavior and tests (e.g., cached simple-reference lookup; additional disposal/finalization scenarios; scanner edge cases).
  • Adjust typemap generation/emission and MSBuild targets to better support incremental builds and CoreCLR per-ABI packaging, plus fix typemap debug duplicate handling.

Reviewed changes

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

Show a summary per file
FileDescription
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.csTweaks excluded test list for CoreCLRTrimmable runtime runs.
tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csprojUpdates category exclusions and replaces/adjusts trimmer roots (including StartupHook).
tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/TrimmableTypeMapTypeManagerTests.csRefactors/extends trimmable typemap runtime tests (incl. disposal/finalization coverage).
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop-Tests.NET.csprojCompiles a Java.Interop GenericMarshaler helper source directly instead of referencing the external project.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.csAdds fixture types for nested invoker edge-case coverage.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.EdgeCases.csAdds regression test for connector nested invoker naming.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.csAdds content-provider authorities test and minor comment cleanup.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.csUpdates expectations around invoker associations in the model.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.csExpands metadata-shape tests for UCO wrappers/ctors and activation ctor referencing.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TrimmableTypeMapGeneratorTests.csUpdates cross-assembly alias merge tests to match current ownership rules.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.csAdds validation for merged vs aggregate anchoring behavior in target attributes.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/FixtureTestBase.csRemoves IL-token scanning helper no longer needed by generator tests.
src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.csFixes Java→managed debug mapping emission to use the correct duplicate template entry.
src/Xamarin.Android.Build.Tasks/Utilities/TypeMapCecilAdapter.csPrefers Mono.Android as the template for debug duplicates when present.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targetsMoves typemap assembly item population into a dedicated pre-target for incremental correctness.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.CoreCLR.targetsRefactors CoreCLR typemap-to-store addition to batch across ABIs and depend on the new prepare target.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.csAdds cached/simple-reference resolution that falls back to base-type chain when needed.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.csSplits native registration into an explicit post-runtime-init step; adds single-run guard.
src/Mono.Android/Microsoft.Android.Runtime/ITypeMapWithAliasing.csClarifies interface doc comment around returned types.
src/Mono.Android/Android.Runtime/JNIEnvInit.csInitializes typemap data earlier and registers typemap natives after JniRuntime.SetCurrent.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.csNormalizes connector-managed type names consistently (nested type separator handling).
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/AssemblyIndex.csCaptures ContentProvider authorities (including ctor array form) into component metadata.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.csUpdates emitted UCO wrapper bodies and references internal runtime helpers for exception bridging behavior.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.csEmits target attributes differently for merged vs per-assembly universes (anchor selection).
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/PEAssemblyBuilder.csIncreases default maxstack for emitted bodies with locals to avoid verifier issues.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.csEnsures invoker types get managed→proxy associations where needed; minor string helper update.

Comment on lines +85 to +103
internal static unsafe void RegisterNativeMethods ()
{
// Use the `string` overload of `JniType` deliberately. Its underlying
// `JniEnvironment.Types.TryFindClass(string, bool)` tries raw JNI `FindClass`
// first and, if that fails, falls back to `Class.forName(name, true, info.Runtime.ClassLoader)`,
// which resolves via the runtime's app ClassLoader — the same one that loads
// `mono.android.Runtime` from the APK.
// The `ReadOnlySpan<byte>` overload (see external/Java.Interop/src/Java.Interop/Java.Interop/JniEnvironment.Types.cs)
// only calls raw JNI `FindClass`, which resolves via the system ClassLoader on
// Android and returns a different `Class` instance from the one JCWs reference.
// Registering natives on that other instance is silently wrong.
using var runtimeClass = new JniType ("mono/android/Runtime");
fixed (byte* name = "registerNatives"u8, sig = "(Ljava/lang/Class;)V"u8) {
var onRegisterNatives = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr, void>)&OnRegisterNatives;
var method = new JniNativeMethod (name, sig, onRegisterNatives);
JniEnvironment.Types.RegisterNatives (runtimeClass.PeerReference, [method]);
lock (s_initLock) {
if (s_nativeMethodsRegistered) {
throw new InvalidOperationException ("TrimmableTypeMap native methods have already been registered.");
}

if (s_instance is null) {
throw new InvalidOperationException (
"TrimmableTypeMap has not been initialized. Ensure RuntimeFeature.TrimmableTypeMap is enabled and the JNI runtime is initialized.");
}

using var runtimeClass = new JniType ("mono/android/Runtime"u8);
fixed (byte* name = "registerNatives"u8, sig = "(Ljava/lang/Class;)V"u8) {
var onRegisterNatives = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr, void>)&OnRegisterNatives;
var method = new JniNativeMethod (name, sig, onRegisterNatives);
JniEnvironment.Types.RegisterNatives (runtimeClass.PeerReference, [method]);
}
s_nativeMethodsRegistered = true;
@simonrozsival
simonrozsival changed the base branch from main to pr-11252-startup-fixes-baseMay 4, 2026 15:38
@simonrozsival
simonrozsivalforce-pushed the trimmable-typemap-startup-hook-root-assembly branch from e8a15d3 to 04969c5CompareMay 4, 2026 15:41
@simonrozsival
simonrozsival changed the base branch from pr-11252-startup-fixes-base to mainMay 4, 2026 17:44
Replace the dedicated StartupHookRoots.xml descriptor with a conditional TrimmerRootAssembly item, matching the rest of the test project roots and avoiding a single-purpose descriptor file.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsivalforce-pushed the trimmable-typemap-startup-hook-root-assembly branch from 5dc9e05 to f8749eaCompareMay 4, 2026 18:31
@jonathanpeppers
jonathanpeppers merged commit baa5bff into mainMay 4, 2026
3 checks passed
@jonathanpeppers
jonathanpeppers deleted the trimmable-typemap-startup-hook-root-assembly branch May 4, 2026 20:49
@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

None yet

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

[tests] Root StartupHook test assembly directly - #11280

Merged
jonathanpeppers merged 1 commit into
mainfrom
trimmable-typemap-startup-hook-root-assembly
May 4, 2026
Merged

[tests] Root StartupHook test assembly directly#11280
jonathanpeppers merged 1 commit into
mainfrom
trimmable-typemap-startup-hook-root-assembly

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented May 4, 2026

Copy link
Copy Markdown
Member

Stacked on #11252 via temporary base branch pr-11252-startup-fixes-base.

Follow-up to #11252 (comment).

Replaces the dedicated StartupHookRoots.xml descriptor with a conditional TrimmerRootAssembly item for StartupHook. Once #11252 lands, this PR should be retargeted to main and should still contain only the StartupHook root simplification.

Validation:

MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -c Release -p:_AndroidTypeMapImplementation=trimmable -p:UseMonoRuntime=false -nr:false

CopilotAI review requested due to automatic review settings May 4, 2026 15:24

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 tightens and simplifies trimming roots for the on-device runtime test app (notably rooting the StartupHook test assembly directly), while also extending the trimmable typemap/runtime/test coverage and improving typemap build/packaging plumbing for CoreCLR.

Changes:

  • Simplify CoreCLRTrimmable test rooting: use TrimmerRootAssembly for StartupHook and switch test assembly roots to RootMode="Visible" under the trimmable typemap path.
  • Improve trimmable typemap runtime behavior and tests (e.g., cached simple-reference lookup; additional disposal/finalization scenarios; scanner edge cases).
  • Adjust typemap generation/emission and MSBuild targets to better support incremental builds and CoreCLR per-ABI packaging, plus fix typemap debug duplicate handling.

Reviewed changes

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

Show a summary per file
FileDescription
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.csTweaks excluded test list for CoreCLRTrimmable runtime runs.
tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csprojUpdates category exclusions and replaces/adjusts trimmer roots (including StartupHook).
tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/TrimmableTypeMapTypeManagerTests.csRefactors/extends trimmable typemap runtime tests (incl. disposal/finalization coverage).
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop-Tests.NET.csprojCompiles a Java.Interop GenericMarshaler helper source directly instead of referencing the external project.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.csAdds fixture types for nested invoker edge-case coverage.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.EdgeCases.csAdds regression test for connector nested invoker naming.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.csAdds content-provider authorities test and minor comment cleanup.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.csUpdates expectations around invoker associations in the model.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.csExpands metadata-shape tests for UCO wrappers/ctors and activation ctor referencing.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TrimmableTypeMapGeneratorTests.csUpdates cross-assembly alias merge tests to match current ownership rules.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.csAdds validation for merged vs aggregate anchoring behavior in target attributes.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/FixtureTestBase.csRemoves IL-token scanning helper no longer needed by generator tests.
src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.csFixes Java→managed debug mapping emission to use the correct duplicate template entry.
src/Xamarin.Android.Build.Tasks/Utilities/TypeMapCecilAdapter.csPrefers Mono.Android as the template for debug duplicates when present.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targetsMoves typemap assembly item population into a dedicated pre-target for incremental correctness.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.CoreCLR.targetsRefactors CoreCLR typemap-to-store addition to batch across ABIs and depend on the new prepare target.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.csAdds cached/simple-reference resolution that falls back to base-type chain when needed.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.csSplits native registration into an explicit post-runtime-init step; adds single-run guard.
src/Mono.Android/Microsoft.Android.Runtime/ITypeMapWithAliasing.csClarifies interface doc comment around returned types.
src/Mono.Android/Android.Runtime/JNIEnvInit.csInitializes typemap data earlier and registers typemap natives after JniRuntime.SetCurrent.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.csNormalizes connector-managed type names consistently (nested type separator handling).
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/AssemblyIndex.csCaptures ContentProvider authorities (including ctor array form) into component metadata.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.csUpdates emitted UCO wrapper bodies and references internal runtime helpers for exception bridging behavior.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.csEmits target attributes differently for merged vs per-assembly universes (anchor selection).
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/PEAssemblyBuilder.csIncreases default maxstack for emitted bodies with locals to avoid verifier issues.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.csEnsures invoker types get managed→proxy associations where needed; minor string helper update.

Comment on lines +85 to +103
internal static unsafe void RegisterNativeMethods ()
{
// Use the `string` overload of `JniType` deliberately. Its underlying
// `JniEnvironment.Types.TryFindClass(string, bool)` tries raw JNI `FindClass`
// first and, if that fails, falls back to `Class.forName(name, true, info.Runtime.ClassLoader)`,
// which resolves via the runtime's app ClassLoader — the same one that loads
// `mono.android.Runtime` from the APK.
// The `ReadOnlySpan<byte>` overload (see external/Java.Interop/src/Java.Interop/Java.Interop/JniEnvironment.Types.cs)
// only calls raw JNI `FindClass`, which resolves via the system ClassLoader on
// Android and returns a different `Class` instance from the one JCWs reference.
// Registering natives on that other instance is silently wrong.
using var runtimeClass = new JniType ("mono/android/Runtime");
fixed (byte* name = "registerNatives"u8, sig = "(Ljava/lang/Class;)V"u8) {
var onRegisterNatives = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr, void>)&OnRegisterNatives;
var method = new JniNativeMethod (name, sig, onRegisterNatives);
JniEnvironment.Types.RegisterNatives (runtimeClass.PeerReference, [method]);
lock (s_initLock) {
if (s_nativeMethodsRegistered) {
throw new InvalidOperationException ("TrimmableTypeMap native methods have already been registered.");
}

if (s_instance is null) {
throw new InvalidOperationException (
"TrimmableTypeMap has not been initialized. Ensure RuntimeFeature.TrimmableTypeMap is enabled and the JNI runtime is initialized.");
}

using var runtimeClass = new JniType ("mono/android/Runtime"u8);
fixed (byte* name = "registerNatives"u8, sig = "(Ljava/lang/Class;)V"u8) {
var onRegisterNatives = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr, void>)&OnRegisterNatives;
var method = new JniNativeMethod (name, sig, onRegisterNatives);
JniEnvironment.Types.RegisterNatives (runtimeClass.PeerReference, [method]);
}
s_nativeMethodsRegistered = true;
@simonrozsival
simonrozsival changed the base branch from main to pr-11252-startup-fixes-baseMay 4, 2026 15:38
@simonrozsival
simonrozsivalforce-pushed the trimmable-typemap-startup-hook-root-assembly branch from e8a15d3 to 04969c5CompareMay 4, 2026 15:41
@simonrozsival
simonrozsival changed the base branch from pr-11252-startup-fixes-base to mainMay 4, 2026 17:44
Replace the dedicated StartupHookRoots.xml descriptor with a conditional TrimmerRootAssembly item, matching the rest of the test project roots and avoiding a single-purpose descriptor file.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsivalforce-pushed the trimmable-typemap-startup-hook-root-assembly branch from 5dc9e05 to f8749eaCompareMay 4, 2026 18:31
@jonathanpeppers
jonathanpeppers merged commit baa5bff into mainMay 4, 2026
3 checks passed
@jonathanpeppers
jonathanpeppers deleted the trimmable-typemap-startup-hook-root-assembly branch May 4, 2026 20:49
@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

None yet

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

[tests] Root StartupHook test assembly directly - #11280

Merged
jonathanpeppers merged 1 commit into
mainfrom
trimmable-typemap-startup-hook-root-assembly
May 4, 2026
Merged

[tests] Root StartupHook test assembly directly#11280
jonathanpeppers merged 1 commit into
mainfrom
trimmable-typemap-startup-hook-root-assembly

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented May 4, 2026

Copy link
Copy Markdown
Member

Stacked on #11252 via temporary base branch pr-11252-startup-fixes-base.

Follow-up to #11252 (comment).

Replaces the dedicated StartupHookRoots.xml descriptor with a conditional TrimmerRootAssembly item for StartupHook. Once #11252 lands, this PR should be retargeted to main and should still contain only the StartupHook root simplification.

Validation:

MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -c Release -p:_AndroidTypeMapImplementation=trimmable -p:UseMonoRuntime=false -nr:false

CopilotAI review requested due to automatic review settings May 4, 2026 15:24

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 tightens and simplifies trimming roots for the on-device runtime test app (notably rooting the StartupHook test assembly directly), while also extending the trimmable typemap/runtime/test coverage and improving typemap build/packaging plumbing for CoreCLR.

Changes:

  • Simplify CoreCLRTrimmable test rooting: use TrimmerRootAssembly for StartupHook and switch test assembly roots to RootMode="Visible" under the trimmable typemap path.
  • Improve trimmable typemap runtime behavior and tests (e.g., cached simple-reference lookup; additional disposal/finalization scenarios; scanner edge cases).
  • Adjust typemap generation/emission and MSBuild targets to better support incremental builds and CoreCLR per-ABI packaging, plus fix typemap debug duplicate handling.

Reviewed changes

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

Show a summary per file
FileDescription
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.csTweaks excluded test list for CoreCLRTrimmable runtime runs.
tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csprojUpdates category exclusions and replaces/adjusts trimmer roots (including StartupHook).
tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/TrimmableTypeMapTypeManagerTests.csRefactors/extends trimmable typemap runtime tests (incl. disposal/finalization coverage).
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop-Tests.NET.csprojCompiles a Java.Interop GenericMarshaler helper source directly instead of referencing the external project.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.csAdds fixture types for nested invoker edge-case coverage.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.EdgeCases.csAdds regression test for connector nested invoker naming.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.csAdds content-provider authorities test and minor comment cleanup.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.csUpdates expectations around invoker associations in the model.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.csExpands metadata-shape tests for UCO wrappers/ctors and activation ctor referencing.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TrimmableTypeMapGeneratorTests.csUpdates cross-assembly alias merge tests to match current ownership rules.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.csAdds validation for merged vs aggregate anchoring behavior in target attributes.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/FixtureTestBase.csRemoves IL-token scanning helper no longer needed by generator tests.
src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.csFixes Java→managed debug mapping emission to use the correct duplicate template entry.
src/Xamarin.Android.Build.Tasks/Utilities/TypeMapCecilAdapter.csPrefers Mono.Android as the template for debug duplicates when present.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targetsMoves typemap assembly item population into a dedicated pre-target for incremental correctness.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.CoreCLR.targetsRefactors CoreCLR typemap-to-store addition to batch across ABIs and depend on the new prepare target.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.csAdds cached/simple-reference resolution that falls back to base-type chain when needed.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.csSplits native registration into an explicit post-runtime-init step; adds single-run guard.
src/Mono.Android/Microsoft.Android.Runtime/ITypeMapWithAliasing.csClarifies interface doc comment around returned types.
src/Mono.Android/Android.Runtime/JNIEnvInit.csInitializes typemap data earlier and registers typemap natives after JniRuntime.SetCurrent.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.csNormalizes connector-managed type names consistently (nested type separator handling).
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/AssemblyIndex.csCaptures ContentProvider authorities (including ctor array form) into component metadata.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.csUpdates emitted UCO wrapper bodies and references internal runtime helpers for exception bridging behavior.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.csEmits target attributes differently for merged vs per-assembly universes (anchor selection).
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/PEAssemblyBuilder.csIncreases default maxstack for emitted bodies with locals to avoid verifier issues.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.csEnsures invoker types get managed→proxy associations where needed; minor string helper update.

Comment on lines +85 to +103
internal static unsafe void RegisterNativeMethods ()
{
// Use the `string` overload of `JniType` deliberately. Its underlying
// `JniEnvironment.Types.TryFindClass(string, bool)` tries raw JNI `FindClass`
// first and, if that fails, falls back to `Class.forName(name, true, info.Runtime.ClassLoader)`,
// which resolves via the runtime's app ClassLoader — the same one that loads
// `mono.android.Runtime` from the APK.
// The `ReadOnlySpan<byte>` overload (see external/Java.Interop/src/Java.Interop/Java.Interop/JniEnvironment.Types.cs)
// only calls raw JNI `FindClass`, which resolves via the system ClassLoader on
// Android and returns a different `Class` instance from the one JCWs reference.
// Registering natives on that other instance is silently wrong.
using var runtimeClass = new JniType ("mono/android/Runtime");
fixed (byte* name = "registerNatives"u8, sig = "(Ljava/lang/Class;)V"u8) {
var onRegisterNatives = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr, void>)&OnRegisterNatives;
var method = new JniNativeMethod (name, sig, onRegisterNatives);
JniEnvironment.Types.RegisterNatives (runtimeClass.PeerReference, [method]);
lock (s_initLock) {
if (s_nativeMethodsRegistered) {
throw new InvalidOperationException ("TrimmableTypeMap native methods have already been registered.");
}

if (s_instance is null) {
throw new InvalidOperationException (
"TrimmableTypeMap has not been initialized. Ensure RuntimeFeature.TrimmableTypeMap is enabled and the JNI runtime is initialized.");
}

using var runtimeClass = new JniType ("mono/android/Runtime"u8);
fixed (byte* name = "registerNatives"u8, sig = "(Ljava/lang/Class;)V"u8) {
var onRegisterNatives = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr, void>)&OnRegisterNatives;
var method = new JniNativeMethod (name, sig, onRegisterNatives);
JniEnvironment.Types.RegisterNatives (runtimeClass.PeerReference, [method]);
}
s_nativeMethodsRegistered = true;
@simonrozsival
simonrozsival changed the base branch from main to pr-11252-startup-fixes-baseMay 4, 2026 15:38
@simonrozsival
simonrozsivalforce-pushed the trimmable-typemap-startup-hook-root-assembly branch from e8a15d3 to 04969c5CompareMay 4, 2026 15:41
@simonrozsival
simonrozsival changed the base branch from pr-11252-startup-fixes-base to mainMay 4, 2026 17:44
Replace the dedicated StartupHookRoots.xml descriptor with a conditional TrimmerRootAssembly item, matching the rest of the test project roots and avoiding a single-purpose descriptor file.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsivalforce-pushed the trimmable-typemap-startup-hook-root-assembly branch from 5dc9e05 to f8749eaCompareMay 4, 2026 18:31
@jonathanpeppers
jonathanpeppers merged commit baa5bff into mainMay 4, 2026
3 checks passed
@jonathanpeppers
jonathanpeppers deleted the trimmable-typemap-startup-hook-root-assembly branch May 4, 2026 20:49
@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

None yet

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

[tests] Root StartupHook test assembly directly - #11280

Merged
jonathanpeppers merged 1 commit into
mainfrom
trimmable-typemap-startup-hook-root-assembly
May 4, 2026
Merged

[tests] Root StartupHook test assembly directly#11280
jonathanpeppers merged 1 commit into
mainfrom
trimmable-typemap-startup-hook-root-assembly

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented May 4, 2026

Copy link
Copy Markdown
Member

Stacked on #11252 via temporary base branch pr-11252-startup-fixes-base.

Follow-up to #11252 (comment).

Replaces the dedicated StartupHookRoots.xml descriptor with a conditional TrimmerRootAssembly item for StartupHook. Once #11252 lands, this PR should be retargeted to main and should still contain only the StartupHook root simplification.

Validation:

MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -c Release -p:_AndroidTypeMapImplementation=trimmable -p:UseMonoRuntime=false -nr:false

CopilotAI review requested due to automatic review settings May 4, 2026 15:24

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 tightens and simplifies trimming roots for the on-device runtime test app (notably rooting the StartupHook test assembly directly), while also extending the trimmable typemap/runtime/test coverage and improving typemap build/packaging plumbing for CoreCLR.

Changes:

  • Simplify CoreCLRTrimmable test rooting: use TrimmerRootAssembly for StartupHook and switch test assembly roots to RootMode="Visible" under the trimmable typemap path.
  • Improve trimmable typemap runtime behavior and tests (e.g., cached simple-reference lookup; additional disposal/finalization scenarios; scanner edge cases).
  • Adjust typemap generation/emission and MSBuild targets to better support incremental builds and CoreCLR per-ABI packaging, plus fix typemap debug duplicate handling.

Reviewed changes

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

Show a summary per file
FileDescription
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.csTweaks excluded test list for CoreCLRTrimmable runtime runs.
tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csprojUpdates category exclusions and replaces/adjusts trimmer roots (including StartupHook).
tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/TrimmableTypeMapTypeManagerTests.csRefactors/extends trimmable typemap runtime tests (incl. disposal/finalization coverage).
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop-Tests.NET.csprojCompiles a Java.Interop GenericMarshaler helper source directly instead of referencing the external project.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.csAdds fixture types for nested invoker edge-case coverage.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.EdgeCases.csAdds regression test for connector nested invoker naming.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.csAdds content-provider authorities test and minor comment cleanup.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.csUpdates expectations around invoker associations in the model.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.csExpands metadata-shape tests for UCO wrappers/ctors and activation ctor referencing.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TrimmableTypeMapGeneratorTests.csUpdates cross-assembly alias merge tests to match current ownership rules.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.csAdds validation for merged vs aggregate anchoring behavior in target attributes.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/FixtureTestBase.csRemoves IL-token scanning helper no longer needed by generator tests.
src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.csFixes Java→managed debug mapping emission to use the correct duplicate template entry.
src/Xamarin.Android.Build.Tasks/Utilities/TypeMapCecilAdapter.csPrefers Mono.Android as the template for debug duplicates when present.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targetsMoves typemap assembly item population into a dedicated pre-target for incremental correctness.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.CoreCLR.targetsRefactors CoreCLR typemap-to-store addition to batch across ABIs and depend on the new prepare target.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.csAdds cached/simple-reference resolution that falls back to base-type chain when needed.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.csSplits native registration into an explicit post-runtime-init step; adds single-run guard.
src/Mono.Android/Microsoft.Android.Runtime/ITypeMapWithAliasing.csClarifies interface doc comment around returned types.
src/Mono.Android/Android.Runtime/JNIEnvInit.csInitializes typemap data earlier and registers typemap natives after JniRuntime.SetCurrent.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.csNormalizes connector-managed type names consistently (nested type separator handling).
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/AssemblyIndex.csCaptures ContentProvider authorities (including ctor array form) into component metadata.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.csUpdates emitted UCO wrapper bodies and references internal runtime helpers for exception bridging behavior.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.csEmits target attributes differently for merged vs per-assembly universes (anchor selection).
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/PEAssemblyBuilder.csIncreases default maxstack for emitted bodies with locals to avoid verifier issues.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.csEnsures invoker types get managed→proxy associations where needed; minor string helper update.

Comment on lines +85 to +103
internal static unsafe void RegisterNativeMethods ()
{
// Use the `string` overload of `JniType` deliberately. Its underlying
// `JniEnvironment.Types.TryFindClass(string, bool)` tries raw JNI `FindClass`
// first and, if that fails, falls back to `Class.forName(name, true, info.Runtime.ClassLoader)`,
// which resolves via the runtime's app ClassLoader — the same one that loads
// `mono.android.Runtime` from the APK.
// The `ReadOnlySpan<byte>` overload (see external/Java.Interop/src/Java.Interop/Java.Interop/JniEnvironment.Types.cs)
// only calls raw JNI `FindClass`, which resolves via the system ClassLoader on
// Android and returns a different `Class` instance from the one JCWs reference.
// Registering natives on that other instance is silently wrong.
using var runtimeClass = new JniType ("mono/android/Runtime");
fixed (byte* name = "registerNatives"u8, sig = "(Ljava/lang/Class;)V"u8) {
var onRegisterNatives = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr, void>)&OnRegisterNatives;
var method = new JniNativeMethod (name, sig, onRegisterNatives);
JniEnvironment.Types.RegisterNatives (runtimeClass.PeerReference, [method]);
lock (s_initLock) {
if (s_nativeMethodsRegistered) {
throw new InvalidOperationException ("TrimmableTypeMap native methods have already been registered.");
}

if (s_instance is null) {
throw new InvalidOperationException (
"TrimmableTypeMap has not been initialized. Ensure RuntimeFeature.TrimmableTypeMap is enabled and the JNI runtime is initialized.");
}

using var runtimeClass = new JniType ("mono/android/Runtime"u8);
fixed (byte* name = "registerNatives"u8, sig = "(Ljava/lang/Class;)V"u8) {
var onRegisterNatives = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr, void>)&OnRegisterNatives;
var method = new JniNativeMethod (name, sig, onRegisterNatives);
JniEnvironment.Types.RegisterNatives (runtimeClass.PeerReference, [method]);
}
s_nativeMethodsRegistered = true;
@simonrozsival
simonrozsival changed the base branch from main to pr-11252-startup-fixes-baseMay 4, 2026 15:38
@simonrozsival
simonrozsivalforce-pushed the trimmable-typemap-startup-hook-root-assembly branch from e8a15d3 to 04969c5CompareMay 4, 2026 15:41
@simonrozsival
simonrozsival changed the base branch from pr-11252-startup-fixes-base to mainMay 4, 2026 17:44
Replace the dedicated StartupHookRoots.xml descriptor with a conditional TrimmerRootAssembly item, matching the rest of the test project roots and avoiding a single-purpose descriptor file.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsivalforce-pushed the trimmable-typemap-startup-hook-root-assembly branch from 5dc9e05 to f8749eaCompareMay 4, 2026 18:31
@jonathanpeppers
jonathanpeppers merged commit baa5bff into mainMay 4, 2026
3 checks passed
@jonathanpeppers
jonathanpeppers deleted the trimmable-typemap-startup-hook-root-assembly branch May 4, 2026 20:49
@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

None yet

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

[tests] Root StartupHook test assembly directly - #11280

Merged
jonathanpeppers merged 1 commit into
mainfrom
trimmable-typemap-startup-hook-root-assembly
May 4, 2026
Merged

[tests] Root StartupHook test assembly directly#11280
jonathanpeppers merged 1 commit into
mainfrom
trimmable-typemap-startup-hook-root-assembly

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented May 4, 2026

Copy link
Copy Markdown
Member

Stacked on #11252 via temporary base branch pr-11252-startup-fixes-base.

Follow-up to #11252 (comment).

Replaces the dedicated StartupHookRoots.xml descriptor with a conditional TrimmerRootAssembly item for StartupHook. Once #11252 lands, this PR should be retargeted to main and should still contain only the StartupHook root simplification.

Validation:

MSBUILDDISABLENODEREUSE=1 ./dotnet-local.sh build tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj -c Release -p:_AndroidTypeMapImplementation=trimmable -p:UseMonoRuntime=false -nr:false

CopilotAI review requested due to automatic review settings May 4, 2026 15:24

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 tightens and simplifies trimming roots for the on-device runtime test app (notably rooting the StartupHook test assembly directly), while also extending the trimmable typemap/runtime/test coverage and improving typemap build/packaging plumbing for CoreCLR.

Changes:

  • Simplify CoreCLRTrimmable test rooting: use TrimmerRootAssembly for StartupHook and switch test assembly roots to RootMode="Visible" under the trimmable typemap path.
  • Improve trimmable typemap runtime behavior and tests (e.g., cached simple-reference lookup; additional disposal/finalization scenarios; scanner edge cases).
  • Adjust typemap generation/emission and MSBuild targets to better support incremental builds and CoreCLR per-ABI packaging, plus fix typemap debug duplicate handling.

Reviewed changes

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

Show a summary per file
FileDescription
tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.csTweaks excluded test list for CoreCLRTrimmable runtime runs.
tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csprojUpdates category exclusions and replaces/adjusts trimmer roots (including StartupHook).
tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/TrimmableTypeMapTypeManagerTests.csRefactors/extends trimmable typemap runtime tests (incl. disposal/finalization coverage).
tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop-Tests.NET.csprojCompiles a Java.Interop GenericMarshaler helper source directly instead of referencing the external project.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.csAdds fixture types for nested invoker edge-case coverage.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.EdgeCases.csAdds regression test for connector nested invoker naming.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/JavaPeerScannerTests.csAdds content-provider authorities test and minor comment cleanup.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.csUpdates expectations around invoker associations in the model.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.csExpands metadata-shape tests for UCO wrappers/ctors and activation ctor referencing.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TrimmableTypeMapGeneratorTests.csUpdates cross-assembly alias merge tests to match current ownership rules.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.csAdds validation for merged vs aggregate anchoring behavior in target attributes.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/FixtureTestBase.csRemoves IL-token scanning helper no longer needed by generator tests.
src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.csFixes Java→managed debug mapping emission to use the correct duplicate template entry.
src/Xamarin.Android.Build.Tasks/Utilities/TypeMapCecilAdapter.csPrefers Mono.Android as the template for debug duplicates when present.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targetsMoves typemap assembly item population into a dedicated pre-target for incremental correctness.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.CoreCLR.targetsRefactors CoreCLR typemap-to-store addition to batch across ABIs and depend on the new prepare target.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.csAdds cached/simple-reference resolution that falls back to base-type chain when needed.
src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.csSplits native registration into an explicit post-runtime-init step; adds single-run guard.
src/Mono.Android/Microsoft.Android.Runtime/ITypeMapWithAliasing.csClarifies interface doc comment around returned types.
src/Mono.Android/Android.Runtime/JNIEnvInit.csInitializes typemap data earlier and registers typemap natives after JniRuntime.SetCurrent.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.csNormalizes connector-managed type names consistently (nested type separator handling).
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/AssemblyIndex.csCaptures ContentProvider authorities (including ctor array form) into component metadata.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.csUpdates emitted UCO wrapper bodies and references internal runtime helpers for exception bridging behavior.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.csEmits target attributes differently for merged vs per-assembly universes (anchor selection).
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/PEAssemblyBuilder.csIncreases default maxstack for emitted bodies with locals to avoid verifier issues.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.csEnsures invoker types get managed→proxy associations where needed; minor string helper update.

Comment on lines +85 to +103
internal static unsafe void RegisterNativeMethods ()
{
// Use the `string` overload of `JniType` deliberately. Its underlying
// `JniEnvironment.Types.TryFindClass(string, bool)` tries raw JNI `FindClass`
// first and, if that fails, falls back to `Class.forName(name, true, info.Runtime.ClassLoader)`,
// which resolves via the runtime's app ClassLoader — the same one that loads
// `mono.android.Runtime` from the APK.
// The `ReadOnlySpan<byte>` overload (see external/Java.Interop/src/Java.Interop/Java.Interop/JniEnvironment.Types.cs)
// only calls raw JNI `FindClass`, which resolves via the system ClassLoader on
// Android and returns a different `Class` instance from the one JCWs reference.
// Registering natives on that other instance is silently wrong.
using var runtimeClass = new JniType ("mono/android/Runtime");
fixed (byte* name = "registerNatives"u8, sig = "(Ljava/lang/Class;)V"u8) {
var onRegisterNatives = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr, void>)&OnRegisterNatives;
var method = new JniNativeMethod (name, sig, onRegisterNatives);
JniEnvironment.Types.RegisterNatives (runtimeClass.PeerReference, [method]);
lock (s_initLock) {
if (s_nativeMethodsRegistered) {
throw new InvalidOperationException ("TrimmableTypeMap native methods have already been registered.");
}

if (s_instance is null) {
throw new InvalidOperationException (
"TrimmableTypeMap has not been initialized. Ensure RuntimeFeature.TrimmableTypeMap is enabled and the JNI runtime is initialized.");
}

using var runtimeClass = new JniType ("mono/android/Runtime"u8);
fixed (byte* name = "registerNatives"u8, sig = "(Ljava/lang/Class;)V"u8) {
var onRegisterNatives = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr, void>)&OnRegisterNatives;
var method = new JniNativeMethod (name, sig, onRegisterNatives);
JniEnvironment.Types.RegisterNatives (runtimeClass.PeerReference, [method]);
}
s_nativeMethodsRegistered = true;
@simonrozsival
simonrozsival changed the base branch from main to pr-11252-startup-fixes-baseMay 4, 2026 15:38
@simonrozsival
simonrozsivalforce-pushed the trimmable-typemap-startup-hook-root-assembly branch from e8a15d3 to 04969c5CompareMay 4, 2026 15:41
@simonrozsival
simonrozsival changed the base branch from pr-11252-startup-fixes-base to mainMay 4, 2026 17:44
Replace the dedicated StartupHookRoots.xml descriptor with a conditional TrimmerRootAssembly item, matching the rest of the test project roots and avoiding a single-purpose descriptor file.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsivalforce-pushed the trimmable-typemap-startup-hook-root-assembly branch from 5dc9e05 to f8749eaCompareMay 4, 2026 18:31
@jonathanpeppers
jonathanpeppers merged commit baa5bff into mainMay 4, 2026
3 checks passed
@jonathanpeppers
jonathanpeppers deleted the trimmable-typemap-startup-hook-root-assembly branch May 4, 2026 20:49
@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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@simonrozsival@jonathanpeppers