Skip to content

[TrimmableTypeMap] Detect overrides and constructors in JavaPeerScanner#10932

Merged
simonrozsival merged 27 commits into
mainfrom
dev/simonrozsival/trimmable-typemap-override-detection
Mar 17, 2026
Merged

[TrimmableTypeMap] Detect overrides and constructors in JavaPeerScanner#10932
simonrozsival merged 27 commits into
mainfrom
dev/simonrozsival/trimmable-typemap-override-detection

Conversation

@simonrozsival

@simonrozsival simonrozsival commented Mar 13, 2026

Copy link
Copy Markdown
Member

TL;DR

JavaPeerScanner now matches legacy CecilImporter output for method overrides, constructor chaining, and interface implementations. The scanner uses a 5-pass approach: direct [Register], property [Register], base class override detection, interface method detection, and constructor chain detection. Also fixes 4 bugs in the integration test harness that were hiding mismatches between legacy and new scanner output. Validated with zero divergence across ~8000 Mono.Android types — no filtering, direct 1:1 comparison.

247 unit tests + 10 integration tests pass.

Part of #10789

Context

The JavaPeerScanner was missing several categories of methods that the legacy CecilImporter finds:

  1. Method overrides without [Register] — User types (e.g., MainActivity) override Activity.OnCreate() without [Register] on the override. The attribute is only on the base class method in Mono.Android.

  2. Java constructors via base ctor chaining — User types have non-activation constructors without [Register]. The legacy CecilImporter walks the base type hierarchy, seeds registered ctors, and accepts derived ctors with compatible parameters.

  3. Constructor parameter JNI signatures for object types — Ctors taking Java peer types (e.g., X509Certificate[]) need L<jniName>; descriptors, not just primitive mappings.

  4. Base JNI name for types without [Register] — Types like XmlReaderPullParser have no [Register] but are Java peers with auto-computed JNI names. ResolveBaseJavaName needs to compute these on demand.

Scanner architecture

The scanner's CollectMarshalMethods now uses a 5-pass approach:

CollectMarshalMethods(typeDef, detectBaseOverrides)
│
├─ Pass 1: Direct [Register] / [Export] / [ExportField] on methods
├─ Pass 2: [Register] on properties (getter)
│
│  (Passes 3-5 only for user ACW types: detectBaseOverrides=true)
│
├─ Pass 3: Override detection — walk base hierarchy
│  ├─ Method overrides (FindBaseRegisteredMethodInfo)
│  └─ Property overrides (FindBaseRegisteredProperty)
├─ Pass 4: Interface method implementations
│  └─ CollectInterfaceMethodImplementations
└─ Pass 5: Constructor chaining from base registered ctors
   └─ CollectBaseConstructorChain
      ├─ Seed base registered ctors
      ├─ Match params → already covered
      └─ Fallback → parameterless super() + BuildJniCtorSignature

Changes

Scanner (JavaPeerScanner.cs, JavaPeerInfo.cs)

Pass 3 — Override detection: For each virtual override without [Register], walks the base hierarchy to find the registered base method and copies its registration info. Also handles property overrides (e.g., Throwable.Message). Populates DeclaringTypeName/DeclaringAssemblyName so UCO wrappers call n_* on the correct base type. Walks all base types with no DoNotGenerateAcw boundary — matching legacy GetBaseDefinition/GetBaseRegisteredMethod.

Pass 5 — Constructor chain detection: Mirrors legacy CecilImporter behavior:

  • Seeds all base registered ctors directly into the type's marshal methods
  • Matches derived ctors with compatible parameters against base registered ctors
  • Fallback: if a base has a registered ()V ctor, accepts derived ctors with novel params (generates super() call via SuperArgumentsString="")
  • Rejects ctors with non-Java parameter types (System.IntPtr, System.Action, etc.)
  • Resolves Java peer object types (e.g., X509Certificate) to L<jniName>; via assembly cache lookup
  • Handles [JniConstructorSignature] (Java.Interop-style) in addition to [Register]
  • Stops base walk at DoNotGenerateAcw boundary (matching legacy)

ResolveBaseJavaName — Now computes auto JNI names for base types without [Register] when they haven't been scanned yet (scan order within an assembly is not guaranteed).

ManagedTypeToJniDescriptorOrNull — Now resolves Java peer object types by looking up their [Register] JNI name in the assembly cache.

Unit tests + fixtures

  • OverrideDetectionTests (10 tests) — overrides without [Register], deep inheritance (3 levels), ACW→ACW→MCW hierarchy, property overrides, mixed methods, new-slot exclusion, DeclaringTypeName verification
  • ConstructorDetectionTests (9 tests) — base ctor chain, implicit default ctor, base ctor seed, JniConstructorSignature, parameterless fallback, same-arity type mismatch, multi-parameter signature computation
  • Test fixtures: UserActivity, FullActivity, DeeplyDerived, MixedMethods, NewSlotActivity, CustomException, BaseFragment, DerivedFragment, GrandchildFragment, JiStyleView, ActivityWithCustomCtor, DialogBase, CustomDialog, ActivityWithMultiParamCtor

Integration test harness improvements

The following changes are not directly related to the scanner features above, but were necessary to properly validate them. The scanner now produces more complete output (interface methods, constructors with object type params, types without [Register]), which exposed pre-existing bugs in how the legacy test harness transforms CecilImporter output for comparison.

GetCecilJavaName — Added JavaNativeTypeManager.ToJniName fallback for types without [Register] (e.g., ActivityTracker). Previously returned null, silently skipping these types. Now matches what CecilImporter.CreateType actually does.

ExtractMethodRegistrations — Fixed to use m.JavaName (JNI name) instead of m.Name (managed name) for [Export] methods. Now includes constructors from wrapper.Constructors so both scanners' constructors are compared directly.

Removed all filtering — No more interface method filtering, no phantom entry fallback, no constructor exclusion. Both RunLegacy and RunNew produce complete unfiltered output compared 1:1 across ~8000 Mono.Android types.


Related issue

Filed #10931JcwJavaSourceGenerator must skip registerNatives in the static block for Application/Instrumentation types.

Test results

  • 247 unit tests pass
  • 10 integration tests pass (including ExactMarshalMethods_MonoAndroid and ExactJavaConstructors_MonoAndroid — no filtering, direct 1:1 comparison)

Legacy comparison notes

  • Override detection matches legacy GetBaseDefinitionGetBaseRegisteredMethod: walks all base types (no DoNotGenerateAcw boundary), checks Virtual || Abstract, matches by name + parameter compatibility.
  • Constructor chaining matches legacy ctorTypes loop (CecilImporter.cs:124-160): stops at DoNotGenerateAcw, seeds registered base ctors, checks AreParametersCompatibleWith, falls back to parameterless super().
  • Object type resolution matches legacy GetJniTypeName: resolves Java peer types to L<jniName>; via [Register] lookup, returns null for non-Java types.
  • Parameter compatibility uses string comparison of decoded S.R.M. signatures — equivalent to Cecil's a.FullName == b.FullName for non-generic cases. Validated against ~8000 types with zero divergence.

simonrozsival and others added 9 commits March 13, 2026 09:46
The scanner only found marshal methods with [Register] directly on them.
User types (e.g., MainActivity) that override Activity.OnCreate() don't
have [Register] on the override — the attribute is only on the base
class method in Mono.Android. This caused missing UCO wrappers,
RegisterNatives, and empty JCW Java files.

Add a third pass in CollectMarshalMethods that walks the base type
find a matching registered base method. Also handles property overrides
(e.g., Throwable.Message). Override detection is gated behind
DoNotGenerateAcw — MCW framework types already have [Register] on
every method that matters.

