Skip to content

[WIP][TrimmableTypeMap] Decompose _GenerateJavaStubs: shared tasks for trimmable path#10810

Draft
simonrozsival wants to merge 12 commits intodev/simonrozsival/trimmable-typemap-03-generatorsfrom
dev/simonrozsival/trimmable-typemap-04-shared-tasks
Draft

[WIP][TrimmableTypeMap] Decompose _GenerateJavaStubs: shared tasks for trimmable path#10810
simonrozsival wants to merge 12 commits intodev/simonrozsival/trimmable-typemap-03-generatorsfrom
dev/simonrozsival/trimmable-typemap-04-shared-tasks

Conversation

@simonrozsival
Copy link
Member

Part of #10807
Stacked on #10808

Summary

Decomposes the monolithic _GenerateJavaStubs mega-target to extract shared tasks (ACW map generation, manifest generation, provider sources) that both the legacy LLVM IR path and the new trimmable typemap path need.

What's included

IManifestTypeInfo abstraction (~40 lines)

Interface decoupling manifest generation from Cecil. Implemented by both CecilManifestTypeInfoAdapter (legacy) and ScannerManifestTypeInfoAdapter (trimmable). Compiled into both projects via <Compile Include>.

ManifestXmlGenerator (~264 lines)

Generates AndroidManifest.xml XElements from ComponentAttributeInfo data without Cecil. Maps ~90 C# attribute properties to android:xxx XML attributes. Handles IntentFilter constructor args, data elements, layout, meta-data, property, and grant-uri-permission sub-elements.

ManifestDocument.Merge() dual-path

Modified to accept IReadOnlyList<IManifestTypeInfo> instead of List<TypeDefinition>. Cecil path casts to CecilManifestTypeInfoAdapter for the existing pipeline. Non-Cecil path uses ManifestXmlGenerator.

GenerateTrimmableJavaStubs MSBuild task (~141 lines)

Replaces GenerateJavaStubs + GenerateMainAndroidManifest for the trimmable path. Uses JavaPeerScanner for scanning, AcwMapWriter for ACW map, ScannerManifestTypeInfoAdapter for conversion.

Trimmable.targets wiring

Fully wired with GenerateTrimmableJavaStubs + GenerateAdditionalProviderSources. Stamp-based incrementality, _ManifestOutput, _MergedManifestDocuments, FileWrites.

Supporting types

  • AcwMapWriter — generates acw-map.txt from scanner results
  • UnconditionalMarker — marks types from customview-map and manifest as unconditional
  • ManifestTypeReader — reads android:name from merged manifest
  • CecilManifestTypeInfoAdapter — wraps Cecil TypeDefinitionIManifestTypeInfo

Tests (349 total, ~1k lines new)

  • ManifestXmlGenerator: 18 tests (all component types, intent filters, ctor args, sub-elements)
  • ManifestTypeReader: 5 tests
  • UnconditionalMarker: 7 tests
  • End-to-end: 11 tests (scan → convert → generate XML roundtrip)
  • AcwMapWriter: existing tests updated

Diff vs base branch

~1,059 lines added (production + tests for this issue only)

@simonrozsival simonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Feb 12, 2026
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-typemap-03-generators branch 4 times, most recently from 83a50be to 4e7d06c Compare February 13, 2026 17:12
Extract all typemap-related MSBuild targets from Xamarin.Android.Common.targets
and Microsoft.Android.Sdk.ILLink.targets into a dedicated
Xamarin.Android.TypeMap.Legacy.targets file, imported conditionally based on
_AndroidTypeMapImplementation. Create empty stub files for the trimmable typemap
implementation (Trimmable.targets, Trimmable.CoreCLR.targets,
Trimmable.NativeAOT.targets).

This is a pure refactoring — no new tasks, no new C# code, no behavioral changes.
When _AndroidTypeMapImplementation != 'trimmable' (the default), the legacy targets
are imported and the build pipeline is identical to before.

Changes:
- New: Xamarin.Android.TypeMap.Legacy.targets — all legacy typemap targets
  - _GenerateLegacyJavaCallableWrappers (JCW gen, ACW map, stubs, marshal methods)
  - _GenerateLegacyAndroidManifest (manifest + additional providers)
  - _GenerateLegacyTypeMappings (LLVM IR typemaps)
  - _SetLegacyTypemapProperties (_TypeMapKind)
  - _GetMonoPlatformJarPath (overrides empty stub)
  - _PrepareNativeAssemblySources (overrides empty stub)
  - _AddLegacyTypeManagerResources (TypeManager.java + mono.android.jar)
  - _RemoveRegisterAttribute (full version with RemoveRegisterAttribute task)
  - _CollectLegacyTypeMapFiles (FastDev archive)
