[TrimmableTypeMap] Remove InvokerType from JavaPeerProxy#11971
[TrimmableTypeMap] Remove InvokerType from JavaPeerProxy#11971simonrozsival wants to merge 5 commits into
Conversation
The trimmable typemap activates managed peers exclusively through the generated `JavaPeerProxy.CreateInstance`, so the `InvokerType` exposed on the proxy attribute is never consumed at runtime: `JavaMarshalValueManager.CreatePeer` calls `TrimmableTypeMap.CreateInstance` and never falls back to invoker-type reflection. Remove the `invokerType` constructor parameter and the `InvokerType` property from both `JavaPeerProxy` and `JavaPeerProxy<T>`, drop the now-dead `TrimmableTypeMap.GetInvokerType`, and make `TrimmableTypeMapTypeManager.GetInvokerTypeCore` throw `NotSupportedException` since it should never be reached in this path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Now that `JavaPeerProxy` no longer exposes an `InvokerType`, the generated proxy `.ctor` no longer passes an invoker `Type` (or `ldnull`) to the base constructor: the non-generic base ctor ref drops to `(string, Type)` and the generic base ctor ref to `(string)`. The model's `InvokerType` is retained where it is genuinely required — to emit `CreateInstance` and the UCO constructor activation for interfaces and abstract classes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- JavaPeerProxyTests: drop the InvokerType assertions/fixture and cover the
generic `JavaPeerProxy<T>` constructor instead.
- JniRuntimeJniTypeManagerTests.GetInvokerType: tag with
[Category ("TrimmableTypeMapUnsupported")]; the trimmable typemap type
manager throws from GetInvokerTypeCore, so this reflection-based test only
applies to the other typemap implementations.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR removes the InvokerType concept from the JavaPeerProxy attribute used by the TrimmableTypeMap path, updating the runtime attribute API, the typemap generator’s emitted proxy constructors, and associated tests to reflect the new activation model (via generated CreateInstance).
Changes:
- Runtime:
JavaPeerProxy/JavaPeerProxy<T>drop theInvokerTypeproperty and constructor parameter;TrimmableTypeMapremoves the now-deadGetInvokerTypehelper. - Generator:
TypeMapAssemblyEmitterupdates emitted proxy.ctorcalls to match the new base constructor signatures. - Tests:
JavaPeerProxyTestsare updated to validate the generic proxy constructor behavior;GetInvokerTypereflection test is categorized as unsupported under TrimmableTypeMap runs.
Show a summary per file
| File | Description |
|---|---|
| tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/JavaPeerProxyTests.cs | Updates tests to no longer assert InvokerType and adds coverage for JavaPeerProxy<T> ctor behavior. |
| src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.cs | Removes the runtime GetInvokerType helper (but currently leaves a doc-comment artifact and a ctor-call site that must be updated). |
| src/Mono.Android/Java.Interop/JavaPeerProxy.cs | Removes InvokerType from the attribute API and adjusts constructors accordingly. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs | Updates emitted base-ctor signatures/argument counts for generated proxy attribute types. |
| external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniRuntime.JniTypeManagerTests.cs | Tags a reflection-based invoker-type test with TrimmableTypeMapUnsupported category for TrimmableTypeMap runs. |
Copilot's findings
- Files reviewed: 5/5 changed files
- Comments generated: 3
- Fix MissingJavaPeerProxy to call the new 2-arg JavaPeerProxy base ctor (string, Type); the invokerType parameter was removed. - Remove a duplicate /// <summary> line left above GetContainerFactory. - Clarify the emitted-.ctor comment: the non-generic base is used for both open generic definitions and interfaces, not only generic definitions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/review |
|
✅ Android PR Reviewer completed successfully! |
There was a problem hiding this comment.
🤖 Code Review Summary
Verdict:
This is a clean, tightly‐scoped removal of dead runtime state. I verified:
- ✅ IL emission matches the new runtime ctors. The non‐generic base ctor ref
(string, Type), the generic base ctor ref(string), and the emitted.ctorarity (parameterCount: useNonGenericBase ? 2 : 1) line up exactly withJavaPeerProxy (string, Type)andJavaPeerProxy<T> (string) : base (jniName, typeof (T)). - ✅ No dangling callers. The removed runtime
JavaPeerProxy.InvokerTypehad no consumers other thanTrimmableTypeMap.GetInvokerType(also removed). All hand‐written subclasses (MissingJavaPeerProxy, the test proxies) were updated to the new ctor shapes, andTrimmableTypeMapTypeManager.GetInvokerTypeCorealready throwsUnreachableException— confirming invoker types are never fetched via reflection in the trimmable path. - ✅ Generator model
InvokerTypecorrectly retained. TheJavaPeerProxyData.InvokerType(a different type from the runtime attribute) is still consumed byEmitCreateInstanceand the UCO activation path for interfaces/abstract classes, so activation behaviour is unchanged. - ✅ Tests. Swapping
Constructor_StoresInvokerTypeforGenericConstructor_StoresJniNameAndTargetTypeis the right move and adds coverage for the new single‐arg generic ctor. - ✅ Public API baselines.
JavaPeerProxy/InvokerTypearen't tracked inPublicAPI.*.txt, so no baseline update is required.
Findings: 0 ❌ · 0 .ctor body, the exact thing this PR changes).
Minor note (non‐blocking, description only): the PR body lists TrimmableTypeMapTypeManager.GetInvokerTypeCore: throws in the trimmable path under Changes, but that file isn't touched in this PR — the UnreachableException is pre‐existing on the base branch. Worth trimming from the description for accuracy.
Nice cleanup. 👍
Generated by Android PR Reviewer for #11971 · 238.5 AIC · ⌖ 26.1 AIC · ⊞ 6.8K
Comment /review to run again
| encoder.OpCode (ILOpCode.Ldnull); | ||
| } | ||
| encoder.Call (baseCtorRef, parameterCount: useNonGenericBase ? 3 : 2, isInstance: true); | ||
| encoder.Call (baseCtorRef, parameterCount: useNonGenericBase ? 2 : 1, isInstance: true); |
There was a problem hiding this comment.
🤖 💡 Testing — This is the crux of the change: the generated .ctor now calls the base ctor with arity 2/1 instead of 3/2. But TypeMapAssemblyGeneratorTests only assert that a .ctor exists by name (Assert.Contains (".ctor", methods)) and that ctorRefs.Count >= 2 — none of them decode the emitted .ctor body. A base-ctor arity/token mismatch here would sail past metadata inspection and only surface as an InvalidProgramException/TypeLoadException on the CoreCLR device legs. There's already precedent for body-level decoding (AssertCreateInstanceReturnsNull, and the JI-ctor signature decode), so consider asserting that the emitted .ctor invokes the base ctor with the expected arity for both the generic (concrete) and non-generic (interface/open-generic) paths. Not blocking — the device legs cover it at runtime.
Rule: Generator tests should validate emitted IL bodies, not just member presence
There was a problem hiding this comment.
Good call — addressed in e9647b2. Added Generate_ProxyCtor_ChainsToExpectedBaseConstructor, a [Theory] that decodes the emitted .ctor body and asserts it chains to the expected base ctor for all three paths:
- concrete class → generic
JavaPeerProxy<T>base, arity 1(string), noGetTypeFromHandle; - interface → non-generic
JavaPeerProxybase, arity 2(string, Type), with aType.GetTypeFromHandlecall; - open-generic → same non-generic base, arity 2.
It resolves the call token to the base-ctor MemberRef and decodes its signature arity (reusing ReadCallTokens/TypeNameSignatureDecoder), so an arity/token mismatch now fails in-suite instead of only on the device legs. 608/608 passing.
Addresses code review feedback: TypeMapAssemblyGeneratorTests only asserted that a proxy .ctor exists by name, never decoding the emitted body. A base-ctor arity/token mismatch would pass metadata inspection but surface as an InvalidProgramException/TypeLoadException on the CoreCLR device legs. Add Generate_ProxyCtor_ChainsToExpectedBaseConstructor, which decodes the emitted .ctor IL and asserts it chains to the expected base constructor: - concrete class -> generic JavaPeerProxy<T> base, (string) arity 1 - interface / open-generic -> non-generic JavaPeerProxy base, (string, Type) arity 2 with a Type.GetTypeFromHandle call. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replaces #11970 (moved off the fork so PR CI runs on the
dotnet-androidpublic pipeline; branch now lives ondotnet/android).Summary
The trimmable typemap activates managed peers exclusively through the generated
JavaPeerProxy.CreateInstance, so theInvokerTypeexposed on theJavaPeerProxyattribute is never consumed at runtime.JavaMarshalValueManager.CreatePeercallsTrimmableTypeMap.CreateInstanceand never falls back to invoker-type reflection.This PR removes
InvokerTypefromJavaPeerProxyand everything that fed it.Changes
Runtime (
src/Mono.Android)JavaPeerProxy/JavaPeerProxy<T>: dropped theinvokerTypeconstructor parameter and theInvokerTypeproperty.TrimmableTypeMap: removed the now-deadGetInvokerType.TrimmableTypeMapTypeManager.GetInvokerTypeCore: throws in the trimmable path (peers are activated viaJavaPeerProxy.CreateInstance).Generator (
src/Microsoft.Android.Sdk.TrimmableTypeMap)TypeMapAssemblyEmitter: the generated proxy.ctorno longer passes an invokerType/ldnullto the base constructor; base ctor refs are now(string, Type)(non-generic) and(string)(generic). The model'sInvokerTypeis retained where genuinely required — emittingCreateInstanceand UCO constructor activation for interfaces/abstract classes.Tests
JavaPeerProxyTests: dropped theInvokerTypeassertions/fixture; added coverage for the genericJavaPeerProxy<T>constructor.JniRuntimeJniTypeManagerTests.GetInvokerType: tagged[Category ("TrimmableTypeMapUnsupported")](the trimmable type manager throws fromGetInvokerTypeCore, so this reflection-based test only applies to the other typemap implementations).Test plan
Microsoft.Android.Sdk.TrimmableTypeMap.Tests: 604/604 passing.