Integration parity: ExactMarshalMethods_UserTypesFixture passes (exact
parity for user types). ExactMarshalMethods_MonoAndroid has 8 known
extras — internal Mono.Android types (InputStreamAdapter, JavaObject,
OutputStreamAdapter) that override registered base methods without
their own [Register]. The legacy scanner skips these because its JCW
pipeline doesn't process them. These extras are harmless — the types
have hand-written JCWs. A follow-up can update the parity test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The scanner doesn't detect Java constructors for user types whose
constructors lack [Register]. The legacy CecilImporter chains from
base registered ctors to derived unregistered ctors, but the new
scanner skips constructors in override detection (Pass 3) and
FindBaseRegisteredMethodInfo requires Virtual/Abstract (which ctors
are not).

Add [Register(".ctor", "()V", "")] to the Activity test fixture
to match real Mono.Android bindings, and add 10 tests:
- 7 failing: MainActivity/SimpleActivity should get JavaConstructors
- 3 passing: UserActivity/FullActivity (activation-only) and CustomView

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

The scanner only found constructors with [Register] directly on them.
User types (e.g., MainActivity) that have a parameterless constructor
without [Register] were missing JavaConstructors, which meant no Java
constructor in the JCW and no UCO constructor wrapper.

Mirror the legacy CecilImporter behavior: walk the base type hierarchy
to collect registered ctors, then accept non-activation ctors on the
user type whose parameters are compatible with an accepted base ctor.

Algorithm (Pass 4 in CollectMarshalMethods):
1. Collect registered ctors from base type hierarchy
2. For each non-activation ctor without [Register] on the user type
3. Find a base registered ctor with compatible parameters
4. Add it as a constructor marshal method using the base registration

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tests:
- Consolidate 10 individual [Fact] into 5 focused tests. Each
  FindFixtureByJavaName call now has a single test with all assertions.

Scanner (CollectBaseConstructorChain):
- Remove explicit IsActivationCtor check. Activation ctors (IntPtr,
  JniHandleOwnership) will never match a base registered ctor because
  AreParametersCompatible already filters on type+count. Legacy
  CecilImporter doesn't skip them explicitly either.
- Add DoNotGenerateAcw stop in CollectBaseRegisteredCtors: stop walking
  after the first MCW base type, matching legacy CecilImporter behavior
  (CecilImporter.cs:133-134).
- Extract TryResolveBaseType helper to eliminate while(true) loop.
- Document known remaining differences from legacy CecilImporter in the
  method summary (parameterless fallback, outerType, managed params).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Three changes to match legacy CecilImporter behavior exactly:

1. Decouple ctor chain from detectBaseOverrides flag
   Pass 4 (CollectBaseConstructorChain) now runs for all types including
   MCW types, matching legacy which runs constructor detection
   unconditionally. Override detection (Pass 3) remains gated.

2. Handle JniConstructorSignatureAttribute
   Java.Interop-style types use [JniConstructorSignature("()V")]
   instead of [Register(".ctor", "()V", "")]. Add handling in
   TryGetMethodRegisterInfo to convert it to a RegisterInfo with
   JniName=".ctor" and the signature from the attribute arg.
   Add JniConstructorSignatureAttribute stub and test fixture.

3. Implement parameterless base ctor fallback
   Legacy CecilImporter (line 394-397) accepts a user ctor with novel
   parameter types when any base has a registered parameterless ctor.
   Compute the JNI signature from managed parameter types using
   BuildJniCtorSignature. Add ActivityWithCustomCtor test fixture.

Update OverrideDetectionTests.MultipleOverrides_AllDetected to filter
out constructor marshal methods from the count assertion, since
FullActivity now correctly gets a ctor via the fallback path.

Known remaining generator-level differences (tracked separately):
- outerType / nested inner-class constructor handling
- Application synthetic constructor (MonoPackageManager.setContext)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The legacy side of the parity test (ScannerRunner.ExtractMethodRegistrations)
only read [Register] attributes directly from a type's own methods. This
missed overrides of registered base methods that the real legacy pipeline
(CecilImporter.CreateType → GetBaseRegisteredMethod) correctly detects.

