Skip to content

[TrimmableTypeMap] Track remaining feature-parity gaps #12561

Description

@simonrozsival

Summary

Track the currently known feature-parity gaps and high-risk untested behavior in the trimmable typemap implementation.

The goal is not to restore every legacy implementation detail. It is to identify behavior that applications and bindings reasonably depend on, decide the intended contract, and add explicit support or an actionable diagnostic.

This list is an investigation backlog. Items without an existing focused issue are not necessarily confirmed product defects.

Known regression that prompted this audit

The annotation regression showed that scanner and runtime coverage can remain green while the semantic contents of generated Java have changed.

Constructor and activation parity

Constructor discovery and ordinary Java-created activation now have substantial coverage, including primitives, strings, arrays, Context, IAttributeSet, exceptions, managed-first construction, and Java-first construction. The remaining risk is concentrated in less common activation paths:

  • Peer identity and replaceability during activation. (#12566)

    • Reentrant and synchronized concurrent constructor-time lookups preserve one final managed peer and exactly-once construction.
    • The unchanged fixture passes with llvm-ir, trimmable CoreCLR, and NativeAOT; no production fix was required.
  • Virtual callbacks from Java constructors. (#12566)

    • Added a replacement fixture using normal [Register] plus generated static/UCO registration, without unsupported dynamic native registration.
    • Peer identity, exactly-once construction, callback dispatch, concurrency, and exception propagation pass unchanged in all three configurations; no production fix was required.
  • Non-static Java inner classes — investigated; not an llvm-ir parity feature.

    • The llvm-ir path omits the nested derived JCW and Java activation fails with ClassNotFoundException, even with an explicit nested [Register].
    • A trimmable-only implementation was prototyped, but it will not be landed under the requirement that parity fixtures pass with llvm-ir first.
  • Constructor throws declarations. (#12564)

    • Confirmed trimmable typemap defect: [Export(Throws = ...)] metadata was parsed but dropped from generated Java constructors.
    • The unchanged constructor fixture passed with llvm-ir and failed with trimmable CoreCLR and NativeAOT before the fix; all three configurations pass after the fix.
  • Interface and abstract-type invoker activation. (#12565)

    • Confirmed trimmable typemap defect: JniTypeSignatureAttribute.InvokerType was ignored, allowing hierarchy fallback to select the wrong proxy.
    • Constructor-based XA, Java.Interop, inherited-interface, abstract-type, and repeated-lookup identity tests now pass with llvm-ir, trimmable CoreCLR, and NativeAOT.
    • Cross-assembly invokers are excluded: llvm-ir resolves invokers from the target assembly and does not support that shape.
  • Closed generic wrapper activation. (#12564)

    • Wrapping an existing Java object as a caller-supplied closed managed generic type was already supported; coverage now proves exactly-once activation and stable identity across all configurations.
    • Java-created open generic peers remain unsupported because Java cannot provide managed type arguments.
  • Ambiguous or unrepresentable constructor signatures. (#12567)

    • Added localized XA4259-XA4262 diagnostics for collapsed JNI signatures, unsupported parameter shapes, missing compatible Java base constructors, and invalid SuperArgumentsString expressions.
    • Validation stops before writing partial Java output and covers inferred, [Export], [Register], and [JniConstructorSignature] constructor forms.

JCW and Java-source parity

  • Duplicate Java wrapper names. (#12569)

    • Confirmed trimmable typemap defect: cross-assembly peers with the same generated JCW name silently overwrote one .java file while retaining duplicate/inconsistent ACW and typemap mappings.
    • The unchanged fixture fails with XA4215 under llvm-ir; trimmable now rejects the collision deterministically before writing typemap, Java, or ACW-map outputs.
  • Generated Java semantic parity. (#12573, #12608)

    • The shared llvm-ir/trimmable fixture compiles both generated source sets with javac and compares parsed classfile semantics for hierarchy, ordered interfaces, constructors, methods, fields, visibility/modifiers, and throws.
    • Runtime-retained annotation presence is independently verified in generated Java, DEX, and an end-to-end WebView JavaScript bridge.
    • The comparator has already exposed and regression-tested checked-exception and interface-declaration defects; future focused parity items should extend this semantic corpus rather than use raw source-string equality.
  • Redundant and colliding interface declarations. (#12585)

    • Confirmed trimmable typemap defect: redundant parent interfaces and two managed aliases for one Java interface produced implements Root, Derived, Derived, which fails javac with repeated interface.
    • Scanner-side most-derived filtering plus ordered Java-name deduplication now matches llvm-ir while preserving complete direct-interface metadata and alias associations.
    • Generic parent ancestry is handled by the type-definition algorithm; deeper covariant/bridge runtime cases remain future corpus expansion rather than blockers for this fix.
  • Unicode Java identifiers. (#12607)

    • Ordinary JCWs support the frozen OpenJDK 21/Unicode 15 portable identifier set, including BMP letters, currency symbols, and connector punctuation, with consistent source/classfile/ACW/typemap/DEX/JNI identity and three-runtime constructor activation.
    • Names must already be NFC; combining/format parts and canonically non-NFC names are rejected early to avoid normalization-sensitive path corruption. Nothing is normalized silently.
    • Supplementary identifiers are preserved in javac/classfile/DEX but cannot be loaded by Android FindClass in any tested runtime, so managed supplementary JCWs are rejected early. Currency/connector starts remain non-component-only because AAPT rejects them in manifest class names.
    • Validation is frozen to JDK 21 Unicode data rather than host .NET tables; newer Unicode assignments require an explicit table/toolchain update.
  • [ExportField] validation and runtime coverage. (#12596)

    • Confirmed NativeAOT gap: parameterized and void initializers could emit typemap/invalid Java output instead of legacy XA4205/XA4208; generic declaring types could bypass XA4207 or report later diagnostics first.
    • Trimmable validation now matches measured llvm-ir precedence and stops before writing root/per-assembly typemaps or Java output.
    • Classfile/javac coverage verifies ordered field declarations, type/modifiers, public/protected static and instance fields; device coverage verifies primitive/object and instance JNI reads across all three configurations.
    • Duplicate and invalid Java field names remain javac failures in llvm-ir and both trimmable runtimes; no trimmable-only diagnostic was introduced.
  • Unsupported export signatures. (#12597)

    • Confirmed NativeAOT defect: arbitrary managed types and other unsupported [Export]/[ExportField] signatures silently degraded to java.lang.Object, emitted typemap/Java/ACW-map outputs, or failed later with non-actionable errors.
    • Trimmable scanning now emits localized XA4263 before outputs for unresolved managed objects, generic method/non-Java instantiations, invalid special mappings, and function pointers, while preserving supported Java peers, interfaces, enums, strings, arrays, collections, and exact Stream/XmlReader mappings.
    • Exported constructors preserve metadata constructor identity, static constructors remain ignored, full attribute/type/assembly identity is validated, and assembly/type-forwarder resolution cannot borrow same-named peers, enums, or framework special types from another assembly.
    • Byref, pointer, rectangular-array, and nested structural constructor shapes remain owned by XA4260 in [typemap] Diagnose unsupported constructor shapes #12567; measured llvm-ir-supported array special mappings are classified without generating invalid trimmable dispatcher IL.

Runtime behavior

  • Java aliases and casts in realistic AppCompat/view inflation paths. (#12609)

    • A focused AppCompat XML-inflation app covers Toolbar/AppCompatImageButton most-derived lookup, repeated identity, a concrete same-JNI alias, ITintableBackgroundView JavaCast/JavaAs, managed-created peers, and caller-directed JavaList<string> wrapping.
    • The unchanged fixture passes under llvm-ir/CoreCLR, trimmable/CoreCLR, and trimmable/NativeAOT, so no production change was required and the focused historical mismatch is stale/not reproducible.
    • The three broad Xamarin.Forms 5.0 skips remain separately unclassified because that legacy package was unavailable in the offline test cache and those fixtures also combine bundle/debug behavior.
  • Static/default interface methods and desugaring. (#12610)

  • Plain managed-object proxy behavior. (#12612)

    • The supported direct contract now covers JavaObjectArray<object>, duplicate/null entries, repeated lookup identity, JavaList<object>, JavaDictionary<object,object>, nested collection round trips, disposal, and GC/Java-root lifetime across MonoVM, llvm-ir/CoreCLR, trimmable/CoreCLR, and NativeAOT.
    • Managed round trips preserve object reference identity/type/value. Two distinct managed-equal objects remain distinct Java collection keys/values.
    • The Investigate JavaProxyObject support in trimmable typemap #11703 semantic difference is intentional: llvm-ir JavaProxyObject forwards managed Equals/GetHashCode/ToString, while trimmable proxies use Java reference identity, System.identityHashCode, and exact default Object.toString.
    • Reflection-oriented general value-marshaler/ActivatePeer APIs remain unsupported; this closes only the narrower direct array/collection proxy contract.
  • Interface-valued Java collections. (#12614)

    • [TrimmableTypeMap] Support Java collections of interface element types under NativeAOT (JavaList<TInterface, TInvoker>) #11770 is stale after canonical collection rooting and generated interface-proxy support.
    • A minimal app with package-private unbound Java peers covers JavaList<IValueProvider>, JavaCollection<IValueProvider>, and dictionaries with interface keys/values/both, plus inherited-interface invokers, identity, null/duplicates, mutation, removal, and raw-JNI enumeration.
    • The unchanged fixture passes under llvm-ir/CoreCLR, trimmable/CoreCLR, and trimmable/NativeAOT. Exclusive NativeAOT DGML paths prove SafeJavaCollectionFactory roots the canonical list/collection/dictionary activation constructors without incidental closed-wrapper roots.
    • JavaCollection<T>.Remove remains independently broken under llvm-ir due to its existing method descriptor, so that non-parity issue is excluded.
  • Application-defined value types in containers.

  • Throwable round trips.

    • Verify that a managed exception passed through Java returns as the original exception rather than an Android or Java.Interop proxy wrapper.

Build, trimming, and packaging

  • R8 retention for Java outside the managed reachability graph.

  • XML-created custom views and resource-designer dependencies.

    • The NativeAOT AppWithStyleableUsageRuns failure reaches the managed constructor but cannot load _Microsoft.Android.Resource.Designer.
    • Treat this as resource-designer rooting/packaging rather than a basic constructor-marshalling failure.
  • Incremental typemap correctness.

  • Pre-generated framework typemaps.

  • Multi-RID and reference/implementation assembly selection.

    • Verify RID-specific assets and trim closures instead of relying on the first RID.
    • Ensure implementation assemblies are selected deterministically when reference and implementation assemblies share a simple name.
  • R8 incremental invalidation.

    • A managed reachability/DGML-only change must update keep rules and force DEX regeneration.
  • Fast deployment and packaging variants.

    • Exercise add/remove/rename of Java peers under the trimmable typemap.
    • Cover APK/AAB, assembly stores, ReadyToRun, single/multiple RIDs, and R8 on/off.

Explicit non-goals and intentional incompatibilities

The following should not be treated as missing parity unless the product contract changes:

  • Dynamic native registration and [JniAddNativeMethodRegistration] are unsupported by design. The build should reject them consistently with a coded diagnostic.
  • Java.Interop callable attributes such as [JavaCallable] and [JavaCallableConstructor] are currently unsupported by design. Supporting them would mean translating them into the existing generated export/constructor/UCO model, not restoring ManagedPeer; investigate only if Java.Interop-authored assemblies are supported Android inputs.
  • Legacy package naming policies and exact crc64... names are intentionally incompatible. Explicit [Register] names are the compatibility mechanism for externally referenced Java names.
  • Reflection-oriented type/value-manager APIs, including runtime ActivatePeer() and general value-marshaler lookup, are intentionally unavailable. Required product behavior should use generated proxies and marshalers.
  • Java-created open generic peers are unsupported because Java cannot identify the managed generic arguments.

Proposed validation strategy

  1. Add a shared fixture corpus that can be processed by both the legacy XAJavaInterop1/llvm-ir and trimmable pipelines.
  2. Compare parsed Java semantics, not raw source text.
  3. Compile all generated JCWs with javac.
  4. Run Java-created, managed-created, reentrant, and concurrent activation cases on device.
  5. Compare Release artifacts and runtime behavior with R8 enabled and disabled.
  6. Exercise CoreCLR Debug/Release and NativeAOT Release across multiple ABIs/RIDs.
  7. Require explicit diagnostics for every intentional non-goal instead of allowing late runtime failures.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions