Skip to content

[Java.Interop] Retire obsolete expression-marshaling contracts - #13058

Merged
simonrozsival merged 2 commits into
mainfrom
simonrozsival-expression-marshalling-retirement
Oct 10, 2026
Merged

simonrozsival merged 2 commits into
mainfrom
simonrozsival-expression-marshalling-retirement

Conversation

@simonrozsival

@simonrozsival simonrozsival commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Summary

Follow up on #13030 by retiring the remaining obsolete expression-marshaling and activation contracts. This is an intentional .NET 12 source/binary API break, without compatibility stubs. Supported Android interop uses generated callbacks and trimmable typemap activation; source searches found no consumer of the removed expression pipeline or ActivatePeer.

Remove:

  • JniRuntime.JniMarshalMemberBuilder, its members, the runtime MarshalMemberBuilder property, and CreationOptions.MarshalMemberBuilder / UseMarshalMemberBuilder.
  • Java.Interop.Expressions.JniValueMarshalerContext, the three expression-generation hooks on JniValueMarshaler, and their expression-only helpers and primitive/string/array overrides.
  • The expression-only ColorValueMarshaler and internal IJavaObjectValueMarshaler, including their referencing attributes.
  • The unused JniValueManager.ActivatePeer contract and its throwing Android/sample overrides.

Retain non-expression JniValueMarshaler / argument-state conversion, primitive/string/array functionality, CreateLocalObjectReferenceArgument, peer/reference ownership, generated callbacks, and trimmable activation. Keep GetRuntimeType, JniInstanceMethods allocation helpers, and unrelated public JNI/type-signature APIs unchanged.

Update both T4 inputs and checked-in generated files. Remove stale DynamicallyAccessedMembers requirements from all eight primitive-array CreateMarshaledValue methods: their remaining conversion path constructs a known wrapper and uses type equality/assignability, not reflective activation of the requested target type. Record contract removals through PublicAPI.Unshipped entries and the existing Mono.Android ApiCompat baseline mechanism. Replace the obsolete architecture proposal with current behavior and add migration notes. No new tests are included, per request.

Validation

All SDK/runtime validation uses this branch's own Release build and repository-pinned SDK 12.0.100-alpha.1.26480.102, not sibling outputs. Framework paths/parameters below reflect the current checkout's build configuration; this change is intended for .NET 12.

Command Result
make prepare CONFIGURATION=Release Passed.
./dotnet-local.sh build build-tools/scripts/Prepare.proj -t:BuildExtraApiLevels -c Release -v minimal Passed; built API 37.1/37.2 and ran their API gates.
./dotnet-local.sh build build-tools/create-packs/Microsoft.Android.Sdk.proj -t:ConfigureLocalWorkload -c Release -v minimal Passed.
make all CONFIGURATION=Release Passed after completing additional-API bootstrap; initial workload setup lacked the 37.1 reference assembly.
dotnet build external/Java.Interop/src/Java.Interop/Java.Interop.csproj -c Release -v minimal -p:DotNetTargetFrameworkVersion=11.0 Passed, including public API analyzer: zero warnings/errors.
./dotnet-local.sh build src/Mono.Android/Mono.Android.csproj -c Release -v minimal -p:DisableApiCompatibilityCheck=False -m:1 Passed, including stable reference ApiCompat; existing compiler/API-baseline warnings remain.
dotnet test external/Java.Interop/tests/generator-Tests/generator-Tests.csproj -c Release -v minimal 478 passed, zero failures/skips.
dotnet test external/Java.Interop/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers-Tests.csproj -c Release -v minimal 50 passed, zero failures/skips.

Host integration command:

./dotnet-local.sh test bin/TestRelease/net10.0/Xamarin.Android.Build.Tests.dll \
  --filter 'FullyQualifiedName~Build_WithTrimmableTypeMap_Succeeds|FullyQualifiedName~Build_TrimmableTypeMap_WithCollectionExport_Succeeds|FullyQualifiedName~Build_TrimmableTypeMap_UsesMonoAndroidImplementationMetadata|FullyQualifiedName~BindingCallbackFormatSupportsDefaultConsumer' \
  --logger 'trx;LogFileName=expression-retirement-host.trx' -v minimal

Passed: 19/19, covering CoreCLR/NativeAOT typemap builds, generated callbacks, collection exports, and implementation metadata.

Device install commands, on exclusively leased emulator-5554 (arm64, API 36):

ANDROID_SERIAL=emulator-5554 ./dotnet-local.sh build -t:Install \
  tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj \
  -c Release -p:PublishAot=false -p:RuntimeIdentifier=android-arm64 \
  '-p:AdbTarget=-s emulator-5554' -m:1 -v minimal

ANDROID_SERIAL=emulator-5554 ./dotnet-local.sh build -t:Install \
  tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj \
  -c Release -p:PublishAot=true -p:RuntimeIdentifier=android-arm64 \
  '-p:AdbTarget=-s emulator-5554' -m:1 -v minimal

Both installs passed. Each install was followed by its matching command below, from tests/Mono.Android-Tests/Mono.Android-Tests:

ANDROID_SERIAL=emulator-5554 ../../../dotnet-local.sh test Mono.Android.NET-Tests.csproj \
  --no-build -c Release -p:PublishAot=false -p:RuntimeIdentifier=android-arm64 \
  '-p:AdbTarget=-s emulator-5554' --report-trx \
  --results-directory ../../../bin/TestRelease/TestResults/expression-retirement-coreclr

ANDROID_SERIAL=emulator-5554 ../../../dotnet-local.sh test Mono.Android.NET-Tests.csproj \
  --no-build -c Release -p:PublishAot=true -p:RuntimeIdentifier=android-arm64 \
  '-p:AdbTarget=-s emulator-5554' --report-trx \
  --results-directory ../../../bin/TestRelease/TestResults/expression-retirement-nativeaot

CoreCLR: 924 passed, 7 existing skips, zero failures. NativeAOT: 869 passed, 7 existing skips, zero failures. TRX results confirm JNI/primitive and object arrays, constructors, callbacks/exports, exceptions, peer control blocks, and reference ownership, including all 38 constructor activation tests and 58 transferred-reference tests on both runtimes. Only the test package was uninstalled afterward.

Both T4 templates regenerate the checked-in files exactly; git diff --check passes.

Review follow-up

The runtime/SDK test results above were collected for commit 1251576d3d98f093ca1879314e12f7c97771091f. Follow-up commit ce74deda0408d0b8268adc8437d07f66a5d6627c only removes the stale primitive-array trimming annotations and their unused constant.

./dotnet-local.sh build external/Java.Interop/src/Java.Interop/Java.Interop.csproj -c Release -v minimal -p:DotNetTargetFrameworkVersion=11.0 -p:EnableTrimAnalyzer=true -m:1 passed with zero warnings/errors, including API and trimming analyzers. Regeneration matches exactly. IL comparison confirmed all 1,333 emitted method bodies remain identical after normalizing RVA addresses and compiler-generated iterator numbering; all eight public conversion APIs remain and their stale parameter attributes are absent. Full SDK/device suites were not rerun for this metadata-only follow-up.

Standalone NativeAOT sample

./dotnet-local.sh build external/Java.Interop/samples/Hello-NativeAOTFromJNI/Hello-NativeAOTFromJNI.csproj -c Release -r osx-arm64 -v minimal -p:DotNetTargetFrameworkVersion=11.0 -p:RestoreConfigFile="$PWD/NuGet.Config" -m:1 passes. Running the compiled NativeAOT library from Java passes and returns Hello from .NET NativeAOT!.

Optional publish remains failed with MSB3030 in the unchanged _NativeLibRequiresLibPrefix target: it expects Hello-NativeAOTFromJNI.dylib, but the pinned SDK emits libHello-NativeAOTFromJNI.dylib. This is not claimed to be baseline-proven; no unrelated sample naming-target fix is included. The sample build and actual JNI execution validate the changed override removal.


Pull Request
title and
description
should follow the
commit-messages.md workflow documentation, and in particular should include:

  • Useful description of why the change is necessary.
  • Links to issues fixed
  • Unit tests

No issue is closed. Existing unit/integration/runtime coverage was run; no new tests were added.

Retire the remaining expression-marshaling and activation contracts after
JavaInterop1 removal.  The supported Android runtime uses generated
callbacks and trimmable typemap activation; it does not consume this
pipeline or JniValueManager.ActivatePeer.

Make the intentional .NET 12 API break without compatibility stubs.
Remove expression-only Color/IJavaObject marshalers and their attributes,
while retaining ordinary value conversion, array helpers, JNI argument
states, and supported peer/reference ownership.

Update T4 inputs and generated sources together, record shipped API
removals and accepted Mono.Android breakages, and replace the obsolete
architecture proposal with current behavior and migration guidance.
Keep array trimming annotations consistent across primitive types.

Validate the own Release SDK/API gates, 19 host integration tests, 478
generator tests, 50 JCW tests, and complete default device suites:
CoreCLR 924 passed/7 skipped; NativeAOT 869 passed/7 skipped, no failures.
Do not add new tests, per user request.

Co-authored-by: Copilot App <[email protected]>
Copilot AI balanced review requested due to automatic review settings October 9, 2026 23:04

Copilot AI 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.

🟡 Changes recommended

The generated primitive-array APIs introduce unnecessary public trimming requirements after their expression consumers are removed.

1 open finding
What changed in this PR

Retires obsolete Java.Interop expression-marshaling and peer-activation APIs for .NET 12.

