Skip to content

Explore ManagedPeer compatibility for trimmable typemap apps #11635

@simonrozsival

Description

@simonrozsival

Context

The trimmable typemap runtime intentionally removes net.dot.jni.ManagedPeer from java_runtime_trimmable.jar and sets Java.Interop.RuntimeFeature.ManagedPeerNativeRegistration=false. Trimmable-generated Java/proxy/native-registration code should use generated UCO/proxy paths such as mono.android.Runtime.registerNatives(Class) instead of the legacy dynamic Java.Interop bridge:

net.dot.jni.ManagedPeer.registerNativeMembers(Class<?> nativeClass, String methods)
net.dot.jni.ManagedPeer.construct(Object self, String constructorSignature, Object... args)

This is desirable for app size and trim/AOT safety, but old generated or handwritten Java bytecode can still reference ManagedPeer, which would fail in trimmable apps with NoClassDefFoundError.

Question

Should we add an explicit opt-in compatibility path for trimmable apps that still package Java bytecode referencing net.dot.jni.ManagedPeer?

Possible shape

Default behavior should stay strict:

  • Do not package ManagedPeer.class in trimmable apps.
  • Keep ManagedPeerNativeRegistration=false.
  • Ideally detect Java bytecode references to net/dot/jni/ManagedPeer at build time and fail with an actionable diagnostic.

Potential opt-in behavior, behind a property/feature switch, e.g. AndroidEnableManagedPeerCompatibility=true:

  1. Package a tiny trimmable-compatible net.dot.jni.ManagedPeer shim.
  2. registerNativeMembers(Class, String) logs a warning and remaps to generated trimmable registration, likely mono.android.Runtime.registerNatives(Class).
  3. construct(Object, String, Object...) probably remains unsupported and throws a clear error unless/until we add generated per-type constructor dispatch.
  4. Build-time detection could downgrade from error to warning when the opt-in is enabled.

Things to decide

  • Is a compatibility shim worth carrying at all, even behind opt-in?
  • Should registerNativeMembers(Class, String) ignore the legacy methods string and rely entirely on generated trimmable metadata?
  • Should construct(...) always fail, or should we eventually support it through generated dispatch metadata?
  • Where should detection happen: Java source scan, class/jar scan, dex scan, or a combination?
  • What property name and diagnostic code should we use?

Why this matters

We want to keep default trimmable apps free of the old dynamic bridge, but we also want a clear story for users who have older JavaInterop1-generated Java wrappers or handwritten Java copied from Java.Interop samples that still call ManagedPeer.*.

Metadata

Metadata

Assignees

Labels

needs-triageIssues that need to be assigned.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions