[TrimmableTypeMap][NativeAOT] Scan runtime host ACWs via a ref assembly#11982
Open
simonrozsival wants to merge 1 commit into
Open
[TrimmableTypeMap][NativeAOT] Scan runtime host ACWs via a ref assembly#11982simonrozsival wants to merge 1 commit into
simonrozsival wants to merge 1 commit into
Conversation
The trimmable typemap generator (_GenerateTrimmableTypeMap) runs only in the RID-independent OUTER build over @(ReferencePath), which contains just the compile closure (app + references + ref-pack framework assemblies). The NativeAOT runtime host, Microsoft.Android.Runtime.NativeAOT, is a runtime-only assembly resolved solely in the per-RID inner build (as a RID-specific runtime pack asset), so it was never scanned. Its only Java Callable Wrapper type, UncaughtExceptionMarshaler, therefore had no JCW, no typemap entry, and no acw-map entry -> R8 had nothing to keep -> an on-device startup crash in JavaInteropRuntime.init (setDefaultUncaughtExceptionHandler). Runtime-pack resolution is inherently per-RID (ResolveRuntimePackAssets needs a single RuntimeIdentifier), but the host assembly's managed metadata is RID-independent (byte-identical across RIDs). So produce a reference assembly for it and ship it in the Microsoft.Android.Sdk pack under tools/typemap-refs, then feed it to the generator as an extra framework input in the outer build. It is intentionally NOT placed in the Microsoft.Android.Ref targeting pack: files under a targeting pack's ref/ folder must all be classified in its FrameworkList and would become universal framework references for every Android app. Shipping it in the SDK pack keeps it a build-time-only input for the trimmable NativeAOT path. Its per-assembly typemap DLL (_Microsoft.Android.Runtime.NativeAOT.TypeMap) is classified as framework for ILC so it stays an IlcReference but is removed from the unmanaged-entrypoint roots, matching Mono.Android/Java.Interop; its JCW native methods register via the runtime registerNatives path. Adds Build_WithTrimmableTypeMap_KeepsNativeAotRuntimeHostAcws, which opts into the trimmable typemap on NativeAOT and asserts classes.dex retains the UncaughtExceptionMarshaler runtime ACW. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a NativeAOT + trimmable typemap startup crash by ensuring the typemap generator scans the runtime-only Microsoft.Android.Runtime.NativeAOT assembly (via a shipped reference assembly), so its runtime ACW(s) (notably UncaughtExceptionMarshaler) are kept by R8 and appear in classes.dex.
Changes:
- Add a new build test asserting
UncaughtExceptionMarshaleris present inclasses.dexfor NativeAOT + trimmable typemap + R8. - Extend
_GenerateTrimmableTypeMapinputs to include an extra framework assembly item list for runtime-only assemblies. - Produce + package a reference assembly for
Microsoft.Android.Runtime.NativeAOTundertools/typemap-refs, and wire it into the NativeAOT trimmable typemap build.
Show a summary per file
| File | Description |
|---|---|
| src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs | Adds regression test verifying the runtime ACW class is retained in classes.dex. |
| src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets | Feeds additional runtime-only framework assemblies into typemap generation inputs. |
| src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.NativeAOT.targets | Injects the NativeAOT runtime host ref assembly into typemap inputs; classifies its per-assembly typemap DLL as framework for ILC. |
| src/Microsoft.Android.Runtime.NativeAOT/Microsoft.Android.Runtime.NativeAOT.csproj | Enables ProduceReferenceAssembly and copies the ref assembly into the SDK pack’s typemap-refs staging area. |
| build-tools/create-packs/Microsoft.Android.Sdk.proj | Packages the NativeAOT runtime host reference assembly under tools/typemap-refs. |
Copilot's findings
- Files reviewed: 5/5 changed files
- Comments generated: 2
Comment on lines
+94
to
+100
| <PropertyGroup> | ||
| <_NativeAotRuntimeHostRefAssembly>$([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)..\tools\typemap-refs\Microsoft.Android.Runtime.NativeAOT.dll'))</_NativeAotRuntimeHostRefAssembly> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <_AndroidTrimmableTypeMapExtraFrameworkAssembly Include="$(_NativeAotRuntimeHostRefAssembly)" | ||
| Condition=" Exists('$(_NativeAotRuntimeHostRefAssembly)') " /> | ||
| </ItemGroup> |
Comment on lines
+101
to
+103
| <Warning Condition=" !Exists('$(_NativeAotRuntimeHostRefAssembly)') " | ||
| Text="The NativeAOT runtime host reference assembly was not found at '$(_NativeAotRuntimeHostRefAssembly)'; runtime Java Callable Wrappers (e.g. UncaughtExceptionMarshaler) may be missing from the typemap." /> | ||
| </Target> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Under the trimmable typemap on NativeAOT, the runtime host assembly
Microsoft.Android.Runtime.NativeAOT— and therefore its only Java Callable Wrapper type,UncaughtExceptionMarshaler— was never scanned by the typemap generator. Its JCW, typemap entry, and acw-map entry were all missing, so R8 had nothing to keep and the ACW was absent fromclasses.dex, crashing the app at startup inJavaInteropRuntime.init(setDefaultUncaughtExceptionHandler).Root cause
_GenerateTrimmableTypeMapruns only in the RID-independent outer build over@(ReferencePath), which contains just the compile closure (app + references + ref-pack framework assemblies).Microsoft.Android.Runtime.NativeAOTis a runtime-only assembly with no reference-assembly counterpart; it is resolved only in the per-RID inner build as a RID-specific runtime-pack asset (ResolveRuntimePackAssetsrequires a singleRuntimeIdentifier). So it never enters the generator's input set.Fix
Its managed metadata is RID-independent (byte-identical across RIDs), so:
Microsoft.Android.Runtime.NativeAOT(ProduceReferenceAssembly).Microsoft.Android.Sdkpack undertools/typemap-refs— not in theMicrosoft.Android.Reftargeting pack, because files under a targeting pack'sref/folder must all be classified in its FrameworkList and would become universal framework references for every Android app. Shipping it in the SDK pack keeps it a build-time-only input for the trimmable NativeAOT path._Microsoft.Android.Runtime.NativeAOT.TypeMap) as framework for ILC, so it stays anIlcReferencebut is removed from the unmanaged-entrypoint roots (matchingMono.Android/Java.Interop); its JCW native methods register via the runtimeregisterNativespath.This is a general fix for any runtime-only assembly with Java peers, not just
UncaughtExceptionMarshaler.Test
Adds
Build_WithTrimmableTypeMap_KeepsNativeAotRuntimeHostAcws, which opts into the trimmable typemap on NativeAOT and assertsclasses.dexretains theUncaughtExceptionMarshalerruntime ACW.Notes
The trimmable typemap is opt-in on
main(_AndroidTypeMapImplementation=trimmable); this fix is dormant otherwise. It is extracted from the "make trimmable the NativeAOT default" work so it can be reviewed and merged independently.