From 3cbe3a2d75f2e8435618afb49778f97c93eb0270 Mon Sep 17 00:00:00 2001 From: ousnius Date: Thu, 25 Sep 2025 00:03:47 +0200 Subject: [PATCH] Generate methods with untyped records as container-transfer return values "transfer-container" is usually used with GLib.List and GLib.SList as return values. The ownership of the returned container is transferred, but the ownership of the elements in the container is not. Example: gdk_pixbuf_get_formats --- .../ReturnType/Converter/UntypedRecord.cs | 1 + .../ReturnType/Converter/UntypedRecord.cs | 3 -- .../Converter/UntypedRecord.cs | 1 + .../girtest-untyped-record-tester.c | 45 +++++++++++++++++++ .../girtest-untyped-record-tester.h | 9 ++++ .../GirTest-0.1.Tests/UntypedRecordTest.cs | 14 ++++++ 6 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/UntypedRecord.cs b/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/UntypedRecord.cs index 6e1cdb80c..778164f42 100644 --- a/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/UntypedRecord.cs +++ b/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/UntypedRecord.cs @@ -18,6 +18,7 @@ public RenderableReturnType Convert(GirModel.ReturnType returnType) { { Transfer: Transfer.Full } => Model.TypedRecord.GetFullyQuallifiedOwnedHandle(type), { Transfer: Transfer.None } => Model.TypedRecord.GetFullyQuallifiedUnownedHandle(type), + { Transfer: Transfer.Container } => Model.TypedRecord.GetFullyQuallifiedOwnedHandle(type), _ => throw new Exception($"Unsupported transfer type {returnType.Transfer} for untyped record {type.Name}") }; diff --git a/src/Generation/Generator/Renderer/Public/ReturnType/Converter/UntypedRecord.cs b/src/Generation/Generator/Renderer/Public/ReturnType/Converter/UntypedRecord.cs index 538759d2e..b1c96ebb4 100644 --- a/src/Generation/Generator/Renderer/Public/ReturnType/Converter/UntypedRecord.cs +++ b/src/Generation/Generator/Renderer/Public/ReturnType/Converter/UntypedRecord.cs @@ -6,9 +6,6 @@ internal class UntypedRecord : ReturnTypeConverter { public RenderableReturnType Create(GirModel.ReturnType returnType) { - if (returnType.Transfer == GirModel.Transfer.Container) - throw new NotSupportedException($"Can't return untyped record {returnType} with transfer mode container"); - var typeName = ComplexType.GetFullyQualified((GirModel.Record) returnType.AnyType.AsT0); return new RenderableReturnType(typeName + Nullable.Render(returnType)); diff --git a/src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/Converter/UntypedRecord.cs b/src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/Converter/UntypedRecord.cs index d1c53597d..e9ac6384c 100644 --- a/src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/Converter/UntypedRecord.cs +++ b/src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/Converter/UntypedRecord.cs @@ -20,6 +20,7 @@ public void Initialize(ReturnTypeToManagedData data, IEnumerable fromVariableName, { Transfer: Transfer.None } => $"{fromVariableName}.Copy()", + { Transfer: Transfer.Container } => fromVariableName, _ => throw new NotImplementedException($"Unsupported transfer type '{returnType.Transfer}' for untyped record {record.Name}") }; diff --git a/src/Native/GirTestLib/girtest-untyped-record-tester.c b/src/Native/GirTestLib/girtest-untyped-record-tester.c index 0b322937f..50f328d83 100644 --- a/src/Native/GirTestLib/girtest-untyped-record-tester.c +++ b/src/Native/GirTestLib/girtest-untyped-record-tester.c @@ -6,6 +6,9 @@ * Test untyped records */ +static GirTestUntypedRecordTester staticElement1; +static GirTestUntypedRecordTester staticElement2; + /** * girtest_untyped_record_tester_new_with_a: (constructor) * @@ -99,6 +102,48 @@ void girtest_untyped_record_tester_out_parameter_caller_allocates(int v, GirTest record->a = v; } +/** + * girtest_untyped_record_tester_returns_transfer_container: + * Returns: (transfer container) (element-type GirTestUntypedRecordTester) + */ +GirTestUntypedRecordContainerTester* +girtest_untyped_record_tester_returns_transfer_container() +{ + GirTestUntypedRecordContainerTester *containerPtr1; + GirTestUntypedRecordContainerTester *containerPtr2; + + staticElement1.a = 1; + staticElement2.a = 2; + + containerPtr1 = g_slice_new0 (GirTestUntypedRecordContainerTester); + containerPtr2 = g_slice_new0 (GirTestUntypedRecordContainerTester); + + containerPtr1->data = &staticElement1; + containerPtr1->next = containerPtr2; + + containerPtr2->data = &staticElement2; + containerPtr2->next = NULL; + + return containerPtr1; +} + +/** + * girtest_untyped_record_tester_get_nth_container_data: + * @container: a #GirTestUntypedRecordContainerTester + * @n: the position of the element + * + * Returns: the element's data, or %NULL if the position + * is off the end of the #GSList + */ +GirTestUntypedRecordTester* +girtest_untyped_record_tester_get_nth_container_data(GirTestUntypedRecordContainerTester* container, guint n) +{ + while (n-- > 0 && container) + container = container->next; + + return container ? container->data : NULL; +} + /** * girtest_untyped_record_tester_callback_out_parameter_caller_allocates: * @callback: (scope call): a function that is called to create a new tester instance diff --git a/src/Native/GirTestLib/girtest-untyped-record-tester.h b/src/Native/GirTestLib/girtest-untyped-record-tester.h index 171bbb843..178b230e8 100644 --- a/src/Native/GirTestLib/girtest-untyped-record-tester.h +++ b/src/Native/GirTestLib/girtest-untyped-record-tester.h @@ -5,12 +5,19 @@ G_BEGIN_DECLS typedef struct _GirTestUntypedRecordTester GirTestUntypedRecordTester; +typedef struct _GirTestUntypedRecordContainerTester GirTestUntypedRecordContainerTester; struct _GirTestUntypedRecordTester { int a; }; +struct _GirTestUntypedRecordContainerTester +{ + GirTestUntypedRecordTester* data; + GirTestUntypedRecordContainerTester *next; +}; + /** * GirTestUntypedRecordCallbackOutParameterCallerAllocates: * @record:(out caller-allocates): the pointer to the integer value @@ -30,6 +37,8 @@ GirTestUntypedRecordTester * girtest_untyped_record_tester_nullable_mirror(GirTe int girtest_untyped_record_tester_get_a(GirTestUntypedRecordTester* record); int girtest_untyped_record_tester_get_a_nullable(int fallback, GirTestUntypedRecordTester* record); void girtest_untyped_record_tester_out_parameter_caller_allocates(int v, GirTestUntypedRecordTester *record); +GirTestUntypedRecordContainerTester* girtest_untyped_record_tester_returns_transfer_container(); +GirTestUntypedRecordTester* girtest_untyped_record_tester_get_nth_container_data(GirTestUntypedRecordContainerTester* container, guint n); GirTestUntypedRecordTester* girtest_untyped_record_tester_callback_out_parameter_caller_allocates(GirTestUntypedRecordCallbackOutParameterCallerAllocates callback); GirTestUntypedRecordTester* girtest_untyped_record_tester_callback_out_parameter_callee_allocates(GirTestUntypedRecordCallbackOutParameterCalleeAllocates callback); int girtest_untyped_record_tester_get_a_from_last_element(GirTestUntypedRecordTester* array, int length); diff --git a/src/Tests/Libs/GirTest-0.1.Tests/UntypedRecordTest.cs b/src/Tests/Libs/GirTest-0.1.Tests/UntypedRecordTest.cs index eb45b781f..025403746 100644 --- a/src/Tests/Libs/GirTest-0.1.Tests/UntypedRecordTest.cs +++ b/src/Tests/Libs/GirTest-0.1.Tests/UntypedRecordTest.cs @@ -103,6 +103,20 @@ public void SupportsReturnValueNullableTransferNone() mirror2.Should().BeNull(); } + [TestMethod] + public void SupportsReturnValueTransferContainer() + { + // Container ownership is transferred to here and then released + UntypedRecordContainerTester container = UntypedRecordTester.ReturnsTransferContainer(); + + // Elements of container are not owned and copied + UntypedRecordTester element1 = UntypedRecordTester.GetNthContainerData(container, 0); + element1!.A.Should().Be(1); + + UntypedRecordTester element2 = UntypedRecordTester.GetNthContainerData(container, 1); + element2!.A.Should().Be(2); + } + [TestMethod] public void SupportsRecordArrayAsParameter() {