Avoid keepalive-only finally blocks in generated bindings - #12343
Avoid keepalive-only finally blocks in generated bindings#12343jonathanpeppers wants to merge 5 commits into
Conversation
Emit straight-line GC.KeepAlive calls when generated JNI bindings have no exceptional-path cleanup, while preserving try/finally for real marshaling cleanup. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Cover keepalive-only object parameters and string marshaling cleanup in compatibility virtual methods for both generator targets. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c892727a-ae15-4b3f-9c29-d6b9c3b89401
There was a problem hiding this comment.
Pull request overview
This PR updates the Java.Interop binding generator to avoid emitting try/finally regions when the finally block would only contain GC.KeepAlive(...), reducing EH metadata and enabling better JIT inlining of thin JNI wrappers. It also refreshes generator baselines and adds/extends tests to validate both the EH-free keepalive path and the retained-cleanup/compat paths.
Changes:
- Emit straight-line invocation +
GC.KeepAlive(and deferred return when needed) when there is no exceptional-path cleanup. - Retain
try/finallywhen marshaling cleanup is required, and retain compat-virtualNoSuchMethodErrortranslation. - Update generator expected-output baselines and add targeted assertions in unit/integration tests.
Reviewed changes
Copilot reviewed 128 out of 128 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| external/Java.Interop/tools/generator/SourceWriters/Extensions/SourceWriterExtensions.cs | Core generator logic to elide keepalive-only finally blocks and optionally defer returns. |
| external/Java.Interop/tools/generator/SourceWriters/BoundFieldAsProperty.cs | Update field-backed property setter emission to avoid keepalive-only finally. |
| external/Java.Interop/tools/generator/SourceWriters/BoundConstructor.cs | Update constructor emission to avoid keepalive-only finally. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/DefaultInterfaceMethodsTests.cs | Add extra structural assertion over generated output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs | Extend compat-virtual coverage and assert new keepalive/cleanup shapes. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1/WriteStaticInterfaceProperty.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1/WriteStaticInterfaceMethod.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1/WriteKotlinUnsignedTypePropertiesClass.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1/WriteKotlinUnsignedTypeMethodsClass.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1/WriteKotlinUnsignedArrayTypePropertiesClass.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1/WriteInterfaceFieldAsDimProperty.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1/WriteInterfaceDefaultPropertyGetterOnly.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1/WriteInterfaceDefaultProperty.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1/WriteInterfaceDefaultMethod.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1/WriteInterface.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1/WriteDefaultInterfaceMethodInvoker.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1/WriteConstSugarInterfaceFields.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1/WriteClass.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1/ObsoleteInterfaceAlternativeClass.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1-NRT/WriteInterface.txt | Baseline update for EH-free keepalive-only output (NRT). |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1-NRT/WriteClass.txt | Baseline update for EH-free keepalive-only output (NRT). |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteObjectField.txt | Baseline update: move keepalive out of keepalive-only finally. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteKotlinUnsignedTypePropertiesClass.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteKotlinUnsignedTypeMethodsClass.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteKotlinUnsignedArrayTypePropertiesClass.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteInterface.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteDuplicateInterfaceEventArgs.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteClass.txt | Baseline update for EH-free keepalive-only output. |
| external/Java.Interop/tests/generator-Tests/Integration-Tests/NormalProperties.cs | Add integration assertion for keepalive-only setter shape. |
| external/Java.Interop/tests/generator-Tests/Integration-Tests/NormalMethods.cs | Add integration assertions for keepalive-only and cleanup-retained shapes. |
| external/Java.Interop/tests/generator-Tests/expected.xaji/TestInterface/Test.ME.TestInterfaceImplementation.cs | Updated expected generated output (XAJavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.xaji/TestInterface/Test.ME.ITestInterface.cs | Updated expected generated output (XAJavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.xaji/TestInterface/Test.ME.IGenericPropertyInterface.cs | Updated expected generated output (XAJavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.xaji/TestInterface/Test.ME.GenericStringPropertyImplementation.cs | Updated expected generated output (XAJavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.xaji/TestInterface/Test.ME.GenericStringImplementation.cs | Updated expected generated output (XAJavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.xaji/TestInterface/Test.ME.GenericObjectPropertyImplementation.cs | Updated expected generated output (XAJavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.xaji/TestInterface/Test.ME.GenericImplementation.cs | Updated expected generated output (XAJavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.xaji/TestInterface/Java.Util.IQueue.cs | Updated expected generated output (XAJavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.xaji/TestInterface/Java.Util.IDeque.cs | Updated expected generated output (XAJavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.xaji/TestInterface/Java.Util.ICollection.cs | Updated expected generated output (XAJavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.xaji/TestInterface/IInterfaceWithoutNamespace.cs | Updated expected generated output (XAJavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.xaji/TestInterface/ClassWithoutNamespace.cs | Updated expected generated output (XAJavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.xaji/Streams/Java.Lang.Throwable.cs | Updated expected generated output (XAJavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.xaji/Streams/Java.IO.OutputStream.cs | Updated expected generated output (XAJavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.xaji/Streams/Java.IO.IOException.cs | Updated expected generated output (XAJavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.xaji/Streams/Java.IO.InputStream.cs | Updated expected generated output (XAJavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.xaji/Streams/Java.IO.FilterOutputStream.cs | Updated expected generated output (XAJavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/TestInterface/Test.ME.TestInterfaceImplementation.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/TestInterface/Test.ME.ITestInterface.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/TestInterface/Test.ME.IGenericPropertyInterface.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/TestInterface/Test.ME.IGenericInterface.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/TestInterface/Test.ME.GenericStringPropertyImplementation.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/TestInterface/Test.ME.GenericStringImplementation.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/TestInterface/Test.ME.GenericObjectPropertyImplementation.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/TestInterface/Test.ME.GenericImplementation.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/TestInterface/Java.Util.IQueue.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/TestInterface/Java.Util.IDeque.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/TestInterface/Java.Util.ICollection.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/TestInterface/IInterfaceWithoutNamespace.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/TestInterface/ClassWithoutNamespace.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/Streams/Java.Lang.Throwable.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/Streams/Java.IO.OutputStream.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/Streams/Java.IO.IOException.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/Streams/Java.IO.InputStream.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/Streams/Java.IO.FilterOutputStream.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/java.util.List/Xamarin.Test.SomeObject.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/java.lang.Enum/Java.Lang.IComparable.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/java.lang.Enum/Java.Lang.Enum.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/ParameterXPath/Xamarin.Test.A.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/Arrays/Xamarin.Test.SomeObject.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/InterfaceMethodsConflict/Xamarin.Test.SomeObject2.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/InterfaceMethodsConflict/Xamarin.Test.SomeObject.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/InterfaceMethodsConflict/Xamarin.Test.II2.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/InterfaceMethodsConflict/Xamarin.Test.II1.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/CSharpKeywords/Xamarin.Test.CSharpKeywords.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/CSharpKeywords/Java.Lang.Throwable.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/Constructors/Xamarin.Test.SomeObject2.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/Constructors/Xamarin.Test.SomeObject.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/NestedTypes/Xamarin.Test.NotificationCompatBase.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/Adapters/Xamarin.Test.GenericReturnObject.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/Adapters/Xamarin.Test.AdapterView.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/Adapters/Xamarin.Test.AbsSpinner.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/AccessModifiers/Xamarin.Test.TestClass.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/AccessModifiers/Xamarin.Test.PublicFinalClass.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/AccessModifiers/Xamarin.Test.PublicClass.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/AccessModifiers/Xamarin.Test.IExtendedInterface.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/AccessModifiers/Xamarin.Test.ExtendPublicClass.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
| external/Java.Interop/tests/generator-Tests/expected.ji/AccessModifiers/Xamarin.Test.BasePublicClass.cs | Updated expected generated output (JavaInterop1) for keepalive-only paths. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
Resolve the generator source-writer overlap after legacy interface invoker support was removed, and refresh the affected expected outputs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c892727a-ae15-4b3f-9c29-d6b9c3b89401
Emit keep-alive calls before translating NoSuchMethodError when no cleanup finally is required, preserving the previous caught-exception lifetime behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c892727a-ae15-4b3f-9c29-d6b9c3b89401
|
/review |
|
✅ Android PR Reviewer completed successfully!
|
There was a problem hiding this comment.
✅ LGTM from code review — 0 errors, 0 warnings, 1 suggestion.
The cleanup/keepalive split is applied consistently across methods, constructors, and field-backed setters. Real marshaling cleanup remains protected by finally, non-void keepalive-only methods defer the return correctly, and compatibility virtual methods preserve argument lifetime on the translated NoSuchMethodError path. The regression coverage exercises straight-line keepalives, retained cleanup blocks, and balanced compatibility control flow.
The focused Java.Interop checks are passing on macOS and Windows; the broader Linux, macOS, and Windows platform builds are still in progress.
Generated by Android PR Reviewer for #12343 · gpt56 · 148.3 AIC · ⌖ 8.81 AIC · ⊞ 25.6K
Comment /review to run again
Scan parameter lists directly when deciding and emitting generated cleanup and keep-alive statements, avoiding per-member temporary collections while preserving generated output. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c892727a-ae15-4b3f-9c29-d6b9c3b89401
Generated binding methods currently use exception-handling regions even when their
finallyblock only callsGC.KeepAlive. These regions add metadata and prevent RyuJIT from inlining otherwise-thin JNI wrappers.This changes methods, constructors, and field-backed property setters to emit straight-line JNI invocation,
GC.KeepAlive, and return sequences when no exceptional-path cleanup exists. Bindings that dispose marshaler state or delete local JNI references retaintry/finally, and compatibility virtual methods retain theirNoSuchMethodErrortranslation. Generated baselines and targeted coverage verify both keep-alive-only and real-cleanup paths across JavaInterop1 and XAJavaInterop1.Fixes #12314
Pull Request
title and
description
should follow the
commit-messages.mdworkflow documentation, and in particular should include:Testing
dotnet test external/Java.Interop/tests/generator-Tests/generator-Tests.csproj(466 passed)