diff --git a/src/Generation/Generator/Generator/Internal/TypedRecord.cs b/src/Generation/Generator/Generator/Internal/TypedRecord.cs new file mode 100644 index 000000000..2176dc1f0 --- /dev/null +++ b/src/Generation/Generator/Generator/Internal/TypedRecord.cs @@ -0,0 +1,29 @@ +using Generator.Model; + +namespace Generator.Generator.Internal; + +internal class TypedRecord : Generator +{ + private readonly Publisher _publisher; + + public TypedRecord(Publisher publisher) + { + _publisher = publisher; + } + + public void Generate(GirModel.Record obj) + { + if (!Record.IsTyped(obj)) + return; + + var source = Renderer.Internal.TypedRecord.Render(obj); + var codeUnit = new CodeUnit( + Project: Namespace.GetCanonicalName(obj.Namespace), + Name: obj.Name, + Source: source, + IsInternal: true + ); + + _publisher.Publish(codeUnit); + } +} diff --git a/src/Generation/Generator/Generator/Internal/TypedRecordData.cs b/src/Generation/Generator/Generator/Internal/TypedRecordData.cs new file mode 100644 index 000000000..77f95a5ef --- /dev/null +++ b/src/Generation/Generator/Generator/Internal/TypedRecordData.cs @@ -0,0 +1,29 @@ +using Generator.Model; + +namespace Generator.Generator.Internal; + +internal class TypedRecordData : Generator +{ + private readonly Publisher _publisher; + + public TypedRecordData(Publisher publisher) + { + _publisher = publisher; + } + + public void Generate(GirModel.Record obj) + { + if (!Record.IsTyped(obj)) + return; + + var source = Renderer.Internal.TypedRecordData.Render(obj); + var codeUnit = new CodeUnit( + Project: Namespace.GetCanonicalName(obj.Namespace), + Name: Model.TypedRecord.GetDataName(obj), + Source: source, + IsInternal: true + ); + + _publisher.Publish(codeUnit); + } +} diff --git a/src/Generation/Generator/Generator/Internal/TypedRecordDelegates.cs b/src/Generation/Generator/Generator/Internal/TypedRecordDelegates.cs new file mode 100644 index 000000000..9e1414b63 --- /dev/null +++ b/src/Generation/Generator/Generator/Internal/TypedRecordDelegates.cs @@ -0,0 +1,33 @@ +using System.Linq; +using Generator.Model; + +namespace Generator.Generator.Internal; + +internal class TypedRecordDelegates : Generator +{ + private readonly Publisher _publisher; + + public TypedRecordDelegates(Publisher publisher) + { + _publisher = publisher; + } + + public void Generate(GirModel.Record record) + { + if (!Record.IsTyped(record)) + return; + + if (!record.Fields.Any(field => field.AnyTypeOrCallback.IsT1)) + return; + + var source = Renderer.Internal.TypedRecordDelegates.Render(record); + var codeUnit = new CodeUnit( + Project: Namespace.GetCanonicalName(record.Namespace), + Name: $"{Model.TypedRecord.GetDataName(record)}.Delegates", + Source: source, + IsInternal: true + ); + + _publisher.Publish(codeUnit); + } +} diff --git a/src/Generation/Generator/Generator/Internal/TypedRecordHandle.cs b/src/Generation/Generator/Generator/Internal/TypedRecordHandle.cs new file mode 100644 index 000000000..313e53964 --- /dev/null +++ b/src/Generation/Generator/Generator/Internal/TypedRecordHandle.cs @@ -0,0 +1,29 @@ +using Generator.Model; + +namespace Generator.Generator.Internal; + +internal class TypedRecordHandle : Generator +{ + private readonly Publisher _publisher; + + public TypedRecordHandle(Publisher publisher) + { + _publisher = publisher; + } + + public void Generate(GirModel.Record obj) + { + if (!Record.IsTyped(obj)) + return; + + var source = Renderer.Internal.TypedRecordHandle.Render(obj); + var codeUnit = new CodeUnit( + Project: Namespace.GetCanonicalName(obj.Namespace), + Name: Model.TypedRecord.GetInternalHandle(obj), + Source: source, + IsInternal: true + ); + + _publisher.Publish(codeUnit); + } +} diff --git a/src/Generation/Generator/Generator/Public/TypedRecord.cs b/src/Generation/Generator/Generator/Public/TypedRecord.cs new file mode 100644 index 000000000..d8880ac1c --- /dev/null +++ b/src/Generation/Generator/Generator/Public/TypedRecord.cs @@ -0,0 +1,29 @@ +using Generator.Model; + +namespace Generator.Generator.Public; + +internal class TypedRecord : Generator +{ + private readonly Publisher _publisher; + + public TypedRecord(Publisher publisher) + { + _publisher = publisher; + } + + public void Generate(GirModel.Record record) + { + if (!Record.IsTyped(record)) + return; + + var source = Renderer.Public.TypedRecord.Render(record); + var codeUnit = new CodeUnit( + Project: Namespace.GetCanonicalName(record.Namespace), + Name: Record.GetPublicClassName(record), + Source: source, + IsInternal: false + ); + + _publisher.Publish(codeUnit); + } +} diff --git a/src/Generation/Generator/Model/Record.cs b/src/Generation/Generator/Model/Record.cs index 86bf13f6a..f5be36b7b 100644 --- a/src/Generation/Generator/Model/Record.cs +++ b/src/Generation/Generator/Model/Record.cs @@ -4,7 +4,7 @@ internal static partial class Record { public static bool IsStandard(GirModel.Record record) { - return !IsOpaqueTyped(record) && !IsOpaqueUntyped(record); + return !IsOpaqueTyped(record) && !IsOpaqueUntyped(record) && !IsTyped(record); } public static bool IsOpaqueTyped(GirModel.Record record) @@ -21,7 +21,15 @@ public static bool IsOpaqueUntyped(GirModel.Record record) //untyped. return record is { Opaque: true, TypeFunction: null or { CIdentifier: "intern" } }; } - + + public static bool IsTyped(GirModel.Record record) + { + //Even if there is a TypeFunction it does not mean that it actually is + //a typed / boxed record. There is a magic keyword "intern" which means this + //record is actually fundamental and does not have a type function. + return record is { Opaque: false, TypeFunction.CIdentifier: not "intern" }; + } + public static string GetFullyQualifiedInternalStructName(GirModel.Record record) => Namespace.GetInternalName(record.Namespace) + "." + GetInternalStructName(record); diff --git a/src/Generation/Generator/Model/TypedRecord.cs b/src/Generation/Generator/Model/TypedRecord.cs new file mode 100644 index 000000000..efaa06130 --- /dev/null +++ b/src/Generation/Generator/Model/TypedRecord.cs @@ -0,0 +1,80 @@ +using System.Linq; + +namespace Generator.Model; + +internal static class TypedRecord +{ + public static string GetPublicClassName(GirModel.Record record) + => record.Name; + + public static string GetFullyQualifiedPublicClassName(GirModel.Record record) + => Namespace.GetPublicName(record.Namespace) + "." + GetPublicClassName(record); + + public static string GetFullyQualifiedInternalClassName(GirModel.Record record) + => Namespace.GetInternalName(record.Namespace) + "." + record.Name; + + public static string GetInternalHandle(GirModel.Record record) + => $"{Type.GetName(record)}Handle"; + + public static string GetInternalOwnedHandle(GirModel.Record record) + => $"{Type.GetName(record)}OwnedHandle"; + + public static string GetInternalUnownedHandle(GirModel.Record record) + => $"{Type.GetName(record)}UnownedHandle"; + + public static string GetFullyQuallifiedHandle(GirModel.Record record) + => $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalHandle(record)}"; + + public static string GetFullyQuallifiedOwnedHandle(GirModel.Record record) + => $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalOwnedHandle(record)}"; + + public static string GetFullyQuallifiedUnownedHandle(GirModel.Record record) + => $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalUnownedHandle(record)}"; + + public static string GetFullyQuallifiedNullHandle(GirModel.Record record) + => $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalUnownedHandle(record)}.NullHandle"; + + public static string GetDataName(GirModel.Record record) + => $"{Type.GetName(record)}Data"; + + public static string GetFullyQuallifiedDataName(GirModel.Record record) + => $"{Namespace.GetInternalName(record.Namespace)}.{GetDataName(record)}"; + + public static string GetInternalArrayHandle(GirModel.Record record) + { + var prefix = $"{Type.GetName(record)}Array"; + if (record.Namespace.Records.Select(x => x.Name).Contains(prefix)) + prefix += "2"; + + return $"{prefix}Handle"; + } + + public static string GetFullyQuallifiedArrayHandle(GirModel.Record record) + => $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalArrayHandle(record)}"; + + public static string GetInternalArrayOwnedHandle(GirModel.Record record) + { + var prefix = $"{Type.GetName(record)}Array"; + if (record.Namespace.Records.Select(x => x.Name).Contains(prefix)) + prefix += "2"; + + return $"{prefix}OwnedHandle"; + } + + public static string GetFullyQuallifiedArrayOwnedHandle(GirModel.Record record) + => $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalArrayOwnedHandle(record)}"; + + public static string GetInternalArrayUnownedHandle(GirModel.Record record) + { + var prefix = $"{Type.GetName(record)}Array"; + if (record.Namespace.Records.Select(x => x.Name).Contains(prefix)) + prefix += "2"; + return $"{prefix}UnownedHandle"; + } + + public static string GetFullyQuallifiedArrayUnownedHandle(GirModel.Record record) + => $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalArrayUnownedHandle(record)}"; + + public static string GetFullyQuallifiedArrayNullHandle(GirModel.Record record) + => $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalArrayUnownedHandle(record)}.NullHandle"; +} diff --git a/src/Generation/Generator/Records.cs b/src/Generation/Generator/Records.cs index b59f7f2c0..2285b9f22 100644 --- a/src/Generation/Generator/Records.cs +++ b/src/Generation/Generator/Records.cs @@ -22,6 +22,13 @@ public static void Generate(IEnumerable records, string path) new Generator.Internal.OpaqueUntypedRecordHandle(publisher), new Generator.Public.OpaqueUntypedRecord(publisher), + //Typed records + new Generator.Internal.TypedRecord(publisher), + new Generator.Internal.TypedRecordDelegates(publisher), + new Generator.Internal.TypedRecordHandle(publisher), + new Generator.Internal.TypedRecordData(publisher), + new Generator.Public.TypedRecord(publisher), + //Regular records new Generator.Internal.RecordDelegates(publisher), new Generator.Internal.RecordHandle(publisher), diff --git a/src/Generation/Generator/Renderer/Internal/Field/Converter/OpaqueTypedRecord.cs b/src/Generation/Generator/Renderer/Internal/Field/Converter/OpaqueTypedRecord.cs new file mode 100644 index 000000000..aeff8ac14 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/Field/Converter/OpaqueTypedRecord.cs @@ -0,0 +1,18 @@ +namespace Generator.Renderer.Internal.Field; + +internal class OpaqueTypedRecord : FieldConverter +{ + public bool Supports(GirModel.Field field) + { + return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.Is(out var record) && Model.Record.IsOpaqueTyped(record); + } + + public RenderableField Convert(GirModel.Field field) + { + return new RenderableField( + Name: Model.Field.GetName(field), + Attribute: null, + NullableTypeName: Model.Type.Pointer + ); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/Field/Converter/OpaqueUntypedRecord.cs b/src/Generation/Generator/Renderer/Internal/Field/Converter/OpaqueUntypedRecord.cs new file mode 100644 index 000000000..f94c9de5f --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/Field/Converter/OpaqueUntypedRecord.cs @@ -0,0 +1,18 @@ +namespace Generator.Renderer.Internal.Field; + +internal class OpaqueUntypedRecord : FieldConverter +{ + public bool Supports(GirModel.Field field) + { + return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.Is(out var record) && Model.Record.IsOpaqueUntyped(record); + } + + public RenderableField Convert(GirModel.Field field) + { + return new RenderableField( + Name: Model.Field.GetName(field), + Attribute: null, + NullableTypeName: Model.Type.Pointer + ); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/Field/Converter/Record.cs b/src/Generation/Generator/Renderer/Internal/Field/Converter/Record.cs index 00f5feb77..d15d5e7a3 100644 --- a/src/Generation/Generator/Renderer/Internal/Field/Converter/Record.cs +++ b/src/Generation/Generator/Renderer/Internal/Field/Converter/Record.cs @@ -6,7 +6,7 @@ internal class Record : FieldConverter { public bool Supports(GirModel.Field field) { - return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.Is(); + return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.Is(out var record) && Model.Record.IsStandard(record); } public RenderableField Convert(GirModel.Field field) diff --git a/src/Generation/Generator/Renderer/Internal/Field/Converter/RecordArray.cs b/src/Generation/Generator/Renderer/Internal/Field/Converter/RecordArray.cs index 190fefe61..6bb009259 100644 --- a/src/Generation/Generator/Renderer/Internal/Field/Converter/RecordArray.cs +++ b/src/Generation/Generator/Renderer/Internal/Field/Converter/RecordArray.cs @@ -4,7 +4,7 @@ internal class RecordArray : FieldConverter { public bool Supports(GirModel.Field field) { - return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.IsArray(); + return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.IsArray(out var record) && Model.Record.IsStandard(record);; } public RenderableField Convert(GirModel.Field field) diff --git a/src/Generation/Generator/Renderer/Internal/Field/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Internal/Field/Converter/TypedRecord.cs new file mode 100644 index 000000000..23de49944 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/Field/Converter/TypedRecord.cs @@ -0,0 +1,28 @@ +using Generator.Model; + +namespace Generator.Renderer.Internal.Field; + +internal class TypedRecord : FieldConverter +{ + public bool Supports(GirModel.Field field) + { + return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.Is(out var record) && Model.Record.IsTyped(record); + } + + public RenderableField Convert(GirModel.Field field) + { + return new RenderableField( + Name: Model.Field.GetName(field), + Attribute: null, + NullableTypeName: GetNullableTypeName(field) + ); + } + + private static string GetNullableTypeName(GirModel.Field field) + { + var type = (GirModel.Record) field.AnyTypeOrCallback.AsT0.AsT0; + return field.IsPointer + ? Type.Pointer + : Model.Record.GetFullyQualifiedInternalStructName(type); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/Field/Converter/TypedRecordArray.cs b/src/Generation/Generator/Renderer/Internal/Field/Converter/TypedRecordArray.cs new file mode 100644 index 000000000..af2670ed7 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/Field/Converter/TypedRecordArray.cs @@ -0,0 +1,33 @@ +namespace Generator.Renderer.Internal.Field; + +internal class TypedRecordArray : FieldConverter +{ + public bool Supports(GirModel.Field field) + { + return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.IsArray(out var record) && Model.Record.IsTyped(record);; + } + + public RenderableField Convert(GirModel.Field field) + { + return new RenderableField( + Name: Model.Field.GetName(field), + Attribute: GetAttribute(field), + NullableTypeName: GetNullableTypeName(field) + ); + } + + private static string? GetAttribute(GirModel.Field field) + { + var arrayType = field.AnyTypeOrCallback.AsT0.AsT1; + return arrayType.FixedSize is not null + ? MarshalAs.UnmanagedByValArray(sizeConst: arrayType.FixedSize.Value) + : null; + } + + private static string GetNullableTypeName(GirModel.Field field) + { + var arrayType = field.AnyTypeOrCallback.AsT0.AsT1; + var type = (GirModel.Record) arrayType.AnyType.AsT0; + return Model.Record.GetFullyQualifiedInternalStructName(type) + "[]"; + } +} diff --git a/src/Generation/Generator/Renderer/Internal/Field/Fields.cs b/src/Generation/Generator/Renderer/Internal/Field/Fields.cs index 2535b6ff2..d9970d09d 100644 --- a/src/Generation/Generator/Renderer/Internal/Field/Fields.cs +++ b/src/Generation/Generator/Renderer/Internal/Field/Fields.cs @@ -14,6 +14,8 @@ internal static class Fields new Field.ClassArray(), new Field.Enumeration(), new Field.EnumerationArray(), + new Field.OpaqueTypedRecord(), + new Field.OpaqueUntypedRecord(), new Field.Pointer(), new Field.PointerAlias(), new Field.PointerArray(), @@ -25,6 +27,8 @@ internal static class Fields new Field.RecordArray(), new Field.String(), new Field.StringArray(), + new Field.TypedRecord(), + new Field.TypedRecordArray(), new Field.Union(), new Field.UnionArray(), }; diff --git a/src/Generation/Generator/Renderer/Internal/InstanceParameter/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Internal/InstanceParameter/Converter/TypedRecord.cs new file mode 100644 index 000000000..52c46a07c --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/InstanceParameter/Converter/TypedRecord.cs @@ -0,0 +1,28 @@ +namespace Generator.Renderer.Internal.InstanceParameter; + +internal class TypedRecord : InstanceParameterConverter +{ + public bool Supports(GirModel.Type type) + { + return type is GirModel.Record r && Model.Record.IsTyped(r); + } + + public RenderableInstanceParameter Convert(GirModel.InstanceParameter instanceParameter) + { + return new RenderableInstanceParameter( + Name: Model.InstanceParameter.GetName(instanceParameter), + NullableTypeName: GetNullableTypeName(instanceParameter) + ); + } + + private static string GetNullableTypeName(GirModel.InstanceParameter instanceParameter) + { + var type = (GirModel.Record) instanceParameter.Type; + return instanceParameter switch + { + { Direction: GirModel.Direction.In, Transfer: GirModel.Transfer.None } => Model.TypedRecord.GetFullyQuallifiedHandle(type), + { Direction: GirModel.Direction.In, Transfer: GirModel.Transfer.Full } => Model.TypedRecord.GetFullyQuallifiedUnownedHandle(type), + _ => throw new System.Exception($"Can't detect typed record instance parameter type {instanceParameter.Name}: CallerAllocates={instanceParameter.CallerAllocates} Direction={instanceParameter.Direction} Transfer={instanceParameter.Transfer}") + }; + } +} diff --git a/src/Generation/Generator/Renderer/Internal/InstanceParameter/InstanceParameters.cs b/src/Generation/Generator/Renderer/Internal/InstanceParameter/InstanceParameters.cs index a3907d6bf..6222183a4 100644 --- a/src/Generation/Generator/Renderer/Internal/InstanceParameter/InstanceParameters.cs +++ b/src/Generation/Generator/Renderer/Internal/InstanceParameter/InstanceParameters.cs @@ -13,6 +13,7 @@ internal static class InstanceParameters new InstanceParameter.OpaqueUntypedRecord(), new InstanceParameter.Pointer(), new InstanceParameter.Record(), + new InstanceParameter.TypedRecord(), new InstanceParameter.Union() }; diff --git a/src/Generation/Generator/Renderer/Internal/Parameter/CallbackParameters.cs b/src/Generation/Generator/Renderer/Internal/Parameter/CallbackParameters.cs index 305f8cef9..b771ce1a6 100644 --- a/src/Generation/Generator/Renderer/Internal/Parameter/CallbackParameters.cs +++ b/src/Generation/Generator/Renderer/Internal/Parameter/CallbackParameters.cs @@ -36,6 +36,8 @@ internal static class CallbackParameters new Parameter.RecordCallback(), //Callbacks do not support record safe handles in parameters new Parameter.RecordGLibPtrArray(), new Parameter.String(), + new Parameter.TypedRecordCallback(), + new Parameter.TypedRecordCallbackArray(), new Parameter.Union(), new Parameter.UnionArray(), new Parameter.UnsignedPointer(), diff --git a/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecord.cs new file mode 100644 index 000000000..a82f4339a --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecord.cs @@ -0,0 +1,41 @@ +using System; + +namespace Generator.Renderer.Internal.Parameter; + +internal class TypedRecord : ParameterConverter +{ + public bool Supports(GirModel.AnyType anyType) + { + return anyType.Is(out var record) && Model.Record.IsTyped(record); + } + + public RenderableParameter Convert(GirModel.Parameter parameter) + { + return new RenderableParameter( + Attribute: string.Empty, + Direction: GetDirection(parameter), + NullableTypeName: GetNullableTypeName(parameter), + Name: Model.Parameter.GetName(parameter) + ); + } + + private static string GetNullableTypeName(GirModel.Parameter parameter) + { + //Native records are represented as SafeHandles and are not nullable + + var type = (GirModel.Record) parameter.AnyTypeOrVarArgs.AsT0.AsT0; + return parameter switch + { + { Direction: GirModel.Direction.In, Transfer: GirModel.Transfer.None } => Model.TypedRecord.GetFullyQuallifiedHandle(type), + { Direction: GirModel.Direction.In, Transfer: GirModel.Transfer.Full } => Model.TypedRecord.GetFullyQuallifiedUnownedHandle(type), + _ => throw new Exception($"Can't detect record parameter type {parameter.Name}: CallerAllocates={parameter.CallerAllocates} Direction={parameter.Direction} Transfer={parameter.Transfer}") + }; + } + + private static string GetDirection(GirModel.Parameter parameter) => parameter switch + { + { Direction: GirModel.Direction.In } => ParameterDirection.In(), + { Direction: GirModel.Direction.InOut } => ParameterDirection.In(), + _ => throw new Exception($"Unknown parameter direction for opaque typed record parameter {parameter.Name}") + }; +} diff --git a/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordArray.cs b/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordArray.cs new file mode 100644 index 000000000..c2fb87bfe --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordArray.cs @@ -0,0 +1,41 @@ +using System; + +namespace Generator.Renderer.Internal.Parameter; + +internal class TypedRecordArray : ParameterConverter +{ + public bool Supports(GirModel.AnyType anyType) + { + return anyType.IsArray(out var record) && Model.Record.IsTyped(record); + } + + public RenderableParameter Convert(GirModel.Parameter parameter) + { + if (parameter.AnyTypeOrVarArgs.AsT0.AsT1.IsPointer) + return PointerArray(parameter); + + return StructArray(parameter); + } + + private static RenderableParameter PointerArray(GirModel.Parameter parameter) + { + return new RenderableParameter( + Attribute: string.Empty, + Direction: string.Empty, + NullableTypeName: $"ref {Model.Type.Pointer}", + Name: Model.Parameter.GetName(parameter) + ); + } + + private static RenderableParameter StructArray(GirModel.Parameter parameter) + { + var record = (GirModel.Record) parameter.AnyTypeOrVarArgs.AsT0.AsT1.AnyType.AsT0; + + return new RenderableParameter( + Attribute: string.Empty, + Direction: string.Empty, + NullableTypeName: Model.TypedRecord.GetFullyQuallifiedArrayHandle(record), + Name: Model.Parameter.GetName(parameter) + ); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordCallback.cs b/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordCallback.cs new file mode 100644 index 000000000..ffd774957 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordCallback.cs @@ -0,0 +1,30 @@ +using System; + +namespace Generator.Renderer.Internal.Parameter; + +internal class TypedRecordCallback : ParameterConverter +{ + public bool Supports(GirModel.AnyType anyType) + { + return anyType.Is(out var record) && Model.Record.IsTyped(record); + } + + public RenderableParameter Convert(GirModel.Parameter parameter) + { + return new RenderableParameter( + Attribute: string.Empty, + Direction: GetDirection(parameter), + NullableTypeName: Model.Type.Pointer, + Name: Model.Parameter.GetName(parameter) + ); + } + + private static string GetDirection(GirModel.Parameter parameter) => parameter switch + { + { Direction: GirModel.Direction.In } => ParameterDirection.In(), + { Direction: GirModel.Direction.InOut } => ParameterDirection.In(), + { Direction: GirModel.Direction.Out, CallerAllocates: true } => ParameterDirection.In(), + { Direction: GirModel.Direction.Out } => ParameterDirection.Out(), + _ => throw new Exception("Unknown direction for record parameter in callback") + }; +} diff --git a/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordCallbackArray.cs b/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordCallbackArray.cs new file mode 100644 index 000000000..f15a9f2e8 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordCallbackArray.cs @@ -0,0 +1,39 @@ +using System; + +namespace Generator.Renderer.Internal.Parameter; + +internal class TypedRecordCallbackArray : ParameterConverter +{ + public bool Supports(GirModel.AnyType anyType) + { + return anyType.IsArray(out var record) && Model.Record.IsTyped(record); + } + + public RenderableParameter Convert(GirModel.Parameter parameter) + { + if (parameter.AnyTypeOrVarArgs.AsT0.AsT1.IsPointer) + return PointerArray(parameter); + + return StructArray(parameter); + } + + private static RenderableParameter PointerArray(GirModel.Parameter parameter) + { + return new RenderableParameter( + Attribute: string.Empty, + Direction: string.Empty, + NullableTypeName: $"ref {Model.Type.Pointer}", + Name: Model.Parameter.GetName(parameter) + ); + } + + private static RenderableParameter StructArray(GirModel.Parameter parameter) + { + return new RenderableParameter( + Attribute: string.Empty, + Direction: string.Empty, + NullableTypeName: Model.Type.Pointer, + Name: Model.Parameter.GetName(parameter) + ); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/Parameter/Parameters.cs b/src/Generation/Generator/Renderer/Internal/Parameter/Parameters.cs index 31a552824..ea3d53030 100644 --- a/src/Generation/Generator/Renderer/Internal/Parameter/Parameters.cs +++ b/src/Generation/Generator/Renderer/Internal/Parameter/Parameters.cs @@ -41,6 +41,8 @@ internal static class Parameters new Parameter.RecordGLibPtrArray(), new Parameter.String(), new Parameter.StringGLibPtrArray(), + new Parameter.TypedRecord(), + new Parameter.TypedRecordArray(), new Parameter.Union(), new Parameter.UnionArray(), new Parameter.UnsignedPointer(), diff --git a/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/Converter/TypedRecord.cs new file mode 100644 index 000000000..7704b4d86 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/Converter/TypedRecord.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; + +namespace Generator.Renderer.Internal.ParameterToManagedExpressions; + +internal class TypedRecord : ToManagedParameterConverter +{ + public bool Supports(GirModel.AnyType type) + => type.Is(out var record) && Model.Record.IsTyped(record); + + public void Initialize(ParameterToManagedData parameterData, IEnumerable parameters) + { + if (parameterData.Parameter.Direction != GirModel.Direction.In) + throw new NotImplementedException($"{parameterData.Parameter.AnyTypeOrVarArgs}: typed record with direction != in not yet supported"); + + var record = (GirModel.Record) parameterData.Parameter.AnyTypeOrVarArgs.AsT0.AsT0; + var variableName = Model.Parameter.GetConvertedName(parameterData.Parameter); + + var signatureName = Model.Parameter.GetName(parameterData.Parameter); + + var ownedHandle = parameterData.Parameter switch + { + { Transfer: GirModel.Transfer.Full } => $"new {Model.TypedRecord.GetFullyQuallifiedOwnedHandle(record)}({signatureName})", + { Transfer: GirModel.Transfer.None } => $"{Model.TypedRecord.GetFullyQuallifiedOwnedHandle(record)}.FromUnowned({signatureName})", + _ => throw new Exception($"Unknown transfer type for typed record parameter {parameterData.Parameter.Name}") + }; + + var nullable = parameterData.Parameter.Nullable + ? $" {signatureName} == IntPtr.Zero ? null :" + : string.Empty; + + parameterData.SetSignatureName(signatureName); + parameterData.SetExpression($"var {variableName} ={nullable} new {Model.TypedRecord.GetFullyQualifiedPublicClassName(record)}({ownedHandle});"); + parameterData.SetCallName(variableName); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/Converter/TypedRecordArray.cs b/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/Converter/TypedRecordArray.cs new file mode 100644 index 000000000..f2cd2d35a --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/Converter/TypedRecordArray.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Generator.Renderer.Internal.ParameterToManagedExpressions; + +internal class TypedRecordArray : ToManagedParameterConverter +{ + public bool Supports(GirModel.AnyType type) + => type.IsArray(out var record) && Model.Record.IsTyped(record); + + public void Initialize(ParameterToManagedData parameterData, IEnumerable parameters) + { + switch (parameterData.Parameter) + { + case { Direction: GirModel.Direction.In } + when parameterData.Parameter.AnyTypeOrVarArgs.AsT0.AsT1.Length is not null: + WithLength(parameterData, parameters); + break; + case { Direction: GirModel.Direction.In }: + WithoutLength(parameterData); + break; + default: + throw new Exception($"{parameterData.Parameter}: This kind of typed record array is not yet supported"); + } + } + + private static void WithoutLength(ParameterToManagedData parameter) + { + var parameterName = Model.Parameter.GetName(parameter.Parameter); + parameter.SetSignatureName(parameterName); + parameter.SetCallName($"ref {parameterName}"); + + //TODO + throw new Exception("Test missing for typed record array passed in via a ref to managed"); + } + + private static void WithLength(ParameterToManagedData parameter, IEnumerable allParameters) + { + if (parameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT1.IsPointer) + PointerArrayWithLength(parameter, allParameters); + else + StructArrayWithLength(parameter, allParameters); + } + + private static void PointerArrayWithLength(ParameterToManagedData parameter, IEnumerable allParameters) + { + throw new Exception("Pointer array not yet supported for typed record arrays"); + } + + private static void StructArrayWithLength(ParameterToManagedData parameter, IEnumerable allParameters) + { + if (parameter.Parameter.Transfer == GirModel.Transfer.Container || parameter.Parameter.Transfer == GirModel.Transfer.Full) + throw new Exception("Can't transfer ownership to native code for typed record"); + + var record = (GirModel.Record) parameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT1.AnyType.AsT0; + var parameterName = Model.Parameter.GetName(parameter.Parameter); + var nativeVariableName = parameterName + "Native"; + + parameter.SetSignatureName(parameterName); + parameter.SetCallName(nativeVariableName); + + var nullable = parameter.Parameter.Nullable + ? $"{parameterName} is null ? ({Model.TypedRecord.GetFullyQuallifiedArrayHandle(record)}){Model.TypedRecord.GetFullyQuallifiedArrayNullHandle(record)} : " + : string.Empty; + + var lengthIndex = parameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT1.Length ?? throw new Exception("Length missing"); + var lengthParameter = allParameters.ElementAt(lengthIndex); + + parameter.SetExpression($"var {nativeVariableName} = new {Model.TypedRecord.GetFullyQuallifiedArrayUnownedHandle(record)}({parameterName});"); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/ParameterToManagedExpression.cs b/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/ParameterToManagedExpression.cs index 6831f53a0..f9b175212 100644 --- a/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/ParameterToManagedExpression.cs +++ b/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/ParameterToManagedExpression.cs @@ -25,6 +25,8 @@ internal static class ParameterToManagedExpression new ParameterToManagedExpressions.Record(), new ParameterToManagedExpressions.RecordArray(), new ParameterToManagedExpressions.String(), + new ParameterToManagedExpressions.TypedRecord(), + new ParameterToManagedExpressions.TypedRecordArray(), new ParameterToManagedExpressions.Utf8StringArray(), }; diff --git a/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/TypedRecord.cs new file mode 100644 index 000000000..3c92ed109 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/TypedRecord.cs @@ -0,0 +1,25 @@ +using GirModel; + +namespace Generator.Renderer.Internal.ReturnType; + +internal class TypedRecord : ReturnTypeConverter +{ + public bool Supports(GirModel.ReturnType returnType) + { + return returnType.AnyType.Is(out var record) && Model.Record.IsTyped(record); + } + + public RenderableReturnType Convert(GirModel.ReturnType returnType) + { + var type = (GirModel.Record) returnType.AnyType.AsT0; + + var typeName = returnType switch + { + { Transfer: Transfer.Full } => Model.TypedRecord.GetFullyQuallifiedOwnedHandle(type), + _ => Model.TypedRecord.GetFullyQuallifiedUnownedHandle(type) + }; + + //Returned SafeHandles are never "null" but "invalid" in case of C NULL. + return new RenderableReturnType(typeName); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/TypedRecordCallback.cs b/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/TypedRecordCallback.cs new file mode 100644 index 000000000..7fa30b97e --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/TypedRecordCallback.cs @@ -0,0 +1,14 @@ +namespace Generator.Renderer.Internal.ReturnType; + +internal class TypedRecordCallback : ReturnTypeConverter +{ + public bool Supports(GirModel.ReturnType returnType) + { + return returnType.AnyType.Is(out var record) && Model.Record.IsTyped(record); + } + + public RenderableReturnType Convert(GirModel.ReturnType returnType) + { + return new RenderableReturnType(Model.Type.Pointer); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/ReturnType/ReturnTypeRenderer.cs b/src/Generation/Generator/Renderer/Internal/ReturnType/ReturnTypeRenderer.cs index 6e290ee9d..5794c1523 100644 --- a/src/Generation/Generator/Renderer/Internal/ReturnType/ReturnTypeRenderer.cs +++ b/src/Generation/Generator/Renderer/Internal/ReturnType/ReturnTypeRenderer.cs @@ -23,6 +23,7 @@ internal static class ReturnTypeRenderer new ReturnType.PrimitiveValueTypeArray(), new ReturnType.Record(), new ReturnType.RecordArray(), + new ReturnType.TypedRecord(), new ReturnType.Union(), new ReturnType.Utf8String(), new ReturnType.Utf8StringArray(), diff --git a/src/Generation/Generator/Renderer/Internal/ReturnType/ReturnTypeRendererCallback.cs b/src/Generation/Generator/Renderer/Internal/ReturnType/ReturnTypeRendererCallback.cs index 0dd765b1d..d79c490ae 100644 --- a/src/Generation/Generator/Renderer/Internal/ReturnType/ReturnTypeRendererCallback.cs +++ b/src/Generation/Generator/Renderer/Internal/ReturnType/ReturnTypeRendererCallback.cs @@ -22,6 +22,7 @@ internal static class ReturnTypeRendererCallback new ReturnType.PrimitiveValueTypeArray(), new ReturnType.RecordArray(), new ReturnType.RecordInCallback(), + new ReturnType.TypedRecordCallback(), new ReturnType.Union(), new ReturnType.Utf8StringInCallback(), new ReturnType.Utf8StringArrayInCallback(), diff --git a/src/Generation/Generator/Renderer/Internal/ReturnTypeToNativeExpression/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Internal/ReturnTypeToNativeExpression/Converter/TypedRecord.cs new file mode 100644 index 000000000..9a9ec1d7a --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/ReturnTypeToNativeExpression/Converter/TypedRecord.cs @@ -0,0 +1,21 @@ +using System; + +namespace Generator.Renderer.Internal.ReturnTypeToNativeExpressions; + +internal class TypedRecord : ReturnTypeConverter +{ + public bool Supports(GirModel.AnyType type) + => type.Is(out var record) && Model.Record.IsTyped(record); + + public string GetString(GirModel.ReturnType returnType, string fromVariableName) + { + return returnType switch + { + { Transfer: GirModel.Transfer.None, Nullable: true } => $"{fromVariableName}?.Handle.DangerousGetHandle() ?? IntPtr.Zero", + { Transfer: GirModel.Transfer.None, Nullable: false } => $"{fromVariableName}.Handle.DangerousGetHandle()", + { Transfer: GirModel.Transfer.Full, Nullable: true } => $"{fromVariableName}?.Handle.UnownedCopy().DangerousGetHandle() ?? IntPtr.Zero", + { Transfer: GirModel.Transfer.Full, Nullable: false } => $"{fromVariableName}.Handle.UnownedCopy().DangerousGetHandle()", + _ => throw new Exception($"Unknown transfer type for record return type which should be converted to native.") + }; + } +} diff --git a/src/Generation/Generator/Renderer/Internal/ReturnTypeToNativeExpression/ReturnTypeToNativeExpression.cs b/src/Generation/Generator/Renderer/Internal/ReturnTypeToNativeExpression/ReturnTypeToNativeExpression.cs index dff6039c5..8b58d46c5 100644 --- a/src/Generation/Generator/Renderer/Internal/ReturnTypeToNativeExpression/ReturnTypeToNativeExpression.cs +++ b/src/Generation/Generator/Renderer/Internal/ReturnTypeToNativeExpression/ReturnTypeToNativeExpression.cs @@ -16,6 +16,7 @@ internal static class ReturnTypeToNativeExpression new ReturnTypeToNativeExpressions.PrimitiveValueType(), new ReturnTypeToNativeExpressions.PrimitiveValueTypeAlias(), new ReturnTypeToNativeExpressions.Record(), + new ReturnTypeToNativeExpressions.TypedRecord(), new ReturnTypeToNativeExpressions.Utf8String(), }; diff --git a/src/Generation/Generator/Renderer/Internal/TypedRecord.cs b/src/Generation/Generator/Renderer/Internal/TypedRecord.cs new file mode 100644 index 000000000..d3fa8c473 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/TypedRecord.cs @@ -0,0 +1,30 @@ +using Generator.Model; + +namespace Generator.Renderer.Internal; + +internal static class TypedRecord +{ + public static string Render(GirModel.Record record) + { + return $@" +using System; +using GObject; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +#nullable enable + +namespace {Namespace.GetInternalName(record.Namespace)}; + +// AUTOGENERATED FILE - DO NOT MODIFY + +{PlatformSupportAttribute.Render(record as GirModel.PlatformDependent)} +public partial class {record.Name} +{{ + {Constructors.Render(record.Constructors)} + {Functions.Render(record.TypeFunction)} + {Functions.Render(record.Functions)} + {Methods.Render(record.Methods)} +}}"; + } +} diff --git a/src/Generation/Generator/Renderer/Internal/TypedRecordData.cs b/src/Generation/Generator/Renderer/Internal/TypedRecordData.cs new file mode 100644 index 000000000..573ccd3a6 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/TypedRecordData.cs @@ -0,0 +1,27 @@ +using Generator.Model; + +namespace Generator.Renderer.Internal; + +internal static class TypedRecordData +{ + public static string Render(GirModel.Record record) + { + return $@" +using System; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +#nullable enable + +namespace {Namespace.GetInternalName(record.Namespace)}; + +// AUTOGENERATED FILE - DO NOT MODIFY + +{PlatformSupportAttribute.Render(record as GirModel.PlatformDependent)} +[StructLayout(LayoutKind.Sequential)] +public partial struct {Model.TypedRecord.GetDataName(record)} +{{ + {Fields.Render(record.Fields)} +}}"; + } +} diff --git a/src/Generation/Generator/Renderer/Internal/TypedRecordDelegates.cs b/src/Generation/Generator/Renderer/Internal/TypedRecordDelegates.cs new file mode 100644 index 000000000..af1c04efc --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/TypedRecordDelegates.cs @@ -0,0 +1,31 @@ +using System; +using System.Linq; +using Generator.Model; + +namespace Generator.Renderer.Internal; + +internal static class TypedRecordDelegates +{ + public static string Render(GirModel.Record record) + { + return $@" +using System; +using System.Runtime.InteropServices; + +#nullable enable + +namespace {Namespace.GetInternalName(record.Namespace)}; + +// AUTOGENERATED FILE - DO NOT MODIFY + +public partial struct {Model.TypedRecord.GetDataName(record)} +{{ + {record.Fields + .Select(x => x.AnyTypeOrCallback) + .Where(x => x.IsT1) + .Select(x => x.AsT1) + .Select(Callback.Render) + .Join(Environment.NewLine)} +}}"; + } +} diff --git a/src/Generation/Generator/Renderer/Internal/TypedRecordHandle.cs b/src/Generation/Generator/Renderer/Internal/TypedRecordHandle.cs new file mode 100644 index 000000000..80826dfd1 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/TypedRecordHandle.cs @@ -0,0 +1,187 @@ +using Generator.Model; + +namespace Generator.Renderer.Internal; + +internal static class TypedRecordHandle +{ + public static string Render(GirModel.Record record) + { + var typeName = Model.TypedRecord.GetInternalHandle(record); + var unownedHandleTypeName = Model.TypedRecord.GetInternalUnownedHandle(record); + var ownedHandleTypeName = Model.TypedRecord.GetInternalOwnedHandle(record); + var arrayHandleType = Model.TypedRecord.GetInternalArrayHandle(record); + var arrayUnownedHandleTypeName = Model.TypedRecord.GetInternalArrayUnownedHandle(record); + var arrayOwnedHandleTypeName = Model.TypedRecord.GetInternalArrayOwnedHandle(record); + var fullyQualifiedType = $"{Model.TypedRecord.GetFullyQualifiedPublicClassName(record)}"; + var getGType = $"{Model.TypedRecord.GetFullyQualifiedInternalClassName(record)}.{Function.GetGType}()"; + + return $@"using System; +using GObject; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +#nullable enable + +namespace {Namespace.GetInternalName(record.Namespace)}; + +// AUTOGENERATED FILE - DO NOT MODIFY + +{PlatformSupportAttribute.Render(record as GirModel.PlatformDependent)} +public abstract class {typeName} : SafeHandle +{{ + public sealed override bool IsInvalid => handle == IntPtr.Zero; + + protected {typeName}(bool ownsHandle) : base(IntPtr.Zero, ownsHandle) {{ }} + + public {ownedHandleTypeName} OwnedCopy() + {{ + var ptr = GObject.Internal.Functions.BoxedCopy({getGType}, handle); + return new {ownedHandleTypeName}(ptr); + }} + + public {unownedHandleTypeName} UnownedCopy() + {{ + var ptr = GObject.Internal.Functions.BoxedCopy({getGType}, handle); + return new {unownedHandleTypeName}(ptr); + }} +}} + +public class {unownedHandleTypeName} : {typeName} +{{ + private static {unownedHandleTypeName}? nullHandle; + public static {unownedHandleTypeName} NullHandle => nullHandle ??= new {unownedHandleTypeName}(); + + /// + /// Creates a new instance of {unownedHandleTypeName}. Used automatically by PInvoke. + /// + internal {unownedHandleTypeName}() : base(false) {{ }} + + /// + /// Creates a new instance of {ownedHandleTypeName}. Assumes that the given pointer is unowned by the runtime. + /// + public {unownedHandleTypeName}(IntPtr ptr) : base(false) + {{ + SetHandle(ptr); + }} + + protected override bool ReleaseHandle() + {{ + throw new Exception(""UnownedHandle must not be freed""); + }} +}} + +public class {ownedHandleTypeName} : {typeName} +{{ + /// + /// Creates a new instance of {ownedHandleTypeName}. Used automatically by PInvoke. + /// + internal {ownedHandleTypeName}() : base(true) {{ }} + + /// + /// Creates a new instance of {ownedHandleTypeName}. Assumes that the given pointer is owned by the runtime. + /// + public {ownedHandleTypeName}(IntPtr ptr) : base(true) + {{ + SetHandle(ptr); + }} + + /// + /// Create a {ownedHandleTypeName} from a pointer that is assumed unowned. To do so a + /// boxed copy is created of the given pointer to be used as the handle. + /// + /// A pointer to a {record.Name} which is not owned by the runtime. + /// A {ownedHandleTypeName} + public static {ownedHandleTypeName} FromUnowned(IntPtr ptr) + {{ + var ownedPtr = GObject.Internal.Functions.BoxedCopy({getGType}, ptr); + return new {ownedHandleTypeName}(ownedPtr); + }} + + protected override bool ReleaseHandle() + {{ + GObject.Internal.Functions.BoxedFree({getGType}, handle); + return true; + }} +}} + +public abstract class {arrayHandleType} : SafeHandle +{{ + public sealed override bool IsInvalid => handle == IntPtr.Zero; + + protected {arrayHandleType}(bool ownsHandle) : base(IntPtr.Zero, ownsHandle) {{ }} +}} + +public class {arrayUnownedHandleTypeName} : {arrayHandleType} +{{ + private static {arrayUnownedHandleTypeName}? nullHandle; + public static {arrayUnownedHandleTypeName} NullHandle => nullHandle ??= new {arrayUnownedHandleTypeName}(); + + private int length; + + /// + /// Creates a new instance of {arrayUnownedHandleTypeName}. Used automatically by PInvoke. + /// + internal {arrayUnownedHandleTypeName}() : base(true) {{ }} + + public {arrayUnownedHandleTypeName}(IntPtr ptr, int length) : base(false) + {{ + this.length = length; + SetHandle(ptr); + }} + + public {fullyQualifiedType}[] ToArray(int length) + {{ + var data = new {fullyQualifiedType}[length]; + var currentHandle = handle; + for(int i = 0; i < length; i++) + {{ +//!!!!!!!!!!!!!!!!!!!! +//TODO Clarify OwnedCopy()!!!!!!!!!!!!!!! +//!!!!!!!!!!!!!!!!!!!!! + var ownedHandle = new {Model.TypedRecord.GetFullyQuallifiedOwnedHandle(record)}(currentHandle).OwnedCopy(); + data[i] = new {fullyQualifiedType}(ownedHandle); + currentHandle += Marshal.SizeOf<{Model.TypedRecord.GetFullyQuallifiedDataName(record)}>(); + }} + + return data; + }} + + protected override bool ReleaseHandle() + {{ + throw new Exception(""UnownedHandle must not be freed""); + }} +}} + +public class {arrayOwnedHandleTypeName} : {arrayHandleType} +{{ + + //This has no constructor without parameters as we can't supply a length to an array via pinvoke. + //The length would need to be set manually and the instance be freed via glib. + + private {arrayOwnedHandleTypeName}(IntPtr ptr) : base(true) + {{ + SetHandle(ptr); + }} + + public static {arrayOwnedHandleTypeName} Create({Model.TypedRecord.GetFullyQualifiedPublicClassName(record)}[] data) + {{ + var size = Marshal.SizeOf<{Model.TypedRecord.GetFullyQuallifiedDataName(record)}>(); + var ptr = Marshal.AllocHGlobal(size * data.Length); + var current = ptr; + for (int i = 0; i < data.Length; i++) + {{ + Marshal.StructureToPtr(data[i], current, false); + current += size; + }} + + return new {arrayOwnedHandleTypeName}(ptr); + }} + + protected override bool ReleaseHandle() + {{ + Marshal.FreeHGlobal(handle); + return true; + }} +}}"; + } +} diff --git a/src/Generation/Generator/Renderer/Public/InstanceParameterToNativeExpression/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Public/InstanceParameterToNativeExpression/Converter/TypedRecord.cs new file mode 100644 index 000000000..1cbe05c7b --- /dev/null +++ b/src/Generation/Generator/Renderer/Public/InstanceParameterToNativeExpression/Converter/TypedRecord.cs @@ -0,0 +1,21 @@ +using System; + +namespace Generator.Renderer.Public.InstanceParameterToNativeExpressions; + +public class TypedRecord : InstanceParameterConverter +{ + public bool Supports(GirModel.Type type) + { + return type is GirModel.Record record && Model.Record.IsTyped(record); + } + + public string GetExpression(GirModel.InstanceParameter instanceParameter) + { + return instanceParameter switch + { + { Transfer: GirModel.Transfer.None } => "this.Handle", + { Transfer: GirModel.Transfer.Full } => "this.Handle.UnownedCopy()", + _ => throw new Exception("Unknown transfer type for opaque untyped instance parameter") + }; + } +} diff --git a/src/Generation/Generator/Renderer/Public/InstanceParameterToNativeExpression/InstanceParameterToNativeExpression.cs b/src/Generation/Generator/Renderer/Public/InstanceParameterToNativeExpression/InstanceParameterToNativeExpression.cs index 92bf98a63..e40c98d7b 100644 --- a/src/Generation/Generator/Renderer/Public/InstanceParameterToNativeExpression/InstanceParameterToNativeExpression.cs +++ b/src/Generation/Generator/Renderer/Public/InstanceParameterToNativeExpression/InstanceParameterToNativeExpression.cs @@ -11,6 +11,7 @@ internal static class InstanceParameterToNativeExpression new InstanceParameterToNativeExpressions.OpaqueTypedRecord(), new InstanceParameterToNativeExpressions.OpaqueUntypedRecord(), new InstanceParameterToNativeExpressions.Pointer(), + new InstanceParameterToNativeExpressions.TypedRecord() }; public static string Render(GirModel.InstanceParameter instanceParameter) diff --git a/src/Generation/Generator/Renderer/Public/Parameter/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Public/Parameter/Converter/TypedRecord.cs new file mode 100644 index 000000000..8993d6abf --- /dev/null +++ b/src/Generation/Generator/Renderer/Public/Parameter/Converter/TypedRecord.cs @@ -0,0 +1,31 @@ +using System; + +namespace Generator.Renderer.Public.Parameter; + +internal class TypedRecord : ParameterConverter +{ + public bool Supports(GirModel.AnyType anyType) + { + return anyType.Is(out var record) && Model.Record.IsTyped(record); + } + + public ParameterTypeData Create(GirModel.Parameter parameter) + { + return new ParameterTypeData( + Direction: GetDirection(parameter), + NullableTypeName: GetNullableTypeName(parameter) + ); + } + + private static string GetNullableTypeName(GirModel.Parameter parameter) + { + var type = (GirModel.Record) parameter.AnyTypeOrVarArgs.AsT0.AsT0; + return Model.TypedRecord.GetFullyQualifiedPublicClassName(type) + Nullable.Render(parameter); + } + + private static string GetDirection(GirModel.Parameter parameter) => parameter switch + { + { Direction: GirModel.Direction.In } => ParameterDirection.In(), + _ => throw new Exception("records with direction != in not yet supported") + }; +} diff --git a/src/Generation/Generator/Renderer/Public/Parameter/Converter/TypedRecordArray.cs b/src/Generation/Generator/Renderer/Public/Parameter/Converter/TypedRecordArray.cs new file mode 100644 index 000000000..fe5eb1130 --- /dev/null +++ b/src/Generation/Generator/Renderer/Public/Parameter/Converter/TypedRecordArray.cs @@ -0,0 +1,31 @@ +using System; + +namespace Generator.Renderer.Public.Parameter; + +internal class TypedRecordArray : ParameterConverter +{ + public bool Supports(GirModel.AnyType anyType) + { + return anyType.IsArray(out var record) && Model.Record.IsTyped(record); + } + + public ParameterTypeData Create(GirModel.Parameter parameter) + { + return new ParameterTypeData( + Direction: GetDirection(parameter), + NullableTypeName: GetNullableTypeName(parameter) + ); + } + + private static string GetNullableTypeName(GirModel.Parameter parameter) + { + var arrayType = parameter.AnyTypeOrVarArgs.AsT0.AsT1; + return $"{Model.TypedRecord.GetFullyQualifiedPublicClassName((GirModel.Record) arrayType.AnyType.AsT0)}[]{Nullable.Render(parameter)}"; + } + + private static string GetDirection(GirModel.Parameter parameter) => parameter switch + { + { Direction: GirModel.Direction.In } => ParameterDirection.In(), + _ => throw new Exception($"Unknown direction for typed record in parameter {parameter.Name}.") + }; +} diff --git a/src/Generation/Generator/Renderer/Public/Parameter/ParameterRenderer.cs b/src/Generation/Generator/Renderer/Public/Parameter/ParameterRenderer.cs index e1f863ff6..742e47424 100644 --- a/src/Generation/Generator/Renderer/Public/Parameter/ParameterRenderer.cs +++ b/src/Generation/Generator/Renderer/Public/Parameter/ParameterRenderer.cs @@ -28,6 +28,8 @@ internal static class ParameterRenderer new Parameter.RecordArray(), new Parameter.String(), new Parameter.StringArray(), + new Parameter.TypedRecord(), + new Parameter.TypedRecordArray(), new Parameter.Union(), new Parameter.Void(), }; diff --git a/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/TypedRecord.cs new file mode 100644 index 000000000..08e20fbce --- /dev/null +++ b/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/TypedRecord.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; + +namespace Generator.Renderer.Public.ParameterToNativeExpressions; + +internal class TypedRecord : ToNativeParameterConverter +{ + public bool Supports(GirModel.AnyType type) + => type.Is(out var record) && Model.Record.IsTyped(record); + + public void Initialize(ParameterToNativeData parameter, IEnumerable _) + { + if (parameter.Parameter.Direction != GirModel.Direction.In) + throw new NotImplementedException($"{parameter.Parameter.AnyTypeOrVarArgs}: record parameter '{parameter.Parameter.Name}' with direction != in not yet supported"); + + var record = (GirModel.Record) parameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT0; + var typeHandle = Model.TypedRecord.GetFullyQuallifiedHandle(record); + var nullHandle = Model.TypedRecord.GetFullyQuallifiedNullHandle(record); + var signatureName = Model.Parameter.GetName(parameter.Parameter); + + var callName = parameter.Parameter switch + { + { Nullable: true, Transfer: GirModel.Transfer.None } => $"({typeHandle}?) {signatureName}?.Handle ?? {nullHandle}", + { Nullable: false, Transfer: GirModel.Transfer.None } => $"{signatureName}.Handle", + { Nullable: true, Transfer: GirModel.Transfer.Full } => $"{signatureName}?.Handle.UnownedCopy() ?? {nullHandle}", + { Nullable: false, Transfer: GirModel.Transfer.Full } => $"{signatureName}.Handle.UnownedCopy()", + _ => throw new Exception($"Can't detect call name for parameter record parameter {parameter.Parameter.Name}") + }; + + parameter.SetSignatureName(signatureName); + parameter.SetCallName(callName); + } +} diff --git a/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/TypedRecordArray.cs b/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/TypedRecordArray.cs new file mode 100644 index 000000000..b4d6db693 --- /dev/null +++ b/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/TypedRecordArray.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Generator.Renderer.Public.ParameterToNativeExpressions; + +internal class TypedRecordArray : ToNativeParameterConverter +{ + public bool Supports(GirModel.AnyType type) + => type.IsArray(out var record) && Model.Record.IsTyped(record); + + public void Initialize(ParameterToNativeData parameterData, IEnumerable parameters) + { + switch (parameterData.Parameter) + { + case { Direction: GirModel.Direction.In } + when parameterData.Parameter.AnyTypeOrVarArgs.AsT0.AsT1.Length is not null: + Span(parameterData, parameters); + break; + case { Direction: GirModel.Direction.In }: + Ref(parameterData); + break; + default: + throw new Exception($"{parameterData.Parameter}: This kind of typed record array is not yet supported"); + } + } + + private static void Ref(ParameterToNativeData parameter) + { + var parameterName = Model.Parameter.GetName(parameter.Parameter); + parameter.SetSignatureName(parameterName); + parameter.SetCallName($"ref {parameterName}"); + + //TODO + throw new Exception("Test missing for typed record array passed in via a ref"); + } + + private static void Span(ParameterToNativeData parameter, IEnumerable allParameters) + { + if (parameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT1.IsPointer) + PointerArray(parameter, allParameters); + else + StructArray(parameter, allParameters); + } + + private static void PointerArray(ParameterToNativeData parameter, IEnumerable allParameters) + { + var parameterName = Model.Parameter.GetName(parameter.Parameter); + var nativeVariableName = parameterName + "Native"; + + parameter.SetSignatureName(parameterName); + parameter.SetCallName($"ref MemoryMarshal.GetReference({nativeVariableName})"); + + var nullable = parameter.Parameter.Nullable + ? $"{parameterName} is null ? null : " + : string.Empty; + + parameter.SetExpression($"var {nativeVariableName} = new Span({nullable}{parameterName}" + + $".Select(record => record.Handle.DangerousGetHandle()).ToArray());"); + + var lengthIndex = parameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT1.Length ?? throw new Exception("Length missing"); + var lengthParameter = allParameters.ElementAt(lengthIndex); + var lengthParameterType = Model.Type.GetName(lengthParameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT0); + + switch (lengthParameter.Parameter.Direction) + { + case GirModel.Direction.In: + lengthParameter.IsArrayLengthParameter = true; + lengthParameter.SetCallName(parameter.Parameter.Nullable + ? $"({lengthParameterType}) ({parameterName}?.Length ?? 0)" + : $"({lengthParameterType}) {parameterName}.Length" + ); + break; + default: + throw new Exception("Unknown direction for length parameter in typed record array"); + } + } + + private static void StructArray(ParameterToNativeData parameter, IEnumerable allParameters) + { + if (parameter.Parameter.Transfer == GirModel.Transfer.Container || parameter.Parameter.Transfer == GirModel.Transfer.Full) + throw new Exception("Can't transfer ownership to native code for typed record"); + + var record = (GirModel.Record) parameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT1.AnyType.AsT0; + var parameterName = Model.Parameter.GetName(parameter.Parameter); + var nativeVariableName = parameterName + "Native"; + + parameter.SetSignatureName(parameterName); + parameter.SetCallName(nativeVariableName); + + var nullable = parameter.Parameter.Nullable + ? $"{parameterName} is null ? ({Model.TypedRecord.GetFullyQuallifiedArrayHandle(record)}){Model.TypedRecord.GetFullyQuallifiedArrayNullHandle(record)} : " + : string.Empty; + + parameter.SetExpression($"var {nativeVariableName} = {nullable} {Model.TypedRecord.GetFullyQuallifiedArrayOwnedHandle(record)}.Create({parameterName});"); + + var lengthIndex = parameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT1.Length ?? throw new Exception("Length missing"); + var lengthParameter = allParameters.ElementAt(lengthIndex); + var lengthParameterType = Model.Type.GetName(lengthParameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT0); + + switch (lengthParameter.Parameter.Direction) + { + case GirModel.Direction.In: + lengthParameter.IsArrayLengthParameter = true; + lengthParameter.SetCallName(parameter.Parameter.Nullable + ? $"({lengthParameterType}) ({parameterName}?.Length ?? 0)" + : $"({lengthParameterType}) {parameterName}.Length" + ); + break; + default: + throw new Exception("Unknown direction for length parameter in typed record array"); + } + } +} diff --git a/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/ParameterToNativeExpression.cs b/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/ParameterToNativeExpression.cs index 1c2da09e0..19143d1d6 100644 --- a/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/ParameterToNativeExpression.cs +++ b/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/ParameterToNativeExpression.cs @@ -28,6 +28,8 @@ internal static class ParameterToNativeExpression new ParameterToNativeExpressions.PrimitiveValueTypeArray(), new ParameterToNativeExpressions.Record(), new ParameterToNativeExpressions.RecordArray(), + new ParameterToNativeExpressions.TypedRecord(), + new ParameterToNativeExpressions.TypedRecordArray(), new ParameterToNativeExpressions.Utf8String(), new ParameterToNativeExpressions.Utf8StringArray(), }; diff --git a/src/Generation/Generator/Renderer/Public/ReturnType/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Public/ReturnType/Converter/TypedRecord.cs new file mode 100644 index 000000000..be399d460 --- /dev/null +++ b/src/Generation/Generator/Renderer/Public/ReturnType/Converter/TypedRecord.cs @@ -0,0 +1,16 @@ +using Generator.Model; + +namespace Generator.Renderer.Public.ReturnType; + +internal class TypedRecord : ReturnTypeConverter +{ + public RenderableReturnType Create(GirModel.ReturnType returnType) + { + var typeName = ComplexType.GetFullyQualified((GirModel.Record) returnType.AnyType.AsT0); + + return new RenderableReturnType(typeName + Nullable.Render(returnType)); + } + + public bool Supports(GirModel.ReturnType returnType) + => returnType.AnyType.Is(out var record) && Model.Record.IsTyped(record); +} diff --git a/src/Generation/Generator/Renderer/Public/ReturnType/ReturnTypeRenderer.cs b/src/Generation/Generator/Renderer/Public/ReturnType/ReturnTypeRenderer.cs index 7ddfa13da..0df6586f9 100644 --- a/src/Generation/Generator/Renderer/Public/ReturnType/ReturnTypeRenderer.cs +++ b/src/Generation/Generator/Renderer/Public/ReturnType/ReturnTypeRenderer.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Reflection.Metadata.Ecma335; namespace Generator.Renderer.Public; @@ -20,6 +21,7 @@ internal static class ReturnTypeRenderer new ReturnType.RecordArray(), new ReturnType.String(), new ReturnType.StringArray(), + new ReturnType.TypedRecord(), new ReturnType.Void(), }; diff --git a/src/Generation/Generator/Renderer/Public/ReturnType/ReturnTypeRendererCallback.cs b/src/Generation/Generator/Renderer/Public/ReturnType/ReturnTypeRendererCallback.cs index 5742eba14..6eb7b65cd 100644 --- a/src/Generation/Generator/Renderer/Public/ReturnType/ReturnTypeRendererCallback.cs +++ b/src/Generation/Generator/Renderer/Public/ReturnType/ReturnTypeRendererCallback.cs @@ -18,8 +18,9 @@ internal static class ReturnTypeRendererCallback new ReturnType.PrimitiveValueTypeAlias(), new ReturnType.Record(), new ReturnType.RecordArray(), - new ReturnType.StringInCallback(), new ReturnType.StringArray(), + new ReturnType.StringInCallback(), + new ReturnType.TypedRecord(), new ReturnType.Void(), }; diff --git a/src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/Converter/TypedRecord.cs new file mode 100644 index 000000000..7b6c7f9a9 --- /dev/null +++ b/src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/Converter/TypedRecord.cs @@ -0,0 +1,28 @@ +using System; +using GirModel; + +namespace Generator.Renderer.Public.ReturnTypeToManagedExpressions; + +internal class TypedRecord : ReturnTypeConverter +{ + public bool Supports(AnyType type) + => type.Is(out var record) && Model.Record.IsTyped(record); + + public string GetString(GirModel.ReturnType returnType, string fromVariableName) + { + var record = (GirModel.Record) returnType.AnyType.AsT0; + + var handleExpression = returnType switch + { + { Transfer: Transfer.Full } => fromVariableName, + { Transfer: Transfer.None } => $"{fromVariableName}.OwnedCopy()", + _ => throw new NotImplementedException("Unknown transfer type") + }; + + var createNewInstance = $"new {Model.ComplexType.GetFullyQualified(record)}({handleExpression})"; + + return returnType.Nullable + ? $"{fromVariableName}.IsInvalid ? null : {createNewInstance};" + : createNewInstance; + } +} diff --git a/src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/ReturnTypeToManagedExpression.cs b/src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/ReturnTypeToManagedExpression.cs index 0546d7f24..a147ffbc4 100644 --- a/src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/ReturnTypeToManagedExpression.cs +++ b/src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/ReturnTypeToManagedExpression.cs @@ -20,6 +20,7 @@ internal static class ReturnTypeToManagedExpression new ReturnTypeToManagedExpressions.PrimitiveValueTypeAlias(), new ReturnTypeToManagedExpressions.PrimitiveValueTypeArray(), new ReturnTypeToManagedExpressions.Record(), + new ReturnTypeToManagedExpressions.TypedRecord(), new ReturnTypeToManagedExpressions.Utf8String(), new ReturnTypeToManagedExpressions.Utf8StringArray(), }; diff --git a/src/Generation/Generator/Renderer/Public/TypedRecord.cs b/src/Generation/Generator/Renderer/Public/TypedRecord.cs new file mode 100644 index 000000000..f8c3b81e2 --- /dev/null +++ b/src/Generation/Generator/Renderer/Public/TypedRecord.cs @@ -0,0 +1,62 @@ +using System; +using System.Linq; +using Generator.Model; + +namespace Generator.Renderer.Public; + +internal static class TypedRecord +{ + public static string Render(GirModel.Record record) + { + var name = Model.TypedRecord.GetPublicClassName(record); + var internalHandleName = Model.TypedRecord.GetFullyQuallifiedOwnedHandle(record); + + return $@" +using System; +using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +#nullable enable + +namespace {Namespace.GetPublicName(record.Namespace)}; + +// AUTOGENERATED FILE - DO NOT MODIFY + +{PlatformSupportAttribute.Render(record as GirModel.PlatformDependent)} +public partial class {name} +{{ + public {internalHandleName} Handle {{ get; }} + + public {name}({internalHandleName} handle) + {{ + Handle = handle; + Initialize(); + }} + + //TODO: This is a workaround constructor as long as we are + //not having https://github.com/gircore/gir.core/issues/397 + private {name}(IntPtr ptr, bool ownsHandle) : this(ownsHandle + ? new {Model.OpaqueTypedRecord.GetFullyQuallifiedOwnedHandle(record)}(ptr) + : new {Model.OpaqueTypedRecord.GetFullyQuallifiedUnownedHandle(record)}(ptr).OwnedCopy()){{ }} + + // Implement this to perform additional steps in the constructor + partial void Initialize(); + + {record.Constructors + .Select(ConstructorRenderer.Render) + .Join(Environment.NewLine)} + + {FunctionRenderer.Render(record.TypeFunction)} + + {record.Functions + .Select(FunctionRenderer.Render) + .Join(Environment.NewLine)} + + {record.Methods + .Where(Method.IsEnabled) + .Select(MethodRenderer.Render) + .Join(Environment.NewLine)} +}}"; + } +} diff --git a/src/Generation/GirLoader/Output/Class.cs b/src/Generation/GirLoader/Output/Class.cs index ab2925885..28079da3a 100644 --- a/src/Generation/GirLoader/Output/Class.cs +++ b/src/Generation/GirLoader/Output/Class.cs @@ -67,5 +67,5 @@ internal override bool Matches(TypeReference typeReference) } public override string ToString() - => Name; + => $"{Repository.Namespace.Name}.{Name}"; } diff --git a/src/Generation/GirLoader/Output/Interface.cs b/src/Generation/GirLoader/Output/Interface.cs index 039e50fe8..c1401a97f 100644 --- a/src/Generation/GirLoader/Output/Interface.cs +++ b/src/Generation/GirLoader/Output/Interface.cs @@ -44,4 +44,7 @@ internal override bool Matches(TypeReference typeReference) return false; } + + public override string ToString() + => $"{Repository.Namespace.Name}.{Name}"; } diff --git a/src/Generation/GirLoader/Output/Record.cs b/src/Generation/GirLoader/Output/Record.cs index 15de9c692..59c998449 100644 --- a/src/Generation/GirLoader/Output/Record.cs +++ b/src/Generation/GirLoader/Output/Record.cs @@ -49,4 +49,7 @@ internal override bool Matches(TypeReference typeReference) return ctypeMatches || (symbolNameMatches && (namespaceMatches || namespaceMissing)); } + + public override string ToString() + => $"{Repository.Namespace.Name}.{Name}"; } diff --git a/src/Libs/GLib-2.0/Public/Source.cs b/src/Libs/GLib-2.0/Public/Source.cs deleted file mode 100644 index d401bc8d6..000000000 --- a/src/Libs/GLib-2.0/Public/Source.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace GLib; - -public partial class Source -{ - public void SetCallback(SourceFunc sourceFunc) - { - var handler = new Internal.SourceFuncNotifiedHandler(sourceFunc); - Internal.Source.SetCallback(Handle, handler.NativeCallback, IntPtr.Zero, handler.DestroyNotify); - } - - public void Attach(MainContext mainContext) - { - Internal.Source.Attach(Handle, mainContext.Handle); - } - - public static void Remove(uint tag) => Internal.Functions.SourceRemove(tag); -} diff --git a/src/Libs/GObject-2.0/Public/Closure.cs b/src/Libs/GObject-2.0/Public/Closure.cs index afef79ece..6a34c89c7 100644 --- a/src/Libs/GObject-2.0/Public/Closure.cs +++ b/src/Libs/GObject-2.0/Public/Closure.cs @@ -15,15 +15,15 @@ public partial class Closure : IDisposable internal Closure(ClosureCallback callback) { _callback = callback; - _handle = Internal.Closure.NewSimple((uint) Marshal.SizeOf(), IntPtr.Zero); + Handle = Internal.Closure.NewSimple((uint) Marshal.SizeOf(), IntPtr.Zero); - Debug.WriteLine($"Instantiating Closure: Address {_handle.DangerousGetHandle()}."); + Debug.WriteLine($"Instantiating Closure: Address {Handle.DangerousGetHandle()}."); _closureMarshal = InternalCallback; //Save delegate to keep instance alive - Internal.Closure.Ref(_handle); - Internal.Closure.Sink(_handle); - Internal.Closure.SetMarshal(_handle, _closureMarshal); + Internal.Closure.Ref(Handle); + Internal.Closure.Sink(Handle); + Internal.Closure.SetMarshal(Handle, _closureMarshal); } private void InternalCallback(IntPtr closure, IntPtr returnValuePtr, uint nParamValues, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] Internal.ValueData[] paramValuesData, IntPtr invocationHint, IntPtr userData) @@ -34,7 +34,7 @@ private void InternalCallback(IntPtr closure, IntPtr returnValuePtr, uint nParam ); var returnValue = returnValuePtr != IntPtr.Zero - ? new Value(new Internal.ValueUnownedHandle(returnValuePtr)) + ? new Value(new Internal.ValueUnownedHandle(returnValuePtr).OwnedCopy()) : null; var paramValues = paramValuesData @@ -54,7 +54,7 @@ private void InternalCallback(IntPtr closure, IntPtr returnValuePtr, uint nParam public void Dispose() { - Debug.WriteLine($"Disposing Closure: Address {_handle.DangerousGetHandle()}."); - _handle.Dispose(); + Debug.WriteLine($"Disposing Closure: Address {Handle.DangerousGetHandle()}."); + Handle.Dispose(); } } diff --git a/src/Libs/GObject-2.0/Public/ConstructArgument.cs b/src/Libs/GObject-2.0/Public/ConstructArgument.cs index df74eb9d7..416c2ace6 100644 --- a/src/Libs/GObject-2.0/Public/ConstructArgument.cs +++ b/src/Libs/GObject-2.0/Public/ConstructArgument.cs @@ -17,32 +17,10 @@ public sealed class ConstructArgument : IDisposable /// public Value Value { get; } - private ConstructArgument(string name, object value) + public ConstructArgument(string name, Value value) { Name = name; - Value = Value.From(value); - } - - /// - /// Creates a new construct time parameter, using the given - /// with the given - /// - /// The property to define at the construct time. - /// The property value. - /// The type of the value to set in the property. - /// The type of the value to set in the property. - /// - /// A new instance of , which describe the - /// property-value pair to use at construct time. - /// - public static ConstructArgument With(PropertyDefinition property, T value) where T : notnull - { - return new ConstructArgument(property.UnmanagedName, value); - } - - public static ConstructArgument With(string propertyName, object value) - { - return new ConstructArgument(propertyName, value); + Value = value; } public void Dispose() diff --git a/src/Libs/GObject-2.0/Public/Object.cs b/src/Libs/GObject-2.0/Public/Object.cs index 4a0163e53..cd31430d7 100644 --- a/src/Libs/GObject-2.0/Public/Object.cs +++ b/src/Libs/GObject-2.0/Public/Object.cs @@ -1,7 +1,6 @@ using System; using System.ComponentModel; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using GLib; @@ -48,7 +47,7 @@ protected Object(bool owned, ConstructArgument[] constructArguments) objectType: gtype.Value, nProperties: (uint) constructArguments.Length, names: GetNames(constructArguments), - values: GetValues(constructArguments) + values: ValueArray2OwnedHandle.Create(constructArguments.Select(x => x.Value).ToArray()) ); // We can't check if a reference is floating via "g_object_is_floating" here @@ -65,18 +64,6 @@ protected Object(bool owned, ConstructArgument[] constructArguments) private string[] GetNames(ConstructArgument[] constructParameters) => constructParameters.Select(x => x.Name).ToArray(); - private Internal.ValueData[] GetValues(ConstructArgument[] constructParameters) - { - var values = new Internal.ValueData[constructParameters.Length]; - - for (int i = 0; i < constructParameters.Length; i++) - { - values[i] = constructParameters[i].Value.GetData(); - } - - return values; - } - /// /// Does common initialization tasks. /// Wrapper and subclasses can override here to perform immediate initialization. diff --git a/src/Libs/GObject-2.0/Public/Value.cs b/src/Libs/GObject-2.0/Public/Value.cs index d76aa6980..485f1d5a4 100644 --- a/src/Libs/GObject-2.0/Public/Value.cs +++ b/src/Libs/GObject-2.0/Public/Value.cs @@ -5,20 +5,15 @@ namespace GObject; -// TODO: Consider splitting value into different types for each type it represents -// to avoid breaking the open closed principle. -// There could an abstract value base class with generic implementations of the concrete types. public partial class Value : IDisposable { - #region Constructors - public Value(Type type) { _handle = ValueManagedHandle.Create(); // We ignore the return parameter as it is a pointer // to the same location like the instance parameter. - _ = Init(_handle, type.Value); + _ = Internal.Value.Init(_handle, type); } public Value(Object value) : this(Type.Object) => SetObject(value); @@ -32,11 +27,15 @@ public Value(Type type) public Value(string value) : this(Type.String) => SetString(value); public Value(string[] value) : this(Type.StringArray) => SetBoxed(Utf8StringArrayNullTerminatedOwnedHandle.Create(value).DangerousGetHandle()); - #endregion - - #region Methods + public Value(Enum value) : this(HasFlags(value) ? Type.Flags : Type.Enum) + { + if (HasFlags(value)) + SetFlags(value); + else + SetEnum(value); + } - internal ValueData GetData() => Marshal.PtrToStructure(Handle.DangerousGetHandle()); + private static bool HasFlags(Enum e) => e.GetType().IsDefined(typeof(FlagsAttribute), false); private nuint GetTypeValue() { @@ -44,81 +43,6 @@ private nuint GetTypeValue() return structure.GType; } - /// - /// Gets an instance of from the given . - /// - /// - /// An instance of if the cast is successful. - /// - /// - /// The given has a type which cannot be parsed as a . - /// - public static Value From(object value) => value switch - { - bool v1 => new Value(v1), - uint v2 => new Value(v2), - int v3 => new Value(v3), - long v4 => new Value(v4), - double v5 => new Value(v5), - float v6 => new Value(v6), - string v7 => new Value(v7), - Enum _ => new Value((long) value), - GLib.Variant v => new Value(v), - Object obj => new Value(obj), - _ => throw new NotSupportedException("Unable to create the value from the given type.") - }; - - public void Set(object? value) - { - switch (value) - { - case bool b: - SetBoolean(b); - break; - case uint u: - SetUint(u); - break; - case int i: - SetInt(i); - break; - case string s: - SetString(s); - break; - case double d: - SetDouble(d); - break; - case Enum e: - if (e.GetType().IsDefined(typeof(FlagsAttribute), false)) - SetFlags(e); - else - SetEnum(e); - break; - case long l: - SetLong(l); - break; - case float f: - SetFloat(f); - break; - case string[] array: - // Marshalling logic happens inside this safe handle. GValue takes a - // copy of the boxed memory so we do not need to keep it alive. The - // Garbage Collector will automatically free the safe handle for us. - var strArray = Utf8StringArrayNullTerminatedOwnedHandle.Create(array); - SetBoxed(strArray.DangerousGetHandle()); - break; - case GLib.Variant v: - SetVariant(v); - break; - case Object o: - SetObject(o); - break; - case null: - break; - default: - throw new NotSupportedException($"Type {value.GetType()} is not supported as a value type"); - } - } - /// /// Extracts the content of this into an object. /// @@ -131,14 +55,14 @@ public void Set(object? value) var type = GetTypeValue(); return type switch { - (nuint) BasicType.Boolean => GetBool(), + (nuint) BasicType.Boolean => GetBoolean(), (nuint) BasicType.UInt => GetUint(), (nuint) BasicType.Int => GetInt(), (nuint) BasicType.Long => GetLong(), (nuint) BasicType.Double => GetDouble(), (nuint) BasicType.Float => GetFloat(), (nuint) BasicType.String => GetString(), - (nuint) BasicType.Pointer => GetPtr(), + (nuint) BasicType.Pointer => GetPointer(), _ => CheckComplexTypes(type) }; } @@ -170,17 +94,9 @@ public void Set(object? value) public T Extract() => (T) Extract()!; - public IntPtr GetPtr() => Internal.Value.GetPointer(Handle); - - public ParamSpec GetParam() - { - var paramHandle = Internal.Value.GetParam(Handle); - return new ParamSpec(paramHandle); - } - public object? GetBoxed(nuint type) { - IntPtr ptr = Internal.Value.GetBoxed(Handle); + var ptr = Internal.Value.GetBoxed(Handle); if (ptr == IntPtr.Zero) return null; @@ -202,62 +118,72 @@ public ParamSpec GetParam() ); } - public Object? GetObject() - => ObjectWrapper.WrapNullableHandle(Internal.Value.GetObject(Handle), false); + public T GetFlags() where T : Enum + { + return (T) (object) Internal.Value.GetFlags(Handle); + } - public bool GetBool() => Internal.Value.GetBoolean(Handle); - public uint GetUint() => Internal.Value.GetUint(Handle); - public int GetInt() => Internal.Value.GetInt(Handle); - public long GetLong() => Internal.Value.GetLong(Handle); - public double GetDouble() => Internal.Value.GetDouble(Handle); - public float GetFloat() => Internal.Value.GetFloat(Handle); - public ulong GetFlags() => Internal.Value.GetFlags(Handle); - public long GetEnum() => Internal.Value.GetEnum(Handle); - public string? GetString() => GetString(Handle).ConvertToString(); - public GLib.Variant? GetVariant() + public T GetEnum() where T : Enum { - var result = Internal.Value.GetVariant(Handle); - return result.IsInvalid ? null : new(result.OwnedCopy()); + return (T) (object) Internal.Value.GetEnum(Handle); } - private void SetBoxed(IntPtr ptr) => Internal.Value.SetBoxed(Handle, ptr); - private void SetBoolean(bool b) => Internal.Value.SetBoolean(Handle, b); - private void SetUint(uint u) => Internal.Value.SetUint(Handle, u); - private void SetInt(int i) => Internal.Value.SetInt(Handle, i); - private void SetDouble(double d) => Internal.Value.SetDouble(Handle, d); - private void SetFloat(float f) => Internal.Value.SetFloat(Handle, f); - private void SetLong(long l) => Internal.Value.SetLong(Handle, l); - private void SetEnum(Enum e) => Internal.Value.SetEnum(Handle, Convert.ToInt32(e)); - private void SetFlags(Enum e) => Internal.Value.SetFlags(Handle, Convert.ToUInt32(e)); - private void SetString(string s) => Internal.Value.SetString(Handle, GLib.Internal.NullableUtf8StringOwnedHandle.Create(s)); - private void SetVariant(GLib.Variant v) => Internal.Value.SetVariant(Handle, v.Handle); - private void SetObject(Object o) => Internal.Value.SetObject(Handle, o.Handle); + public void SetEnum(Enum e) => Internal.Value.SetEnum(Handle, Convert.ToInt32(e)); + public void SetFlags(Enum e) => Internal.Value.SetFlags(Handle, Convert.ToUInt32(e)); + public void Set(object? value) + { + switch (value) + { + case bool b: + SetBoolean(b); + break; + case uint u: + SetUint(u); + break; + case int i: + SetInt(i); + break; + case string s: + SetString(s); + break; + case double d: + SetDouble(d); + break; + case Enum e: + if (HasFlags(e)) + SetFlags(e); + else + SetEnum(e); + break; + case long l: + SetLong(l); + break; + case float f: + SetFloat(f); + break; + case string[] array: + // Marshalling logic happens inside this safe handle. GValue takes a + // copy of the boxed memory so we do not need to keep it alive. The + // Garbage Collector will automatically free the safe handle for us. + var strArray = Utf8StringArrayNullTerminatedOwnedHandle.Create(array); + SetBoxed(strArray.DangerousGetHandle()); + break; + case GLib.Variant v: + SetVariant(v); + break; + case Object o: + SetObject(o); + break; + case null: + break; + default: + throw new NotSupportedException($"Type {value.GetType()} is not supported as a value type"); + } + } + public void Dispose() { Handle.Dispose(); } - - #endregion - - #region Internal - - // This redeclares the "g_value_init" method. The internal method - // returns a GObject.Internal.Value.Handle which can not be freed - // via a free function. The Marshaller would create an instance - // of GObject.Internal.Value.Handle and the GC would try to - // dispose it which is not possible and throws an Exception. To - // avoid the GObject.Internal.Value.Handle creation this method - // returns just an IntPtr. It is okay to return an IntPtr as the - // returned IntPtr points to the location of the "value" parameter. - [DllImport(ImportResolver.Library, EntryPoint = "g_value_init")] - private static extern IntPtr Init(GObject.Internal.ValueHandle value, nuint gType); - - //TODO: g_value_get_string get's redeclared here as it is not annotated correctly. - //Remove after release of: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3301 - //Use "Internal.Value.GetString(Handle).ConvertToString();" again - [DllImport(ImportResolver.Library, EntryPoint = "g_value_get_string")] - public static extern GLib.Internal.NullableUtf8StringUnownedHandle GetString(GObject.Internal.ValueHandle value); - - #endregion } diff --git a/src/Native/GirTestLib/girtest-typed-record-tester.c b/src/Native/GirTestLib/girtest-typed-record-tester.c new file mode 100644 index 000000000..2a1063cd6 --- /dev/null +++ b/src/Native/GirTestLib/girtest-typed-record-tester.c @@ -0,0 +1,329 @@ +#include "girtest-typed-record-tester.h" + +G_DEFINE_BOXED_TYPE (GirTestTypedRecordTester, girtest_typed_record_tester, girtest_typed_record_tester_ref, girtest_typed_record_tester_unref) + +/** + * girtest_typed_record_tester_new: (constructor) + * + * Returns: (transfer full): a new `GirTestTypedRecordTester` + **/ +GirTestTypedRecordTester * +girtest_typed_record_tester_new () +{ + GirTestTypedRecordTester *result; + result = g_new0 (GirTestTypedRecordTester, 1); + result->ref_count = 1; + return result; +} + +/** + * girtest_typed_record_tester_try_new: + * @returnNull: TRUE to return null, FALSE to create a new instance. + * + * Returns: (transfer full) (nullable): a new `GirTestTypedRecordTester` or NULL + **/ +GirTestTypedRecordTester * +girtest_typed_record_tester_try_new (gboolean returnNull) +{ + if(returnNull) + return NULL; + + return girtest_typed_record_tester_new(); +} + +/** + * girtest_typed_record_tester_ref: + * @self: a `GirTestRecordTester` + * + * Increments the reference count on `data`. + * + * Returns: (transfer full): the data. + **/ +GirTestTypedRecordTester * +girtest_typed_record_tester_ref (GirTestTypedRecordTester *self) +{ + g_return_val_if_fail (self != NULL, NULL); + self->ref_count += 1; + return self; +} + +/** + * girtest_typed_record_tester_try_ref: + * @self: a `GirTestRecordTester` + * @returnNull: TRUE to return NULL, otherwise FALSE + * + * Increments the reference count on `data`. + * + * Returns: (transfer full) (nullable): the data or NULL + **/ +GirTestTypedRecordTester * +girtest_typed_record_tester_try_ref (GirTestTypedRecordTester *self, gboolean returnNull) +{ + if(returnNull) + return NULL; + + return girtest_typed_record_tester_ref(self); +} + +/** + * girtest_typed_record_tester_mirror: + * @data: a `GirTestRecordTester` + * + * Mirrors the given data as the return value. Ownership is not transferred. + * + * Returns: (transfer none): the mirrored data. + **/ +GirTestTypedRecordTester * +girtest_typed_record_tester_mirror(GirTestTypedRecordTester *data) +{ + return data; +} + +/** + * girtest_typed_record_tester_nullable_mirror: + * @data: a `GirTestRecordTester` + * @mirror: true to mirror data, false to return NULL + * + * Mirrors the given data as the return value if @mirror is true. Ownership is not transferred. + * + * Returns: (transfer none) (nullable): the mirrored data or NULL. + **/ +GirTestTypedRecordTester * +girtest_typed_record_tester_nullable_mirror(GirTestTypedRecordTester *data, gboolean mirror) +{ + if(!mirror) + return NULL; + + return data; +} + +/** + * girtrest_typed_record_tester_unref: + * @self: (transfer full): a `GirTestTypedRecordTester` + * + * Decrements the reference count on `data` and frees the + * data if the reference count is 0. + **/ +void +girtest_typed_record_tester_unref (GirTestTypedRecordTester *self) +{ + g_return_if_fail (self != NULL); + + self->ref_count -= 1; + if (self->ref_count > 0) + return; + + g_free (self); +} + +/** + * girtest_typed_record_tester_get_ref_count: + * @self: a `GirTestTypedRecordTester` + * + * Returns: The current ref count of the record. + **/ +int +girtest_typed_record_tester_get_ref_count(GirTestTypedRecordTester *self) +{ + g_return_val_if_fail (self != NULL, -1); + return self->ref_count; +} + +/** + * girtest_typed_record_tester_try_get_ref_count: + * @dummy: not used + * @self: (nullable): a `GirTestTypedRecordTester` + * + * Returns: The current ref count of the record or -1 if @self is NULL + **/ +int girtest_typed_record_tester_try_get_ref_count(int dummy, GirTestTypedRecordTester *self) +{ + if(self == NULL) + return -1; + + return self->ref_count; +} + +/** + * girtest_typed_record_tester_take_and_unref: + * @self: (transfer full): a `GirTestTypedRecordTester` + * + * Takes ownership and decrements the reference count on `data` and frees the + * data if the reference count is 0. + **/ +void +girtest_typed_record_tester_take_and_unref(GirTestTypedRecordTester *self) +{ + girtest_typed_record_tester_unref(self); +} + +/** + * girtest_typed_record_tester_take_and_unref_func: + * @dummy: Just an unused dummy value + * @data: (transfer full): a `GirTestTypedRecordTester` + * + * Takes ownership and decrements the reference count on `data` and frees the + * data if the reference count is 0. + **/ +void +girtest_typed_record_tester_take_and_unref_func(int dummy, GirTestTypedRecordTester *data) +{ + girtest_typed_record_tester_take_and_unref(data); +} + +/** + * girtest_typed_record_tester_take_and_unref_func_nullable: + * @dummy: Just an unused dummy value + * @data: (transfer full) (nullable): a `GirTestTypedRecordTester` + * + * Takes ownership and decrements the reference count on `data` and frees the + * data if the reference count is 0. + **/ +void +girtest_typed_record_tester_take_and_unref_func_nullable(int dummy, GirTestTypedRecordTester *data) +{ + if(data == NULL) + return; + + girtest_typed_record_tester_take_and_unref(data); +} + +/** + * girtest_typed_record_tester_get_ref_count_sum: + * @data: (array length=size): an array of `GirTestTypedRecordTester` pointers + * @size: The length of @data + * + * Returns: The count of all refs of the @data. + **/ +int girtest_typed_record_tester_get_ref_count_sum(GirTestTypedRecordTester * const *data, gsize size) +{ + int sum = 0; + + for (int i = 0; i < size; i++) + { + sum = sum + girtest_typed_record_tester_get_ref_count(data[i]); + } + + return sum; +} + +/** + * girtest_typed_record_tester_get_ref_count_sum_nullable: + * @data: (nullable) (array length=size): an array of `GirTestTypedRecordTester` pointers + * @size: The length of @data + * + * Returns: The count of all refs of the @data. -1 if NULL is supplied as @data. + **/ +int girtest_typed_record_tester_get_ref_count_sum_nullable(GirTestTypedRecordTester * const *data, gsize size) +{ + if(data == NULL) + return -1; + + return girtest_typed_record_tester_get_ref_count_sum(data, size); +} + +/** + * girtest_typed_record_tester_run_callback_return_no_ownership_transfer: + * @callback: (scope call): a callback + * + * Calls the callback and returns the newly created instance. + * + * Returns: (transfer none): a GirTestTypedRecordTester + **/ +GirTestTypedRecordTester * +girtest_typed_record_tester_run_callback_return_no_ownership_transfer(GirTestCreateTypedRecordTesterNoOwnershipTransfer callback) +{ + return callback(); +} + +/** + * girtest_typed_record_tester_run_callback_return_no_ownership_transfer_nullable: + * @callback: (scope call): a callback + * + * Calls the callback and returns the newly created instance or NULL + * + * Returns: (transfer none) (nullable): a GirTestTypedRecordTester + **/ +GirTestTypedRecordTester * girtest_typed_record_tester_run_callback_return_no_ownership_transfer_nullable(GirTestCreateTypedRecordTesterNoOwnershipTransferNullable callback) +{ + return callback(); +} + +/** + * girtest_typed_record_tester_run_callback_return_full_ownership_transfer: + * @callback: (scope call): a callback + * + * Calls the callback and returns the newly created instance. + * + * Returns: (transfer full): a GirTestTypedRecordTester + **/ +GirTestTypedRecordTester * girtest_typed_record_tester_run_callback_return_full_ownership_transfer(GirTestCreateTypedRecordTesterFullOwnershipTransfer callback) +{ + return callback(); +} + +/** + * girtest_typed_record_tester_run_callback_return_full_ownership_transfer_nullable: + * @callback: (scope call): a callback + * + * Calls the callback and returns the newly created instance. + * + * Returns: (transfer full) (nullable): a GirTestTypedRecordTester or NULL + **/ +GirTestTypedRecordTester * girtest_typed_record_tester_run_callback_return_full_ownership_transfer_nullable(GirTestCreateTypedRecordTesterFullOwnershipTransferNullable callback) +{ + return callback(); +} + +/** + * girtest_typed_record_tester_run_callback_parameter_full_ownership_transfer: + * @callback: (scope call): a callback + * + * Calls the callback and supplies a new TypedRecordTester. + **/ +void +girtest_typed_record_tester_run_callback_parameter_full_ownership_transfer(GirTestGetTypedRecordTesterFullOwnershipTransfer callback) +{ + callback(girtest_typed_record_tester_new()); +} + +/** + * girtest_typed_record_tester_run_callback_parameter_full_ownership_transfer_nullable: + * @useNull: TRUE to pass null to the callback, otherwise FALSE. + * @callback: (scope call): a callback + * + * Calls the callback and supplies a new TypedRecordTester if @useNull is FALSE. + **/ +void girtest_typed_record_tester_run_callback_parameter_full_ownership_transfer_nullable(gboolean useNull, GirTestGetTypedRecordTesterFullOwnershipTransferNullable callback) +{ + if(useNull) + callback(NULL); + else + callback(girtest_typed_record_tester_new()); +} + +/** + * girtest_typed_record_tester_run_callback_parameter_no_ownership_transfer: + * @callback: (scope call): a callback + * @data: (transfer none): A GirTestTypedRecordTester + * + * Calls the callback and supplies the given TypedRecordTester. + **/ +void +girtest_typed_record_tester_run_callback_parameter_no_ownership_transfer(GirTestGetTypedRecordTesterNoOwnershipTransfer callback, GirTestTypedRecordTester *data) +{ + callback(data); +} + +/** + * girtest_typed_record_tester_run_callback_parameter_no_ownership_transfer_nullable: + * @callback: (scope call): a callback + * @data: (transfer none) (nullable): A GirTestTypedRecordTester + * + * Calls the callback and supplies the given TypedRecordTester. + **/ +void +girtest_typed_record_tester_run_callback_parameter_no_ownership_transfer_nullable(GirTestGetTypedRecordTesterNoOwnershipTransferNullable callback, GirTestTypedRecordTester *data) +{ + callback(data); +} \ No newline at end of file diff --git a/src/Native/GirTestLib/girtest-typed-record-tester.h b/src/Native/GirTestLib/girtest-typed-record-tester.h new file mode 100644 index 000000000..4ee18e624 --- /dev/null +++ b/src/Native/GirTestLib/girtest-typed-record-tester.h @@ -0,0 +1,96 @@ +#pragma once + +#include + +G_BEGIN_DECLS + +/** + * GirTestTypedRecordTester: + * + * Just a record. + */ +struct _GirTestTypedRecordTester +{ + int ref_count; +}; + +typedef struct _GirTestTypedRecordTester GirTestTypedRecordTester; +#define GIRTEST_TYPE_TYPED_RECORD_TESTER (girtest_typed_record_tester_get_type()) + +GType girtest_typed_record_tester_get_type (void) G_GNUC_CONST; + +/** + * GirTestCreateTypedRecordTesterNoOwnershipTransfer: + * + * Returns: (transfer none): a new OpaqueRecordTester. + */ +typedef GirTestTypedRecordTester* (*GirTestCreateTypedRecordTesterNoOwnershipTransfer) (); + +/** + * GirTestCreateTypedRecordTesterNoOwnershipTransferNullable: + * + * Returns: (transfer none) (nullable): a new OpaqueRecordTester or NULL. + */ +typedef GirTestTypedRecordTester* (*GirTestCreateTypedRecordTesterNoOwnershipTransferNullable) (); + +/** + * GirTestCreateTypedRecordTesterFullOwnershipTransfer: + * + * Returns: (transfer full): a new TypedRecordTester. + */ +typedef GirTestTypedRecordTester* (*GirTestCreateTypedRecordTesterFullOwnershipTransfer) (); + +/** + * GirTestCreateTypedRecordTesterFullOwnershipTransferNullable: + * + * Returns: (transfer full) (nullable): a new TypedRecordTester or NULL. + */ +typedef GirTestTypedRecordTester* (*GirTestCreateTypedRecordTesterFullOwnershipTransferNullable) (); + +/** + * GirTestGetTypedRecordTesterFullOwnershipTransfer: + * @data: (transfer full): An TypedRecordTester + */ +typedef void (*GirTestGetTypedRecordTesterFullOwnershipTransfer) (GirTestTypedRecordTester *data); + +/** + * GirTestGetTypedRecordTesterFullOwnershipTransferNullable: + * @data: (transfer full) (nullable): An TypedRecordTester + */ +typedef void (*GirTestGetTypedRecordTesterFullOwnershipTransferNullable) (GirTestTypedRecordTester *data); + +/** + * GirTestGetTypedRecordTesterNoOwnershipTransfer: + * @data: (transfer none): An TypedRecordTester + */ +typedef void (*GirTestGetTypedRecordTesterNoOwnershipTransfer) (GirTestTypedRecordTester *data); + +/** + * GirTestGetTypedRecordTesterNoOwnershipTransferNullable: + * @data: (transfer none) (nullable): An TypedRecordTester + */ +typedef void (*GirTestGetTypedRecordTesterNoOwnershipTransferNullable) (GirTestTypedRecordTester *data); + +GirTestTypedRecordTester * girtest_typed_record_tester_new (); +GirTestTypedRecordTester * girtest_typed_record_tester_try_new (gboolean returnNull); +GirTestTypedRecordTester * girtest_typed_record_tester_ref (GirTestTypedRecordTester *self); +GirTestTypedRecordTester * girtest_typed_record_tester_try_ref (GirTestTypedRecordTester *self, gboolean returnNull); +GirTestTypedRecordTester * girtest_typed_record_tester_mirror(GirTestTypedRecordTester *data); +GirTestTypedRecordTester * girtest_typed_record_tester_nullable_mirror(GirTestTypedRecordTester *data, gboolean mirror); +void girtest_typed_record_tester_unref(GirTestTypedRecordTester *self); +int girtest_typed_record_tester_get_ref_count(GirTestTypedRecordTester *self); +int girtest_typed_record_tester_try_get_ref_count(int dummy, GirTestTypedRecordTester *self); +void girtest_typed_record_tester_take_and_unref(GirTestTypedRecordTester *self); +void girtest_typed_record_tester_take_and_unref_func(int dummy, GirTestTypedRecordTester *data); +void girtest_typed_record_tester_take_and_unref_func_nullable(int dummy, GirTestTypedRecordTester *data); +int girtest_typed_record_tester_get_ref_count_sum(GirTestTypedRecordTester * const *data, gsize size); +int girtest_typed_record_tester_get_ref_count_sum_nullable(GirTestTypedRecordTester * const *data, gsize size); +GirTestTypedRecordTester * girtest_typed_record_tester_run_callback_return_no_ownership_transfer(GirTestCreateTypedRecordTesterNoOwnershipTransfer callback); +GirTestTypedRecordTester * girtest_typed_record_tester_run_callback_return_no_ownership_transfer_nullable(GirTestCreateTypedRecordTesterNoOwnershipTransferNullable callback); +GirTestTypedRecordTester * girtest_typed_record_tester_run_callback_return_full_ownership_transfer(GirTestCreateTypedRecordTesterFullOwnershipTransfer callback); +GirTestTypedRecordTester * girtest_typed_record_tester_run_callback_return_full_ownership_transfer_nullable(GirTestCreateTypedRecordTesterFullOwnershipTransferNullable callback); +void girtest_typed_record_tester_run_callback_parameter_full_ownership_transfer(GirTestGetTypedRecordTesterFullOwnershipTransfer callback); +void girtest_typed_record_tester_run_callback_parameter_full_ownership_transfer_nullable(gboolean useNull, GirTestGetTypedRecordTesterFullOwnershipTransferNullable callback); +void girtest_typed_record_tester_run_callback_parameter_no_ownership_transfer(GirTestGetTypedRecordTesterNoOwnershipTransfer callback, GirTestTypedRecordTester *data); +void girtest_typed_record_tester_run_callback_parameter_no_ownership_transfer_nullable(GirTestGetTypedRecordTesterNoOwnershipTransferNullable callback, GirTestTypedRecordTester *data); +G_END_DECLS diff --git a/src/Native/GirTestLib/girtest.h b/src/Native/GirTestLib/girtest.h index 546b2b76b..8822d8ab9 100644 --- a/src/Native/GirTestLib/girtest.h +++ b/src/Native/GirTestLib/girtest.h @@ -20,6 +20,7 @@ #include "girtest-returning-signal-tester.h" #include "girtest-signal-tester.h" #include "girtest-string-tester.h" +#include "girtest-typed-record-tester.h" #include "girtest-utf8-string-array-null-terminated-tester.h" #include "data/girtest-executor.h" #include "data/girtest-executor-impl.h" diff --git a/src/Native/GirTestLib/meson.build b/src/Native/GirTestLib/meson.build index f76267d15..454a7e1c0 100644 --- a/src/Native/GirTestLib/meson.build +++ b/src/Native/GirTestLib/meson.build @@ -23,6 +23,7 @@ header_files = [ 'girtest-returning-signal-tester.h', 'girtest-signal-tester.h', 'girtest-string-tester.h', + 'girtest-typed-record-tester.h', 'girtest-utf8-string-array-null-terminated-tester.h', 'data/girtest-executor.h', 'data/girtest-executor-impl.h', @@ -47,6 +48,7 @@ source_files = [ 'girtest-returning-signal-tester.c', 'girtest-signal-tester.c', 'girtest-string-tester.c', + 'girtest-typed-record-tester.c', 'girtest-utf8-string-array-null-terminated-tester.c', 'data/girtest-executor.c', 'data/girtest-executor-impl.c', diff --git a/src/Tests/Libs/GObject-2.0.Tests/Records/ValueTest.cs b/src/Tests/Libs/GObject-2.0.Tests/Records/ValueTest.cs index bec13e5f1..d7cb3748a 100644 --- a/src/Tests/Libs/GObject-2.0.Tests/Records/ValueTest.cs +++ b/src/Tests/Libs/GObject-2.0.Tests/Records/ValueTest.cs @@ -1,4 +1,5 @@ -using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -28,6 +29,18 @@ public void VariantFromDataShouldContainGivenData() var v = Value.From(variant); v.Extract().GetString(out _).Should().Be(text); } + + [TestMethod] + public void ValueContainsEnum() + { + throw new Exception("TODO ENUM TESTEN IN GIRTEST"); + } + + [TestMethod] + public void ValueContainsFlags() + { + throw new Exception("TODO ENUM WITH FLAGS TESTEN IN GIRTEST"); + } [DataTestMethod] [DataRow("Hello", Internal.BasicType.String)] diff --git a/src/Tests/Libs/GirTest-0.1.Tests/BitfieldTest.cs b/src/Tests/Libs/GirTest-0.1.Tests/BitfieldTest.cs index a0cef7196..b3999a833 100644 --- a/src/Tests/Libs/GirTest-0.1.Tests/BitfieldTest.cs +++ b/src/Tests/Libs/GirTest-0.1.Tests/BitfieldTest.cs @@ -21,8 +21,14 @@ public void CanBeUsedInGValue() { var flags = BitfieldTesterSimpleFlags.One | BitfieldTesterSimpleFlags.Two; var value = new Value(Type.Flags); - value.Set(flags); - var result = value.Extract(); - result.Should().Be(flags); + value.SetFlags(flags); + + var result1 = value.Extract(); + result1.Should().Be(flags); + + var result2 = value.GetFlags(); + result2.Should().Be(flags); + + value.GetFlags().Should().Be((uint) flags); } } diff --git a/src/Tests/Libs/GirTest-0.1.Tests/EnumerationTest.cs b/src/Tests/Libs/GirTest-0.1.Tests/EnumerationTest.cs index a85829225..382fa6776 100644 --- a/src/Tests/Libs/GirTest-0.1.Tests/EnumerationTest.cs +++ b/src/Tests/Libs/GirTest-0.1.Tests/EnumerationTest.cs @@ -12,8 +12,14 @@ public void CanBeUsedInGValue() { var e = EnumTesterSimpleEnum.A; var value = new Value(Type.Enum); - value.Set(e); - var result = value.Extract(); - result.Should().Be(e); + value.SetEnum(e); + + var result1 = value.Extract(); + result1.Should().Be(e); + + var result2 = value.GetEnum(); + result2.Should().Be(e); + + value.GetEnum().Should().Be((int) e); } }