Skip to content

[TrimmableTypeMap] Scanner and JCW generator gaps vs legacy pipeline #10933

@simonrozsival

Description

@simonrozsival

Overview

Systematic comparison of the new trimmable scanner (JavaPeerScanner) and JCW generator (JcwJavaSourceGenerator) against the legacy Cecil-based pipeline (CecilImporter + CallableWrapperType.Generate) revealed several gaps. These were discovered while building the samples/HelloWorld app with _AndroidTypeMapImplementation=trimmable and by deep analysis of the legacy code paths.

The existing integration parity tests (ExactMarshalMethods, ExactTypeMap, etc.) cover the scanner output. The gaps below are in areas not yet covered by parity tests, primarily in the JCW generator and interface handling.

Gaps

1. ❌ Interface method implementations without [Register] on the implementing method

Severity: High — common user pattern

When a user class implements a Java interface (e.g., IOnClickListener), the implementing method may not have [Register] directly on it. The legacy pipeline handles this via the interface loop in CecilImporter.cs lines 100-120 — it iterates type.Interfaces, resolves each interface, and adds its registered methods to the JCW.

The new scanner only finds methods via:

  1. Direct [Register] on the method
  2. Base class override detection (added in [TrimmableTypeMap] Add GenerateTrimmableTypeMap MSBuild task and targets #10924)

It does NOT iterate implemented interfaces to find methods that need JCW entries. This means user types that implement Java interfaces without explicit [Register] on each method will have incomplete JCWs.

Example:

// User code — no [Register] on OnClick
public class MyListener : Java.Lang.Object, IOnClickListener
{
    public void OnClick(View v) { }  // Missing from JCW
}

2. ⚠️ [Export] method Java access modifiers

Severity: Low — rare pattern

The legacy JCW generator respects the JavaAccess property from [Export] attributes (e.g., protected instead of public). The new JcwJavaSourceGenerator always emits public for all methods.

Legacy code: CallableWrapperMethod.Generate() uses IsExport ? JavaAccess : "public".

3. ⚠️ [ExportField] attributes

Severity: Medium

The legacy pipeline generates Java fields via CallableWrapperField for methods decorated with [ExportField]. The new generator does not handle ExportField at all — no Java field declarations are emitted.

Legacy code: CecilImporter.AddMethod() lines 471-480 creates fields via CreateField().

4. ✅ Application/Instrumentation onCreate special casing

Tracked separately in #10931.

5. ⚠️ Constructor super() argument matching for complex hierarchies

Severity: Medium

The legacy CecilImporter walks the base type constructor chain (lines 125-160) to find the right constructor signatures for super(...) calls. The new generator uses JavaConstructorInfo.SuperArgumentsString from [Export] constructors and falls back to forwarding all parameters. For deep hierarchies with multiple constructors at different levels, the super arguments may not match what the legacy pipeline would generate.

6. 💡 Covariant return type overrides in framework types

Severity: Low — no user impact (gated by DoNotGenerateAcw)

18 methods in Mono.Android types (e.g., StringBuilder.append, CursorLoader.loadInBackground) where the C# override narrows the return type. The legacy GetBaseDefinition matches these, but the new scanner's AreParametersCompatible doesn't find them because the parameter matching is done at the managed type level. These are all DoNotGenerateAcw=true framework types, so they don't affect user apps.

7. 💡 Unit test coverage for JCW generator edge cases

The JCW Java output is not compared against the legacy pipeline (and shouldn't be — the formats are deliberately different). However, the following edge cases should have targeted unit tests:

  • Nested JCW types
  • Multiple constructors with different super() argument patterns
  • [Export] methods with ThrownNames, SuperArgumentsString, and custom access modifiers
  • Interface implementation methods on class JCWs
  • Abstract class JCWs

Proposed approach

Address gaps in priority order:

  1. Interface method detection — extend CollectBaseMethodOverrides to also check implemented interfaces (or add a new pass)
  2. ExportField — add support in JcwJavaSourceGenerator
  3. Constructor super() matching — improve constructor hierarchy walking
  4. Export access modifiers — add JavaAccess support
  5. Unit tests — add targeted tests for each gap
  6. Covariant returns — low priority, only affects framework types

Part of #10789

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions