[typemap] Validate exported Java fields - #12596
Open
simonrozsival wants to merge 5 commits into
Open
Conversation
Match legacy XA4205 and XA4208 validation in the trimmable scanner, before invalid field members can reach generated typemap or Java outputs. Extend semantic, build, and device coverage for valid static and instance ExportField behavior and classify legacy-unsupported field names. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review tier: Lite
Findings: 1
New issues introduced by this change (2)
| Severity | Finding |
|---|---|
src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs — ❌ error — The typemap output assertion only checks for *.TypeMap.dll, but trimmable typemap… |
|
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs — 💡 suggestion — ValidateExportField() introduces an extra per-method custom-attribute scan and… |
What changed in this PR
This PR tightens trimmable typemap parity with the legacy [ExportField] pipeline by validating invalid exported-field initializer shapes early (parameterized/void), emitting the corresponding XA4205/XA4208 diagnostics, and ensuring the generator stops before writing partial typemap/Java outputs. It also expands the Java-source semantic parity fixture and adds new host/device coverage for static/instance/protected exported fields.
Changes:
- Add
[ExportField]signature validation in the trimmable scanner and surface XA4205/XA4208 through the shared logger/task plumbing. - Extend generator + integration fixtures/tests to assert “no partial Java fields / marshal methods” for invalid
[ExportField]shapes and to validate ordered field semantics (static/instance/protected). - Add on-device coverage for instance primitive
[ExportField]initialization visibility.
| File | Description |
|---|---|
| tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/ExportTests.cs | Adds a device test validating instance primitive exported-field initialization is visible via JNI. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.cs | Introduces invalid [ExportField] fixture types (parameterized + void return) for regression testing. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TrimmableTypeMapGeneratorTests.cs | Adds generator-level assertions for legacy XA4205/XA4208 diagnostics and absence of partial Java/typemap members. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/ExportFieldTests.cs | Adds focused unit coverage ensuring invalid export fields produce no Java field or marshal wrapper. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.IntegrationTests/JavaSourceSemanticParityTests.cs | Extends the semantic parity fixture to cover field ordering/modifiers and adds a Java consumer for reads. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.IntegrationTests/JavaSourceParityFixture/JavaSourceParityTypes.cs | Adds a protected exported field to increase fixture coverage for visibility and instance fields. |
| src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs | Adds build-time coverage asserting invalid [ExportField] stops before writing typemap/Java outputs and matches legacy diagnostics. |
| src/Xamarin.Android.Build.Tasks/Tasks/GenerateTrimmableTypeMap.cs | Plumbs XA4205/XA4208 into the MSBuild logger using localized Java.Interop resources. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs | Adds scanner-side validation to reject parameterized/void [ExportField] initializers before member emission. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/ITrimmableTypeMapLogger.cs | Extends the shared logger interface with new ExportField-specific diagnostic hooks. |
| } | ||
|
|
||
| Assert.IsEmpty ( | ||
| Directory.GetFiles (typemapDirectory, "*.TypeMap.dll", SearchOption.TopDirectoryOnly), |
Comment on lines
656
to
662
| if (!ValidateExportField (methodDef, index)) { | ||
| continue; | ||
| } | ||
|
|
||
| // Check for [ExportField] — produces both a marshal method AND a field | ||
| CollectExportField (methodDef, index, fields); | ||
|
|
Prove that parameter-count validation takes precedence over void-return validation for an initializer violating both rules, matching measured llvm-ir behavior across trimmable CoreCLR and NativeAOT without partial outputs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Match legacy XA4207 precedence for ExportField methods declared on generic types before parameter-count or void-return validation, and reject them before typemap or Java outputs are written. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Cover plural root and per-assembly typemap DLLs plus trimmable and llvm-ir Java source locations when asserting that invalid ExportField metadata produced no partial outputs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Ignore unrelated attributes that share the ExportFieldAttribute simple name by matching the Java.Interop namespace consistently during validation, field collection, and marshal-method registration. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This was referenced Aug 31, 2026
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
[ExportField]initializer validation in the trimmable scanner with localized XA4205/XA4207/XA4208 diagnosticsJava.Interop.ExportFieldAttributeby full identity during validation, field collection, and registration parsingTDD evidence
Before the fix, parameterized and void
[ExportField]initializers failed with XA4205/XA4208 under llvm-ir and trimmable CoreCLR. Trimmable NativeAOT instead emitted_UnnamedProject.TypeMap.dllplus invalidValidationPeer.java, then failed later in javac because the initializer was called with no arguments or because it emitted avoidfield.After the fix, llvm-ir CoreCLR, trimmable CoreCLR, and trimmable NativeAOT all report the matching XA diagnostic without writing typemap assemblies or partial Java field output.
A review suggested that a parameterized initializer returning
voidshould report both codes. Execution disproved that premise: legacyDiagnostic.Errorthrows on XA4205, so llvm-ir never reaches XA4208. The combined-invalid regression proves XA4205 is present, XA4208 is absent, and no outputs are written for llvm-ir CoreCLR, trimmable CoreCLR, and trimmable NativeAOT.Generic declaring types exposed a separate parity gap. LLVM-ir reports XA4207 before inspecting the initializer signature. Before the fix, trimmable CoreCLR could write typemap/Java outputs before a later linker XA4207, parameter/void shapes reported XA4205/XA4208 first, and NativeAOT lacked XA4207. Trimmable now reports localized XA4207 first for valid, parameterized, void, and combined generic initializers in both runtimes, without partial outputs.
Simple-name matching also treated unrelated user-defined
ExportFieldAttributetypes asJava.Interop.ExportFieldAttribute. The scanner now uses full namespace/name matching consistently at all three in-scope paths. A signature-invalid lookalike produces no field, marshal method, or diagnostic, while the real attribute remains recognized.Duplicate and invalid Java field names remain javac failures in all three configurations, matching llvm-ir; no trimmable-only diagnostic was introduced. Arbitrary managed return types remain deferred to the general unsupported-export-signatures layer because llvm-ir does not support that shape either.
Validation
Microsoft.Android.Sdk.TrimmableTypeMap.TeststestsMicrosoft.Android.Sdk.TrimmableTypeMap.IntegrationTeststests, including classfile semantic parity and javacGenerateExportedMembers_Microsoft.Android.TypeMaps.dll, trimmable Java, and llvm-ir Javagit diff --check; every added line is at most 180 charactersStacked on #12585. Tracks #12561 and #11289.