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.

    • Compare legacy XAJavaInterop1 and trimmable output for hierarchy, interfaces, constructors, methods, fields, visibility, annotations, and throws.
    • Normalize only intentional package-name and native-registration differences.
    • Compile the resulting source with javac; fragment/string assertions are insufficient.
  • Redundant and colliding interface declarations.

    • Verify most-derived interface filtering and Java-name deduplication.
    • Cover generic parent interfaces, covariant returns, Java bridge methods, and two managed members mapping to the same Java signature.
  • Unicode Java identifiers.

    • Determine whether valid non-ASCII Java identifiers are part of the supported contract.
    • If supported, ensure source paths, manifest names, DEX names, and JNI lookup use the same normalization.
  • [ExportField] validation and runtime coverage.

  • Unsupported export signatures.

    • Unknown types must not silently degrade to java.lang.Object.
    • Require correct support or an early coded diagnostic for generic, byref, pointer, function-pointer, rectangular-array, and arbitrary managed-object signatures.

Runtime behavior

  • Java aliases and casts in realistic AppCompat/view inflation paths.

    • NativeAOT AppCompat/Forms scenarios currently contain skips for invalid casts during startup.
    • Add focused JavaCast<T>/JavaAs<T> coverage for aliases, interfaces, generic wrappers, and most-derived binding types.
  • Static/default interface methods and desugaring.

    • A NativeAOT device scenario is currently skipped after NoSuchMethodError.
    • Cover API-native and D8/R8-desugared forms, nested interfaces, and Java bridge methods.
  • Plain managed-object proxy behavior.

  • Interface-valued Java collections.

  • 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