Changes:

  • Removes expression-marshaling builders, hooks, overrides, and activation contracts.
  • Updates generated primitive-array code and API compatibility baselines.
  • Replaces obsolete architecture guidance and adds migration notes.
File Description
tests/​api-compatibility/​acceptable-breakages-vReference-net11.0.txt Records Mono.Android compatibility breaks.
src/​Mono.Android/​PublicAPI/​API-37/​PublicAPI.Unshipped.txt Records Color marshaler removal.
src/​Mono.Android/​PublicAPI/​API-37.2/​PublicAPI.Unshipped.txt Records API 37.2 removals.
src/​Mono.Android/​PublicAPI/​API-37.1/​PublicAPI.Unshipped.txt Records API 37.1 removals.
src/​Mono.Android/​PublicAPI/​API-36/​PublicAPI.Unshipped.txt Records API 36 removals.
src/​Mono.Android/​PublicAPI/​API-36.1/​PublicAPI.Unshipped.txt Records API 36.1 removals.
src/​Mono.Android/​PublicAPI/​API-35/​PublicAPI.Unshipped.txt Records API 35 removals.
src/​Mono.Android/​Mono.Android.csproj Removes the deleted marshaler source.
src/​Mono.Android/​Microsoft.Android.Runtime/​TrimmableTypeMapValueManager.cs Removes the obsolete activation override.
src/​Mono.Android/​Android.Runtime/​IJavaObjectValueMarshaler.cs Deletes the expression-only marshaler.
src/​Mono.Android/​Android.Runtime/​IJavaObject.cs Removes its marshaler attribute.
src/​Mono.Android/​Android.Graphics/​Color.cs Removes ColorValueMarshaler.
external/​Java.Interop/​src/​Java.Interop/​PublicAPI.Unshipped.txt Records Java.Interop API removals.
external/​Java.Interop/​src/​Java.Interop/​Java.Interop/​JniValueMarshaler.cs Removes expression-generation contracts.
external/​Java.Interop/​src/​Java.Interop/​Java.Interop/​JniStringValueMarshaler.cs Removes string expression overrides.
external/​Java.Interop/​src/​Java.Interop/​Java.Interop/​JniRuntime.JniValueManager.cs Removes ActivatePeer.
external/​Java.Interop/​src/​Java.Interop/​Java.Interop/​JniRuntime.JniMarshalMemberBuilder.cs Deletes the obsolete builder API.
external/​Java.Interop/​src/​Java.Interop/​Java.Interop/​JniBuiltinMarshalers.tt Updates built-in marshaler template.
external/​Java.Interop/​src/​Java.Interop/​Java.Interop/​JniBuiltinMarshalers.cs Regenerates built-in marshalers.
external/​Java.Interop/​src/​Java.Interop/​Java.Interop/​JavaPrimitiveArrays.tt Updates primitive-array template.
external/​Java.Interop/​src/​Java.Interop/​Java.Interop/​JavaPrimitiveArrays.cs Regenerates primitive arrays.
external/​Java.Interop/​src/​Java.Interop/​Java.Interop/​JavaArray.cs Adds shared trimming metadata.
external/​Java.Interop/​src/​Java.Interop/​GlobalSuppressions.cs Removes a stale suppression.
external/​Java.Interop/​samples/​Hello-NativeAOTFromJNI/​JavaInteropRuntime.cs Removes the sample override.
external/​Java.Interop/​Documentation/​Architecture.md Documents the current architecture.
Documentation/​release-notes/​expression-marshalling-retirement.md Adds migration guidance.

🧠 Review effort: Balanced

Comment thread external/Java.Interop/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs Outdated
CreateMarshaledValue constructs its known primitive-array wrapper and
converts targetType through equality and assignability checks.  It does
not inspect or activate targetType constructors, so preserving every
constructor and interface is no longer required.

Remove the stale parameter annotations uniformly from all eight arrays,
update the T4 input, and delete the unused shared constant.  Keep the
public conversion APIs and their ownership behavior unchanged.

Validate the Release Java.Interop build with API and trimming analyzers.
Compare emitted IL: all method bodies remain unchanged after accounting
for relocated RVAs and compiler-generated iterator numbering.

Co-authored-by: Copilot App <[email protected]>
@simonrozsival

Copy link
Copy Markdown
Member Author

@dalexsoto review

@dalexsoto dalexsoto left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The complete correctness and integration review found no blocking regression in the intentional .NET 12 API retirement. Supported non-expression conversion, reference ownership, generated callbacks and typemap activation remain intact; the removal baselines and migration notes match the accepted break. The stale array trimming annotations are fixed, and both T4 inputs match their generated files exactly.

@simonrozsival
simonrozsival merged commit 3e79b1e into main Oct 10, 2026
42 checks passed
@simonrozsival
simonrozsival deleted the simonrozsival-expression-marshalling-retirement branch October 10, 2026 09:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants