[TrimmableTypeMap] Java peer scanner core#10823
Conversation
b6895af to
cf78bbe
Compare
There was a problem hiding this comment.
Pull request overview
Adds the core System.Reflection.Metadata-based Java peer scanning pipeline for the new Microsoft.Android.Sdk.TrimmableTypeMap feature area. This introduces a new netstandard2.0 project responsible for indexing assemblies and producing a JavaPeerInfo model for downstream generators without using Cecil.
Changes:
- Introduces the
Microsoft.Android.Sdk.TrimmableTypeMapproject and supporting polyfills/extensions for nullable + modern C# features onnetstandard2.0. - Adds phase-1
AssemblyIndex(per-assembly metadata indexing) and phase-2JavaPeerScanner(cross-assembly analysis) to discover Java peer types and marshal methods. - Adds minimal SRM type providers (
SignatureTypeProvider,CustomAttributeTypeProvider) and the core data model (JavaPeerInfo,MarshalMethodInfo, activation ctor metadata).
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/SignatureTypeProvider.cs | SRM signature decoder producing managed type-name strings. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs | Main scanner pipeline: builds indices, resolves peers, computes JNI names, collects marshal methods, resolves activation ctors. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerInfo.cs | Data model for discovered peers, marshal methods, and activation constructor info. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/CustomAttributeTypeProvider.cs | SRM custom-attribute decoder with enum underlying-type support and nested type handling. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/AssemblyIndex.cs | Per-assembly indexer for type names, [Register] info, and component attribute metadata. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/NullableExtensions.cs | NRT-friendly IsNullOrEmpty/IsNullOrWhiteSpace helpers for netstandard2.0. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Microsoft.Android.Sdk.TrimmableTypeMap.csproj | New netstandard2.0 project wiring + package references + InternalsVisibleTo for tests. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/CompilerFeaturePolyfills.cs | Polyfills for init/required-member compiler features on netstandard2.0. |
| eng/Versions.props | Adds a pinned package version property for System.Reflection.Metadata. |
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs
Outdated
Show resolved
Hide resolved
src-ThirdParty/System.Runtime.CompilerServices/CompilerFeaturePolyfills.cs
Show resolved
Hide resolved
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/AssemblyIndex.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs
Outdated
Show resolved
Hide resolved
1520328 to
265b874
Compare
Add the TrimmableTypeMap scanner project (netstandard2.0) with: - JavaPeerInfo, MarshalMethodInfo, ActivationCtorInfo records — the core data model representing Java peer types discovered in assemblies - SignatureTypeProvider — decodes method signatures from metadata to extract parameter types for marshal method and activation constructor matching - CustomAttributeTypeProvider — decodes custom attribute arguments with lazy enum type caching and correct nested type resolution - CompilerFeaturePolyfills — netstandard2.0 shims for required/init - NullableExtensions — IsNullOrEmpty/IsNullOrWhiteSpace helpers - System.Reflection.Metadata 11.0.0-preview.1 package reference Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add AssemblyIndex — the first phase of the scanner pipeline that reads a single assembly and indexes all Java peer metadata: - Discovers [Register], [Export] attributes on types and methods - Builds RegisterInfo/ExportInfo records from custom attribute blobs - Resolves TypeAttributeInfo for component attributes ([Activity], [Service], [BroadcastReceiver], [ContentProvider]) including their JNI name properties - Maps type definitions to their Java peer registration data for downstream consumption by JavaPeerScanner Key design: uses System.Reflection.Metadata directly (no Cecil) and produces immutable record types. The index is per-assembly so scanning can be parallelized across the app closure. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add JavaPeerScanner — the second phase that consumes AssemblyIndex results and produces the final list of JavaPeerInfo for the app: - Walks all indexed types and resolves their Java peer registrations - Handles inheritance: traverses base types across assemblies to find the nearest registered Java peer ancestor - Detects activation constructors (IntPtr+JniHandleOwnership) and distinguishes between direct declarations and inherited ones - Collects marshal methods ([Register] on methods) and exported methods ([Export]) with their JNI signatures - Merges component attribute metadata ([Activity], etc.) and resolves JNI names from attribute properties - Flags types for unconditional preservation when component attributes with non-default JNI names are present The scanner is designed as a pure function: assemblies in → peer info out, with no side effects or global state. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
265b874 to
e709c94
Compare
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/AssemblyIndex.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/AssemblyIndex.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/AssemblyIndex.cs
Outdated
Show resolved
Hide resolved
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/CustomAttributeTypeProvider.cs
Outdated
Show resolved
Hide resolved
Address unresolved review comments in scanner core: - make AssemblyIndex.customAttributeTypeProvider private - simplify component Name parsing via TryGetTypeProperty path - merge duplicate named-argument helpers into a single generic TryGetNamedArgument<T>(..., out T) with notnull constraint - remove boolean-specific helper and reuse generic method - simplify enum cache assignment in CustomAttributeTypeProvider - route JavaPeerScanner attribute decoding through AssemblyIndex helpers Build verified for Microsoft.Android.Sdk.TrimmableTypeMap.csproj. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Apply PR10823-style simplifications across TrimmableTypeMap scanner code: - centralize namespace/nested type name joins in MetadataTypeNameResolver - reuse those helpers in JavaPeerScanner ResolveTypeReference - simplify AssemblyIndex register parsing by removing provider overload and decoding through a single path - reduce duplicated DecodeValue call sites via DecodeAttribute helper Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
src-ThirdParty/System.Runtime.CompilerServices/CompilerFeaturePolyfills.cs
Show resolved
Hide resolved
| static readonly HashSet<string> KnownComponentAttributes = new (StringComparer.Ordinal) { | ||
| "ActivityAttribute", | ||
| "ServiceAttribute", | ||
| "BroadcastReceiverAttribute", | ||
| "ContentProviderAttribute", | ||
| "ApplicationAttribute", | ||
| "InstrumentationAttribute", | ||
| }; |
There was a problem hiding this comment.
So, I believe there is an interface, Java.Interop.IJniNameProviderAttribute, that the old code was looking for? All these attributes implement it. I think the idea was if Android introduced a new attribute, it could work without updating this list?
There is probably not any customers using Java.Interop.IJniNameProviderAttribute, but do we need a code path that looks for it?
There was a problem hiding this comment.
I already had this implemented but it complicates things quite a lot. It's not trivial to check if a type implements an interface with SRM. For now, I chose to ignore it. It shouldn't have practical implications, but I agree that we shold eventually implement it for completeness. I'll make sure to add this to the issue for future work. Is that OK?
There was a problem hiding this comment.
So efficient way to implement this:
- only check custom attributes on Java.Lang.Object or Java.Lang.Throwable subclasses
- ignore attributes which don't have the
Namenamed argument - ignore well known attributes (Register, Application, Activity, ...)
There was a problem hiding this comment.
I updated the "Future work" section of #10788
Move CompilerFeaturePolyfills.cs from the TrimmableTypeMap project folder to src-ThirdParty/System.Runtime.CompilerServices and link it from the csproj via a Compile item, matching the repository pattern for shared polyfills. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/azp run Xamarin.Android-PR |
|
No pipelines are associated with this pull request. |
|
/azp run Xamarin.Android-PR |
|
No pipelines are associated with this pull request. |
|
Part of #10798 |
Sliced from #10805. Adds the core scanner pipeline for the TrimmableTypeMap feature — the component that reads .NET assemblies and discovers all Java peer types using
System.Reflection.Metadata.What this PR adds
New
Microsoft.Android.Sdk.TrimmableTypeMapproject (netstandard2.0, ~1500 lines) with three logical layers:1. Data model and metadata type providers
JavaPeerInforecord — represents a discovered Java peer with its JNI name, marshal methods, activation constructor info, and component attributesSignatureTypeProvider— decodes method signatures to extract parameter types (needed for marshal method and activation constructor matching)CustomAttributeTypeProvider— decodes[Register],[Export],[Activity]etc. attribute blobs; includes a lazy enum type cache and correct nested type resolution2. AssemblyIndex — per-assembly metadata indexer
First phase of the pipeline. Reads a single assembly and builds an index of:
[Register]/[Export]attributes on types and methods →RegisterInfo/ExportInforecords[Activity],[Service],[BroadcastReceiver],[ContentProvider]) with their JNI name propertiesDesigned for per-assembly parallelism — each assembly gets its own index.
3. JavaPeerScanner — execution logic
Second phase. Consumes all
AssemblyIndexresults and produces the finalJavaPeerInfolist:IntPtr+JniHandleOwnership)Pure function: assemblies in → peer info out, no side effects.
Review guide
Three commits, one per layer — review in order:
Scanner unit tests follow in #10813.