Skip to content

[Java.Interop] Use native UTF-8 JNI remaps - #12795

Open
simonrozsival wants to merge 2 commits into
simonrozsival-fix-r8-obfuscation-policyfrom
simonrozsival-optimize-jni-remapping
Open

simonrozsival wants to merge 2 commits into
simonrozsival-fix-r8-obfuscation-policyfrom
simonrozsival-optimize-jni-remapping

Conversation

@simonrozsival

@simonrozsival simonrozsival commented Sep 15, 2026

Copy link
Copy Markdown
Member

Motivation

R8 runtime remapping already stores replacement JNI type, method, field, and signature names as stable null-terminated UTF-8 strings in generated native data. Converting those names to managed UTF-16 strings and then encoding them back to UTF-8 for JNI adds avoidable allocations and work on every remapped member lookup.

Approach

  • Add UTF-8 pointer-backed replacement values while preserving the existing string properties through lazy decoding.
  • Add JniType class/member lookup paths that accept stable UTF-8 pointers and pass them directly to FindClass, GetMethodID, and GetFieldID.
  • Use the pointer path from JniPeerMembers when available, while retaining the existing string/span fallback for custom type managers and legacy remaps without generated target signatures.
  • Keep diagnostic string conversion lazy, so successful release lookups do not perform a UTF-8 → UTF-16 → UTF-8 round trip.

Stack

This is the foundational PR for #12796 and is based on the R8 runtime-remapping work in #12692.

Validation

  • Java.Interop JniPeerMembersTests: 23 passed, 1 skipped.
  • Java.Interop Debug and Release builds.
  • Exercised by the MonoVM, CoreCLR, and NativeAOT device coverage in the next PR.

Carry stable UTF-8 replacement pointers through remapping results and use them directly for class and member lookup, avoiding UTF-16 round trips.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

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.

Copilot review overview

🟡 Changes recommended

Unresolved remapping wiring, fast-path, diagnostic, and test-coverage issues remain.

Get a fresh assessment by requesting another Copilot review.

Review tier: Lite
Findings: 3 Medium severity · 1 Low severity

Open (4)
What changed in this PR

This pull request adds native UTF-8 pointer support for Java.Interop JNI remapping to reduce managed string conversions.

Changes:

  • Adds pointer-backed remapping metadata and lookup overloads.
  • Updates instance/static method and field remapping.
  • Updates the unshipped API baseline.
File Description
external/​Java.Interop/​src/​Java.Interop/​PublicAPI.Unshipped.txt Records new UTF-8 pointer APIs.
external/​Java.Interop/​src/​Java.Interop/​Java.Interop/​JniType.cs Adds native pointer lookup overloads.
external/​Java.Interop/​src/​Java.Interop/​Java.Interop/​JniRuntime.JniTypeManager.cs Adds pointer-backed replacement metadata.
external/​Java.Interop/​src/​Java.Interop/​Java.Interop/​JniPeerMembers.JniStaticMethods.cs Updates static method remapping.
external/​Java.Interop/​src/​Java.Interop/​Java.Interop/​JniPeerMembers.JniStaticFields.cs Updates static field remapping.
external/​Java.Interop/​src/​Java.Interop/​Java.Interop/​JniPeerMembers.JniInstanceMethods.cs Updates instance method and constructor remapping.
external/​Java.Interop/​src/​Java.Interop/​Java.Interop/​JniPeerMembers.JniInstanceFields.cs Updates instance field remapping.
external/​Java.Interop/​src/​Java.Interop/​Java.Interop/​JniPeerMembers.cs Adds shared remapping helper dispatch.

Comment thread external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.cs Outdated
Comment thread external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.cs Outdated
Use native pointers independently for replacement names and signatures, and report effective fallback values when a remapped member lookup fails.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsival

Copy link
Copy Markdown
Member Author

/review

@github-actions

github-actions Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

Generated by Android PR Reviewer for #12795

@github-actions github-actions Bot 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.

⚠️ Needs Changes

Findings: 0 errors · 2 warnings · 0 suggestions

The mixed UTF-8/string fallback handling added in the follow-up commit resolves the earlier all-or-nothing fast-path issue, and the JNI exception cleanup paths remain consistent with the existing implementation. Before merging, the new public raw-pointer contract should explicitly define its lifetime/ownership requirements, and the pointer-backed and mixed lookup branches need direct regression coverage.

CI is still in progress with no failures reported so far; Java.Interop tests have passed on macOS and Windows.

Generated by Android PR Reviewer for #12795 · copilot · gpt56 · 132.3 AIC · ⌖ 11.4 AIC · ⊞ 21K
Comment /review to run again

ReadOnlySpan<char> fallbackSignature,
[NotNullWhen (true)] out JniMethodInfo? method)
{
if (info.TargetJniMethodNameUtf8 != IntPtr.Zero) {

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.

🤖 ⚠️ Testing — None of the Java.Interop remapping tests populate a TargetJni*Utf8 property, so the pointer/pointer and both mixed pointer/span branches added here are untested; the original all-or-nothing fast-path bug compiled and passed the cited 23 tests for exactly this reason. Please add JniPeerMembersTests coverage using stable unmanaged UTF-8 storage for method and field remaps (instance/static), including an omitted target signature, and cover the constructor signature-pointer path as well.

Rule: Regression coverage for new branches

targetJniMethodSignatureUtf8 = IntPtr.Zero;
}
}
public IntPtr TargetJniTypeUtf8 {

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.

🤖 ⚠️ API design — These newly public IntPtr properties do not document the required encoding, NUL termination, ownership, or lifetime. Both the paired string getters and the JNI lookup path dereference/scan the pointer after GetReplacement*InfoCore() has returned, so a custom JniTypeManager can reasonably return a pointer backed by a temporary fixed block or stack buffer and leave a dangling pointer before it is consumed. Please define the stable-lifetime contract in XML docs for all six pointer properties (method and field), or keep the raw-pointer representation behind an internal API that can guarantee ownership.

Rule: Interop pointer lifetime

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.

2 participants