Replace the hand-rolled attribute reader with CecilImporter.CreateType(),
filtering out interface method implementations (which the new scanner
places on the interface peer type, not the implementing type).

Results after this fix:
- ExactMarshalMethods_UserTypesFixture: PASSES (exact parity for user types)
- ExactMarshalMethods_MonoAndroid: 22 remaining MISSING (down from 9047)
  These are covariant return type overrides and overloaded method variants
  that the new scanner's AreParametersCompatible doesn't yet handle.
  The legacy GetBaseDefinition uses looser matching.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Scanner:
- Set SuperArgumentsString="" on fallback-path ctors so the generator
  emits super() instead of super(p0, ...). The Java ctor calls the
  base parameterless ctor, then delegates args to nctor_N(). Matches
  legacy CecilImporter which passes superCall="" for the fallback
  (CecilImporter.cs:396) vs null for compatible-params (line 391).

Tests:
- Use Assert.Single where exactly one item is expected
- Assert specific JNI signatures and SuperArgumentsString on activation
  ctor fallback tests (UserActivity, FullActivity)
- Move count assertion before Contains in OverrideDetectionTests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The legacy side of the parity test (ScannerRunner.ExtractMethodRegistrations)
only read [Register] attributes directly from a type's own methods. This
missed overrides of registered base methods that the real legacy pipeline
(CecilImporter.CreateType → GetBaseRegisteredMethod) correctly detects.

Replace the hand-rolled attribute reader with CecilImporter.CreateType()
for non-DoNotGenerateAcw class types (matching the real build pipeline).
Interface types and DoNotGenerateAcw types use direct attribute extraction
since CecilImporter doesn't process them. Filter interface method
implementations from the CecilImporter output since the new scanner
places those on the interface peer type.

Constructors are excluded from both sides of the comparison (compared
separately in ExactJavaConstructors).

Results: ExactMarshalMethods_MonoAndroid and ExactMarshalMethods_UserTypesFixture
both pass with exact parity.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Scanner:
- Seed all base registered ctors directly into JavaConstructors, matching
  legacy CecilImporter which adds base ctors to the wrapper during the
  reversed ctorTypes walk. This fixes Implementor types (e.g.,
  IOnNavigationListenerImplementor) that have no registered ctors
  themselves but need the base ()V ctor.
- Re-gate Pass 4 behind detectBaseOverrides. Legacy CecilImporter only
  processes ctors for types that go through CreateType (non-interface,
  non-DoNotGenerateAcw). MCW types use direct [Register] only.
- Add ManagedTypeToJniDescriptorOrNull for the fallback path. Legacy
  GetJniSignature returns null for non-Java types (System.Object,
  System.IntPtr, System.Action, etc.), rejecting ctors with such
  params. Our fallback was incorrectly accepting them via the default
  Ljava/lang/Object; mapping. Now ctors with non-primitive,
  non-string params are skipped in the fallback, matching legacy.

Integration tests:
- Replace manual [Register] attribute scanning in TypeDataBuilder with
  real CecilImporter.CreateType to get accurate legacy ctor list
  including the base ctor chain. Add ExtractDirectRegisterCtors as
  fallback for interfaces and DoNotGenerateAcw types.

Unit tests:
- Update UserActivity/FullActivity to expect single ()V base seed
  (activation ctors rejected by the fallback's null-signature check).
- Fix MixedMethods_NoDuplicates to dedupe by (JniName, JniSignature)
  since .ctor can appear multiple times with different signatures.

All 235 unit tests and 10 integration tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CI enforces xUnit analyzers as errors. Replace
Assert.Single(collection.Where(predicate)) with
Assert.Single(collection, predicate).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival marked this pull request as ready for review March 13, 2026 13:02
Copilot AI review requested due to automatic review settings March 13, 2026 13:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the TrimmableTypeMap Java peer scanning pipeline to match legacy JCW behavior for (1) overrides without [Register] and (2) constructor discovery via base-ctor chaining, and expands the test suite to validate parity against the real legacy Cecil-based pipeline.

Changes:

  • Extend JavaPeerScanner to detect base-registered method/property overrides and derive Java constructors from base registered constructors (including parameterless fallback).
  • Rework integration-test legacy extraction to use CecilImporter.CreateType (instead of manual [Register] scanning) for accurate baseline comparison.
  • Add new fixtures and unit tests covering override and constructor-chain scenarios (including JniConstructorSignatureAttribute).

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs Adds override detection and constructor chain detection passes; recognizes JniConstructorSignatureAttribute.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.cs Adds new fixture types for override + ctor-chain scenarios; adjusts Activity ctor registration.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/StubAttributes.cs Adds stub JniConstructorSignatureAttribute for unit tests.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/OverrideDetectionTests.cs New unit tests validating override detection behavior.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/ConstructorDetectionTests.cs New unit tests validating constructor chain detection behavior.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.IntegrationTests/ScannerRunner.cs Legacy method extraction now uses CecilImporter.CreateType, with interface/MCW fallbacks and interface-impl filtering.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.IntegrationTests/TypeDataBuilder.cs Legacy ctor extraction now uses CecilImporter.CreateType, with fallback to direct attribute scanning.

Comment thread src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs Outdated
Comment thread tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.cs Outdated
Comment thread tests/Microsoft.Android.Sdk.TrimmableTypeMap.IntegrationTests/TypeDataBuilder.cs Outdated
…ies, key consistency

- Remove Signature! null-forgiving operator in CollectBaseConstructorChain;
  extract to local variable with null guard instead
- Stop FindBaseRegisteredMethodInfo and FindBaseRegisteredProperty recursion
  at the DoNotGenerateAcw boundary (matching CollectBaseRegisteredCtors)
- Use consistent signature-based key format in CollectBasePropertyOverrides
  (name + decoded params) to match Pass 1/2 key format
- Merge duplicate if (detectBaseOverrides) blocks into a single block
- Remove unnecessary try/catch fallback in TypeDataBuilder; let
  CecilImporter.CreateType failures propagate rather than silently
  falling back to different extraction logic
- Replace default! with default in test fixture constructors (both
  params are value types, ! is unnecessary)
- Consolidate OverrideDetectionTests: merge tests that look up the
  same fixture type into single Facts to reduce redundant lookups

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

@simonrozsival simonrozsival left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI Review Summary

Verdict: ✅ LGTM

Found 0 issues in the updated diff.

The previous round of review feedback has been fully addressed:

  • Signature! null-forgiving operators replaced with proper null guards
  • FindBaseRegisteredMethodInfo and FindBaseRegisteredProperty now stop at DoNotGenerateAcw boundary
  • Property override key format made consistent with Pass 1/2
  • default! replaced with default in test fixtures
  • Bare catch in TypeDataBuilder removed
  • Duplicate if (detectBaseOverrides) blocks merged
  • Override detection tests consolidated (14→7 Facts, all assertions preserved)

👍 The override detection and constructor chaining logic is well-structured, thoroughly documented, and correctly mirrors legacy CecilImporter behavior. The 4-pass architecture (direct [Register], property [Register], base overrides, base ctor chain) has clear separation of concerns. Test coverage is comprehensive across edge cases (deep inheritance, new-slot exclusion, property overrides, JI-style attributes, parameterless fallback).

⚠️ CI is still pending (build 1334063) — cannot confirm merge-readiness until checks pass.


Review generated by android-reviewer from review guidelines.

simonrozsival and others added 3 commits March 13, 2026 14:44
FindBaseRegisteredMethod and FindBaseRegisteredProperty now populate
DeclaringTypeName and DeclaringAssemblyName on MarshalMethodInfo so
UCO wrappers call n_* callbacks on the correct base type (e.g.,
Activity.n_OnCreate) rather than the derived user type.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add deep hierarchy fixtures: BaseFragment → DerivedFragment → GrandchildFragment
  to test DeclaringTypeName resolution across ACW → ACW → MCW boundaries
- Add DialogBase with registered ctor(Context) to test same-arity parameter
  type mismatch in AreParametersCompatible
- Add ActivityWithMultiParamCtor to test multi-parameter JNI signature
  computation in BuildJniCtorSignature (string, int, bool → Ljava/lang/String;IZ)
- Strengthen DeepInheritance test to verify DeclaringTypeName == Activity
- Strengthen MultipleOverrides test comment: exact count proves non-registered
  Object virtuals (ToString/Equals/GetHashCode) are correctly excluded

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Legacy GetBaseRegisteredMethod (via GetBaseDefinition) walks ALL base
types with no DoNotGenerateAcw stop — only the constructor chain has
that boundary. Remove the boundary check from FindBaseRegisteredMethodInfo
and FindBaseRegisteredProperty to match legacy behavior exactly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread tests/Microsoft.Android.Sdk.TrimmableTypeMap.IntegrationTests/TypeDataBuilder.cs Outdated
simonrozsival and others added 7 commits March 13, 2026 18:09
CecilImporter.CreateType failures should propagate — this is test
infrastructure matching legacy behavior, not extending it.

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

Interface method implementations are collected by both CecilImporter
(legacy) and CollectInterfaceMethodImplementations (new scanner) but
through different codepaths with different deduplication strategies.
Filter them from both sides of the ExactMarshalMethods comparison
since interface methods are tested via interface peer types.

Added IsInterfaceImplementation flag to MarshalMethodInfo so the new
scanner side can identify and filter these methods in the comparison.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ExtractMethodRegistrations used wrapper.Methods[].Name (managed method
name, e.g., 'DoWork') instead of .JavaName (JNI name, e.g., 'doWork').
For [Export] methods, these differ — the JNI name comes from the
Export attribute. The new scanner correctly uses the JNI name, so the
legacy comparison must too.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
GetCecilJavaName only read [Register] attributes, silently skipping
types like ActivityTracker that get their JNI name computed by
JavaNativeTypeManager.ToJniName. This matches what CecilImporter.CreateType
does (line 26).

Also removes the interface method filtering from both legacy and new
scanner sides of the comparison — both sides now correctly discover
and include interface method implementations for all types.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ManagedTypeToJniDescriptorOrNull returned null for Java peer object
types like Java.Security.Cert.X509Certificate, causing ctors that
take these types to be skipped entirely.

Now looks up the managed type name in assemblyCache to find its
[Register] JNI name and emits L<jniName>; — matching legacy
JavaNativeTypeManager.GetJniTypeName behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ResolveBaseJavaName returned null for base types like
XmlReaderPullParser that have no [Register] attribute but are Java
peers (they extend Java.Lang.Object and get auto-computed JNI names).

When the base type isn't found in already-scanned results (scan order
within an assembly is not guaranteed), fall back to resolving it the
same way ScanAssembly does: check ExtendsJavaPeer and compute the
auto JNI name via ComputeAutoJniNames.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Now that GetCecilJavaName uses ToJniName fallback, every type from
javaTypes gets a JNI name and is added to methodsByJavaName. The
fallback loop that added types from dataSets with 0 methods is no
longer reachable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
simonrozsival and others added 2 commits March 16, 2026 13:48
Both scanners discover constructors — compare them directly instead
of excluding them. Legacy side now includes wrapper.Constructors,
new scanner side no longer filters IsConstructor.

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