- New: Xamarin.Android.TypeMap.Trimmable.targets — dispatcher + validation stub
- New: Xamarin.Android.TypeMap.Trimmable.CoreCLR.targets — empty stub
- New: Xamarin.Android.TypeMap.Trimmable.NativeAOT.targets — empty stub
- Modified: Common.targets — stripped of all legacy typemap task invocations,
  added empty stubs + conditional imports
- Modified: ILLink.targets — added conditions to skip legacy ILLink steps
  when _AndroidTypeMapImplementation == 'trimmable'

Fixes: #10779
…erateLegacyAndroidManifest

GenerateMainAndroidManifest unregisters NativeCodeGenState from the build
engine, so GenerateTypeMappings must retrieve it first. Add DependsOnTargets
to ensure correct ordering between the two AfterTargets targets.
Move _PrepareLinking, _FixRootAssembly, and _TouchAndroidLinkFlag from
Microsoft.Android.Sdk.ILLink.targets into Xamarin.Android.TypeMap.Legacy.targets.
This eliminates scattered _AndroidTypeMapImplementation != 'trimmable' conditions
in ILLink.targets — the conditions are now implicit since Legacy.targets is only
imported when _AndroidTypeMapImplementation is not 'trimmable'.
Address PR feedback:
- Rename Xamarin.Android.TypeMap.*.targets -> Microsoft.Android.Sdk.TypeMap.*.targets
- Move files to Microsoft.Android.Sdk/targets/ (new file convention)
- Remove @(None) entries (handled by _CopyNetSdkTargets wildcard)
- Delete Microsoft.Android.Sdk.ILLink.targets (moved _LinkAssemblies to Common.targets)
- Add _ValidateAndroidTypeMapImplementation target
- Add _GenerateJavaStubs stub in Trimmable.targets
- Fix UsingTask references to use $(_XamarinAndroidBuildTasksAssembly)
- Fix ILLink DLL paths for targets/ directory context
…e stub

- Restore missing TypeMappingStep ILLink custom step for managed typemap
  implementation (used by NativeAOT)
- Add proper DependsOnTargets, Inputs/Outputs, and stamp file to the
  trimmable _GenerateJavaStubs stub for incremental build support
- Add TODO comment about manifest extraction (#10807): both
  GenerateMainAndroidManifest and GenerateAdditionalProviderSources
  depend on NativeCodeGenState and need to be decoupled before they
  can be shared across typemap paths
Introduce Microsoft.Android.Build.TypeMap assembly with a two-phase
scanner that extracts Java peer type information from .NET assemblies
using SRM (no Mono.Cecil dependency).

Key components:
- JavaPeerScanner: scans assemblies for [Register], [Export], and
  component attributes to build JavaPeerInfo entries
- AssemblyIndex: per-assembly O(1) lookup index built in phase 1
- JavaPeerInfo/MarshalMethodInfo: rich data model for downstream
  generators (PR2/PR3)
- CRC64 package name computation via System.IO.Hashing

Tests:
- 62 xUnit tests covering all scanner code paths
- 3 integration tests comparing side-by-side with legacy Cecil-based
  scanner on Mono.Android.dll (all pass)
- Rename project from Microsoft.Android.Build.TypeMap to Microsoft.Android.Sdk.TrimmableTypeMap
  to match naming of Microsoft.Android.Sdk.ILLink and Microsoft.Android.Sdk.Analysis
- Fix ParseJniType crash on malformed JNI signatures
- Add cycle detection to ExtendsJavaPeer
- Fix SignatureTypeProvider nested type resolution
- Implement GetUnderlyingEnumType via value__ field inspection
- Implement TryGetTypeProperty for BackupAgent and ManageSpaceActivity
- Fix InternalsVisibleTo: remove AssemblyName hack, add proper entries
- Add 7 ParseJniSignature unit tests
…rom [Application]

Types referenced by [Application(BackupAgent = typeof(X))] or
[Application(ManageSpaceActivity = typeof(X))] are now forced unconditional
in Phase 3 of the scan, since the manifest will reference them regardless
of whether anything else in managed code does.

Added 3 unit tests, removed the skipped test placeholder.
…overage

Review-driven fixes:

Bug fixes:
- Fix critical bug: constructor JNI signatures were hardcoded to "()V"
  in BuildNativeRegistrations. Now propagated from JavaConstructorInfo
  through UcoConstructorData.JniSignature to NativeRegistrationData.
- Fix non-deterministic alias ordering: replaced Dictionary with
  SortedDictionary, sort alias peers by ManagedTypeName.
- Fix Export attribute ThrownNames parsing: string[] was not being
  decoded from ImmutableArray<CustomAttributeTypedArgument<string>>.

Test improvements:
- Cache scanner results with static Lazy<> in all test classes
- Add parameterized constructor JNI signature test
- Add fixture-based CustomView constructor signature assertions
- Add PE blob validation tests (2-arg vs 3-arg TypeMap attributes)
- Add determinism test (same input → same output)
- Add Export with throws clause test fixture + JCW test
- Fix Build_CreatesOneEntryPerPeer for alphabetical ordering

Code quality:
- Add ECMA-335 comment explaining Type args as serialized strings
- Add comment explaining UCO constructor 2-param signature
- Fix doc comment: remove 'Intermediate representation' wording

215 tests pass, 1 skipped.
…support

- Invoker types no longer get their own proxy types or TypeMap entries.
  They are only referenced as a TypeRef in the interface proxy's
  get_InvokerType property and CreateInstance method.
- Invoker detection now uses explicit relationship from [Register] third
  argument (InvokerTypeName) instead of name-based heuristic.
- Interface proxy CreateInstance creates the invoker type, not the
  interface itself.
- Added dotnetVersion parameter to TypeMapAssemblyEmitter,
  TypeMapAssemblyGenerator, and RootTypeMapAssemblyGenerator constructors
  (will be passed from $(DotNetTargetVersion) MSBuild property later).
- JCW generator now uses SuperArgumentsString for [Export] constructor
  super() calls instead of always forwarding all parameters.
- Documented PE-reading test helpers with clear comments explaining
  the approach and its limitations.
Implement core building blocks for issue #10807:

Phase 1: ACW map generation
- AcwMapWriter: generates acw-map.txt from scanner results with
  XA4214/XA4215 conflict detection
- TrimmableTypeMapGenerator: per-assembly pipeline orchestrator
  (scan → TypeMap → JCW → ACW map → component data)

Phase 2: IManifestTypeInfo abstraction
- IManifestTypeInfo interface: decouples manifest generation from Cecil
- ManifestTypeInfo: default mutable implementation
- ManifestComponentKind enum: Activity, Service, BroadcastReceiver,
  ContentProvider, Application, Instrumentation
- ComponentAttributeInfo: raw property dictionaries for attributes

Phase 3: Component attribute extraction
- ComponentAttributeExtractor: SRM-based extraction of component
  attributes ([Activity], [Service], etc.) and sub-attributes
  ([IntentFilter], [MetaData], [Layout], [Property], [GrantUriPermission])
- Integrated into JavaPeerScanner (ComponentData on JavaPeerInfo)
- ScannerManifestTypeInfoAdapter: converts scanner output to
  IManifestTypeInfo objects
- ComponentDataSerializer: text-based serialization for inter-target
  communication of component data

Tests: 346 tests (28 new) covering all new code:
- AcwMapWriterTests (14 tests)
- ManifestTypeInfoTests (12 tests)
- ComponentAttributeExtractorTests (19 tests)
- ScannerManifestTypeInfoAdapterTests (16 tests)
- EndToEndTests (10 tests)
- ComponentDataSerializerTests (19 tests)
- TrimmableTypeMapGeneratorTests (9 tests)
…apGenerator

Remove 1,280 lines of unnecessary code:
- ComponentDataSerializer (349 lines) + tests (512 lines): custom text
  serializer with no consumer — ScannerManifestTypeInfoAdapter already
  converts JavaPeerInfo → IManifestTypeInfo directly
- TrimmableTypeMapGenerator (118 lines) + tests (212 lines): pipeline
  orchestrator with no caller — individual pieces are independently tested

Also trim verbose doc comments and simplify null-coalescing in
ScannerManifestTypeInfoAdapter.

All 318 tests pass.
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-typemap-04-shared-tasks branch from 1459e93 to 01136d4 Compare February 13, 2026 17:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant