[TrimmableTypeMap] Add UCO wrappers, JCW generator, and JNI signature helpers#10917
Conversation
03016b7 to
6a9778f
Compare
simonrozsival
left a comment
There was a problem hiding this comment.
🤖 AI Review Summary
Verdict: ✅ LGTM
Found 0 issues.
Reviewed all 17 changed files against 14 rule categories. Read full source files (not just diff) to understand invariants and call patterns.
Checked and verified:
- No null-forgiving
!, noArray.Empty<T>()in new code, nostring.Empty, no#nullable enable, no empty catch blocks - JNI boolean correctly encoded as
byte(blittable for[UnmanagedCallersOnly]) - JI-style activation preserved from
#10808(JniObjectReference conversion + JNIEnv.DeleteRef cleanup) - Content-dependent deterministic MVID (ComputeContentFingerprint)
- Proxy name collision guard (usedProxyNames)
- Implementor/EventDispatcher trimmability heuristic with documented trade-off
- Input validation on all public methods; internal
Generate(type, writer)has single call site with pre-validated args - No resource leaks — all IDisposable properly managed
👍 Clean scanner → model → emitter pipeline. UCO wrappers, JCW Java source, and JNI signature parsing integrate well with the #10808 proxy groundwork. 219 tests with good edge-case coverage and stream-based helpers.
Review generated by android-reviewer from review guidelines.
|
@jonathanpeppers this is ready for review. Adds UCO wrappers, JCW Java source generator, and JNI signature helpers on top of #10808. |
There was a problem hiding this comment.
Pull request overview
Adds TypeMap generator support for ACW scenarios by emitting UCO (UnmanagedCallersOnly) wrapper methods, JNI RegisterNatives registration, a JCW .java source generator, and shared JNI signature parsing helpers—building on the existing TrimmableTypeMap scanner/model/emitter pipeline.
Changes:
- Extend scanning/model/emission pipeline to represent and emit ACW-specific data (constructors, UCO wrappers, native registrations,
RegisterNatives). - Add
JcwJavaSourceGeneratorandJniSignatureHelperfor generating Java stubs and parsing JNI signatures/types. - Expand/adjust tests and fixtures to cover UCO wrappers, native registration, JNI signature parsing, and
throwsclauses.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.cs | Adds ExportWithThrows fixture type used to validate throws clause generation. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/StubAttributes.cs | Extends ExportAttribute stub with ThrownNames to support fixture scanning/tests. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.cs | Adds model-level tests for ACW classification, UCO wrapper/ctor generation, and native registrations. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs | Adds PE metadata tests verifying RegisterNatives and UCO-related metadata presence. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.cs | Updates root generator tests for empty and multi-target cases. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/JcwJavaSourceGeneratorTests.cs | New tests covering JCW Java output structure, signatures, filtering, and throws clauses. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/FixtureTestBase.cs | Enhances fixture helpers for ACW peers and adds temp directory helpers for file-based JCW tests. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs | Adds constructor extraction and fixes native callback naming for constructors. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerInfo.cs | Enriches scanned model with constructors and additional marshal/native metadata. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs | Emits UCO wrappers, RegisterNatives, and ACW proxy interface implementation. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs | Builds ACW/UCO/native-registration model data and tracks cross-assembly callback dependencies. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/Model/TypeMapAssemblyData.cs | Adds model records for UCO methods/ctors and native registrations, plus IsAcw proxy flag. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/JniSignatureHelper.cs | New helper for parsing JNI signatures and mapping to CLR/Java types, plus JNI name validation. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/JcwJavaSourceGenerator.cs | New generator producing JCW .java files for ACW types based on scanned JavaPeerInfo. |
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs
Show resolved
Hide resolved
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.cs
Show resolved
Hide resolved
|
Re: the two unresolved review comments:
Nullable after |
be7e313 to
8220692
Compare
simonrozsival
left a comment
There was a problem hiding this comment.
🤖 AI Review Summary
Verdict:
Found 3 issues: 1 warning and 2 suggestions. CI is still pending (build 1332295).
⚠️ Error handling: Empty catch block swallowsIOExceptionsilently inDeleteTempDir(FixtureTestBase.cs:141)- 💡 Formatting: Missing newline at end of file in
JcwJavaSourceGeneratorTests.cs,TypeMapAssemblyGeneratorTests.cs, andTypeMapModelBuilderTests.cs - 💡 Testing:
ClassDeclaration,AbstractType, andNestedTypetests useAssert.Contains("...\n", java)while the 5 analogous failing tests useAssertContainsLine. These happen to work because they assertwriter.Write($"...\n")output (not raw strings), but consider usingAssertContainsLinethroughout for consistency
👍 Excellent test coverage — 215 tests covering all generators, model builder, scanner, and edge cases. The JNI validation in ValidateJniName properly prevents path traversal. The UCO/RegisterNatives emission architecture is well-documented with clear XML docs and a helpful pseudo-Java example in the class remarks. The AssertContainsLine helper is a clean fix for the cross-platform newline issue. Good use of record types for data models.
Review generated by android-reviewer from review guidelines.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/FixtureTestBase.cs
Outdated
Show resolved
Hide resolved
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/JcwJavaSourceGeneratorTests.cs
Outdated
Show resolved
Hide resolved
… helpers Add the UCO (UnifiedCallableObject) wrapper generation, JCW (Java Callable Wrapper) Java source generator, and JNI signature parsing utilities on top of the TypeMap proxy groundwork from PR #10808. ### Generator additions - JcwJavaSourceGenerator: generates .java source files for JCW types - JniSignatureHelper: JNI signature parsing (parameter types, return types, CLR type encoding) - Extended ModelBuilder with UCO method/constructor handling - Extended TypeMapAssemblyEmitter with UCO wrapper IL emission ### Scanner enrichment - JavaPeerInfo: added BaseJavaName, ImplementedInterfaceJavaNames, JavaConstructors, JniParameterInfo for downstream generators - JavaPeerScanner: resolver methods for base types, interfaces, constructors ### Tests - 219 tests covering all generators, model builder scenarios, scanner behavior, and edge cases - Test fixtures extended with ExportWithThrows type Rebased on main after PR #10808 merge — adapted test code to use init-only record patterns (with {}) and added missing required members. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Proactively address feedback patterns from the #10808 code review: - Remove null! from TypeMapAssemblyEmitter: make _pe readonly, init in ctor - Fix misleading JniParamKind.Boolean comment (Z → bool, not sbyte) - Make AddActivationCtorRef style-aware: support both XamarinAndroid (IntPtr, JniHandleOwnership) and JavaInterop (ref JniObjectReference, JniObjectReferenceOptions) activation constructor styles - Remove default parameter values from MakeInterfacePeer test helper - Replace disk I/O with MemoryStream in generator tests: add Stream overloads to Emit/Generate, use MemoryStream in tests for faster and more reliable test execution Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix NativeCallbackName bug: constructors now produce 'n_ctor' instead of invalid 'n_.ctor' (derived from managed method name '.ctor') - Merge ParseParameterTypeStrings + ParseJniParameters into single JniSignatureHelper.ParseParameters returning List<JniParameterInfo> - Extract EmitCreateInstance into 5 named methods (EmitNullCreateInstance, EmitGenericCreateInstance, EmitInvokerCreateInstance, etc.) - Inline MakeMarshalMethod helper: replace with direct MarshalMethodInfo initializers at all 16 call sites (reviewer feedback) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Merge 6 individual [Fact] tests into 2 [Theory] tests with [InlineData]: - ParseParameterTypes_SingleParam_MapsToCorrectKind (2 cases) - ParseReturnType_MapsToCorrectKind (4 cases) Uses int casts in [InlineData] to work around JniParamKind being internal. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove null-forgiving operator on TargetTypeReference; use explicit null check with throw instead - Validate IndexOf results in JniSignatureHelper: throw ArgumentException for malformed signatures missing ')' or ';' instead of silently producing incorrect results - Replace Array.Empty<T>() with [] per repo convention - Add #nullable enable to new files (JcwJavaSourceGenerator, JniSignatureHelper, TypeMapAssemblyData) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…llable - Remove #nullable enable from files (project-level Nullable already enabled) - Remove blank lines inside /// doc comment blocks - Fix JniParamKind enum indentation (was missing tab indent) - Fix DeleteTempDir: add braces around if body, expand try-catch - Restore MetadataHelper.ComputeContentFingerprint and content-dependent DeterministicMvid that were accidentally simplified away - Restore PEAssemblyBuilder.EmitPreamble contentFingerprint parameter Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restores the JI-style activation code (EmitCreateInstanceViaJavaInteropNewobj, EmitCreateInstanceInheritedJavaInteropCtor, JNIEnv.DeleteRef, etc.) that was inadvertently removed from TypeMapAssemblyEmitter.cs and ModelBuilder.cs. Re-applies UCO (UnifiedCallableObject) additions on top: - EmitUcoMethod, EmitUcoConstructor, EmitRegisterNatives - IAndroidCallableWrapper interface implementation for ACW types - IsImplementorOrEventDispatcher heuristic for trimmability - BuildUcoMethods, BuildUcoConstructors, BuildNativeRegistrations in ModelBuilder - Restores 5-arg EmitBody overload with local variables support Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
JNI jboolean is an unsigned byte. UnmanagedCallersOnly methods require blittable parameter types, so Boolean must be encoded as byte (not CLR bool) in UCO wrapper signatures. This matches the issue requirement: 'Non-blittable params converted correctly (byte for bool)'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tests were accidentally dropped during the rebase refactoring: - EmitterBehavior: Emit_CalledTwice_Throws, signature blob corruption - DeterminismTests: different/identical content MVIDs - JavaInteropActivation: JI-style ctor type refs, byref param, DeleteRef Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…blyGenerator to main Undo unnecessary renames (stream → output) and refactoring in files that don't need changes for this PR. Main already has the stream overloads needed by tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restore main's flat test structure instead of reorganizing into nested classes. Only add 10 new tests (ACW proxy, UCO, JniSignatureHelper, edge cases) as pure additions with zero deletions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restore main's test structure and append only new UCO tests as pure additions. Fix Implementor/EventDispatcher test to match new trimmability heuristic. Reduces diff from +730/-49 to +206/-4. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove IsImplementorOrEventDispatcher name-based heuristic and related test. This optimization is tracked in #10911 and should not be part of this PR. Restore main's behavior where these types are unconditional. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tShortName Restore main's implementations and only add the necessary changes: - n_ctor fix for constructor callback names - NativeCallbackName, JniReturnType, Parameters fields in AddMarshalMethod - BuildJavaConstructors method - JavaConstructors assignment in peer building Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Compute return type and parameter lists on-demand from JniSignature in JcwJavaSourceGenerator instead of storing them on MarshalMethodInfo and JavaConstructorInfo. Simplifies the data model — JniSignature is the single source of truth. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
8220692 to
d28814e
Compare
simonrozsival
left a comment
There was a problem hiding this comment.
🤖 AI Review Summary (v3)
Verdict:
All previous findings have been addressed. No new code issues found.
⚠️ CI: All legs are pending (build1332407). The previous run's Linux failure (CS0246: AdbDeviceInfo) was a pre-existingmainissue from thexamarin-android-toolssubmodule bump — not caused by this PR. Cannot confirm merge readiness until CI is green.
👍 Clean diff: no null!, no empty catch blocks, no Array.Empty, no disk IO in tests, all files end with newlines. AssertContainsLine properly handles cross-platform line endings. 214 tests cover generators, model builder, scanner, and edge cases.
Review generated by android-reviewer from review guidelines.
…dings Add AssertContainsLine helper that normalizes line endings before comparing, so tests pass on Windows where raw string literals in the generator produce \r\n. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Refactor tests to avoid temp directory creation and cleanup: - Filtering test now checks the scanned peer list directly - JNI name validation tests call ValidateJniName directly - Remove CreateTempDir/DeleteTempDir helpers and the empty catch block - Remove Generate_CreatesCorrectFileStructure integration test All content generation tests already use in-memory StringWriter. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
d28814e to
04e0b2e
Compare
Closes #10799
Summary
Adds UCO (UnifiedCallableObject) wrapper generation, JCW (Java Callable Wrapper) Java source generation, and JNI signature parsing utilities on top of the TypeMap proxy groundwork from PR #10808.
Generator additions
.javasource files for each ACW type withregisterNativesinstatic {}blocks,nativemethod declarations, constructor chains withsuper()calls, and@OverrideannotationsisAcwclassificationEmitUcoMethod(UnmanagedCallersOnly wrappers that delegate to n_* callbacks),EmitUcoConstructor,EmitRegisterNatives(JNI native method registration), andIAndroidCallableWrapperinterface implementation for ACW proxy typesScanner enrichment
BaseJavaName,ImplementedInterfaceJavaNames,JavaConstructors,JniParameterInfo,ThrownNamesfor downstream generatorsNativeCallbackNamefor constructors (n_ctorinstead of invalidn_.ctor)Model additions
Tests
ExportWithThrowstypeFollow-up work
GetContainerFactory()— moved to [TrimmableTypeMap] Implement TypeManager and ValueManager and other Runtime code #10791 (runtime issue); will emit the method body onceJavaPeerContainerFactoryis defined_GenerateJavaStubsdecomposition) — tracked in [TrimmableTypeMap] Decompose _GenerateJavaStubs: shared tasks for trimmable path #10807Note on
GetFunctionPointerThe issue spec mentioned
GetFunctionPointer(int)as an alternative dispatch mechanism. Since we useRegisterNatives(which binds JNI native methods to UCO wrappers at class load time),GetFunctionPointeris not needed.