@simonrozsival simonrozsival left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI Review Summary

Verdict: ✅ LGTM

Found 0 issues across 14 changed files (~1800 additions).

No null-forgiving operators, no empty catch blocks, no filtering or hidden data in scanner comparison. Both RunLegacy and RunNew produce complete unfiltered output (methods, properties, interface implementations, constructors) compared 1:1 across ~8000 Mono.Android types.

The 5-pass scanner architecture is well-structured:

  • Pass 1-2: direct [Register]/[Export] on methods and properties
  • Pass 3: override detection walking base hierarchy (no DoNotGenerateAcw boundary, matching legacy GetBaseDefinition)
  • Pass 4: interface method implementations
  • Pass 5: constructor chaining with parameterless fallback

The integration test harness fixes (4 bugs) are clearly separated in commits and correctly documented as supporting infrastructure.

⚠️ CI: Linux ✅, Windows/Mac still pending. Cannot confirm merge-readiness until all checks pass.


Review generated by android-reviewer from review guidelines.

simonrozsival and others added 4 commits March 16, 2026 17:26
…patible

- Extract TryGetPrimitiveJniDescriptor shared by both
  ManagedTypeToJniDescriptor (falls back to Ljava/lang/Object;) and
  ManagedTypeToJniDescriptorOrNull (looks up assemblyCache, returns null)
- Rename AreParametersCompatible to HaveIdenticalParameterTypes — the
  method checks for exact type match, not compatibility
- Remove unused index1/index2 parameters from HaveIdenticalParameterTypes

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

Extended TryResolveBaseType with baseTypeName/baseAssemblyName out
params. Used it in FindBaseRegisteredMethodInfo, FindBaseRegisteredProperty,
and ResolveBaseJavaName to replace repeated GetBaseTypeInfo → TryResolveType
→ GetTypeDefinition boilerplate (~20 lines removed).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rays, interface inheritance

- DerivedFragment onViewCreated DeclaringTypeName → BaseFragment (ACW→ACW)
- ConcreteImpl overrides abstract DoWork (abstract base detection)
- OverloadDerived overrides Process(int) but not Process() (correct overload)
- EmptyConnector override still detected (Activity.OnStart has empty connector)
- ViewArrayActivity ctor(View[]) → [Landroid/view/View; (object array JNI)
- UnsignedParamActivity ctor(ushort,uint,ulong) → (SIJ)V (unsigned primitives)
- NamedClickListenerImpl gets methods from parent interface (interface inheritance)

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

Interface method and property collection was manually creating
MarshalMethodInfo with the same field logic as AddMarshalMethod.
Now calls AddMarshalMethod with isInterfaceImplementation parameter,
removing ~20 lines of duplicated MarshalMethodInfo construction.

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

@simonrozsival simonrozsival left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI Review Summary

Verdict: ✅ LGTM

Found 0 issues across 14 changed files (~2400 line diff).

No null-forgiving operators, no empty catch blocks, no filtering or hidden data in the comparison. Code duplication has been addressed:

  • TryResolveBaseType extended to share base type resolution across 3 methods
  • TryGetPrimitiveJniDescriptor extracted to share JNI mapping between nullable and non-nullable variants
  • AddMarshalMethod reused in CollectInterfaceMethodImplementations (eliminated ~20 lines of duplicated MarshalMethodInfo construction)
  • HaveIdenticalParameterTypes renamed from AreParametersCompatible with unused params removed

Test coverage is comprehensive: 254 unit tests + 10 integration tests. Recent additions cover abstract base overrides, correct overload selection, Java peer object arrays in ctor signatures, unsigned primitives, empty connectors, interface inheritance chains, and ACW→ACW DeclaringTypeName resolution.

⚠️ CI is pending (build 1337757). Previous build (1337248) was all green on Linux/Mac/Windows.


Review generated by android-reviewer from review guidelines.

@simonrozsival simonrozsival merged commit 9646bda into main Mar 17, 2026
6 checks passed
@simonrozsival simonrozsival deleted the dev/simonrozsival/trimmable-typemap-override-detection branch March 17, 2026 06:20
@simonrozsival simonrozsival added the copilot `copilot-cli` or other AIs were used to author this label Mar 19, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

copilot `copilot-cli` or other AIs were used to author this trimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants