Skip to content

[TrimmableTypeMap] Close scanner and JCW generator gaps vs legacy pipeline#10934

Draft
simonrozsival wants to merge 6 commits intodev/simonrozsival/trimmable-typemap-override-detectionfrom
dev/simonrozsival/trimmable-typemap-scanner-gaps
Draft

[TrimmableTypeMap] Close scanner and JCW generator gaps vs legacy pipeline#10934
simonrozsival wants to merge 6 commits intodev/simonrozsival/trimmable-typemap-override-detectionfrom
dev/simonrozsival/trimmable-typemap-scanner-gaps

Conversation

@simonrozsival
Copy link
Member

Part of #10933. Stacked on #10924 (override detection PR).

Summary

Closes gaps between the new trimmable scanner/JCW generator and the legacy Cecil-based pipeline, discovered by building the samples/HelloWorld app and deep analysis against CecilImporter.

What we can now detect

Interface method implementations without [Register]

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

The scanner now iterates implemented interfaces and adds their [Register]'d methods to the implementing type's marshal methods.

[Export] method access modifiers

[Export ("protectedMethod")]
protected void ProtectedMethod () { }

// JCW now correctly emits:
//   protected void protectedMethod () { ... }
// Previously always emitted "public".

[ExportField] attributes

[ExportField ("VALUE")]
public string GetValue () => "hello";

// JCW now emits:
//   public java.lang.String VALUE = GetValue ();
//   public java.lang.String GetValue () { return n_GetValue (); }

Constructor super() fallback patterns

// User ctor params don't match base — falls back to super():
public CustomParamActivity (string title, int count) { super (); ... }

Already worked correctly; now has test coverage.

Covariant return type overrides

// Base: [Register ("getResult", "()Ljava/lang/Object;")]
// Override with narrower return — JCW uses base's JNI signature.
public override Java.Lang.Object GetResult () => myString;

Already worked correctly; now has test coverage.

Tests

245 unit tests pass (+4 new test files, 25 new test methods).

simonrozsival and others added 6 commits March 13, 2026 15:18
When a user type implements a Java interface, the implementing method
often has no [Register] attribute:

    public class MyListener : Java.Lang.Object, IOnClickListener
    {
        public void OnClick (View v) { }  // no [Register] here
    }

The scanner must detect onClick from the interface definition.
5 of 7 tests fail, confirming the gap.

Part of #10933

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When a user type implements a Java interface, the implementing method
may not have [Register] directly on it:

    // In Mono.Android — the interface defines [Register]:
    [Register ("android/view/View$OnClickListener")]
    public interface IOnClickListener
    {
        [Register ("onClick", "(Landroid/view/View;)V", "...")]
        void OnClick (View v);
    }

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

Add Pass 4 in CollectMarshalMethods that iterates implemented interfaces,
resolves their [Register]'d methods/properties, and adds them as marshal
methods. Gated behind !doNotGenerateAcw && !isInterface.

Part of #10933

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

The legacy JCW generator respects the C# visibility for [Export] methods.
A protected export produces a protected Java method:

    [Export ("protectedMethod")]
    protected void ProtectedMethod () { }

    // Generated JCW Java:
    protected void protectedMethod () { ... }
    protected native void n_protectedMethod ();

The new pipeline always used "public". Add IsExport and JavaAccess
properties to MarshalMethodInfo and use them in JcwJavaSourceGenerator.

Part of #10933

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
[ExportField] on a method produces a Java field initialized by calling it:

    [ExportField ("VALUE")]
    public string GetValue () => "hello";

    // Generated Java:
    public java.lang.String VALUE = GetValue ();
    public java.lang.String GetValue () { return n_GetValue (); }
    public native java.lang.String n_GetValue ();

Add JavaFieldInfo model, scan [ExportField] in Pass 1 alongside
[Register]/[Export], emit field declarations in JcwJavaSourceGenerator,
and resolve Java return types via JNI signatures.

Part of #10933

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Verifies the three super() argument patterns:

    // 1. Compatible base ctor -> forward all params
    public MyView (Context p0) { super (p0); ... }

    // 2. No compatible params, parameterless fallback -> super()
    public CustomParamActivity (String p0, int p1) { super (); ... }

    // 3. [Export(SuperArgumentsString="p0")] -> custom super args
    public MyService (Context p0, int p1) { super (p0); ... }

The existing CollectBaseConstructorChain already handles all three
cases correctly. These tests document the expected behavior.

Part of #10933

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When a derived type overrides a base method with a narrower return type,
the JCW must use the base method's JNI signature:

    // Base: [Register ("getResult", "()Ljava/lang/Object;", "...")]
    public virtual Java.Lang.Object GetResult () => null;

    // User override — narrower return, no [Register]:
    public override Java.Lang.Object GetResult () => myString;

    // JCW must use base's "()Ljava/lang/Object;", not derived's type.

Also addresses review feedback: consolidated tests, JNI-based
ExportField type resolution, consistent signature-based dedup keys.

Part of #10933

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-typemap-scanner-gaps branch from 52ccc51 to c880856 Compare March 13, 2026 14:21
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.

1 participant