Skip to content

Commit

Permalink
Support foreigen untyped records
Browse files Browse the repository at this point in the history
  • Loading branch information
badcel committed Mar 13, 2024
1 parent d4e3a6c commit e225931
Show file tree
Hide file tree
Showing 19 changed files with 250 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Generator.Model;

namespace Generator.Generator.Internal;

internal class ForeignUntypedRecordHandle : Generator<GirModel.Record>
{
private readonly Publisher _publisher;

public ForeignUntypedRecordHandle(Publisher publisher)
{
_publisher = publisher;
}

public void Generate(GirModel.Record obj)
{
if (!Record.IsForeignUntyped(obj))
return;

var source = Renderer.Internal.ForeignUntypedRecordHandle.Render(obj);
var codeUnit = new CodeUnit(
Project: Namespace.GetCanonicalName(obj.Namespace),
Name: Model.ForeignTypedRecord.GetInternalHandle(obj),
Source: source,
IsInternal: true
);

_publisher.Publish(codeUnit);
}
}
29 changes: 29 additions & 0 deletions src/Generation/Generator/Generator/Public/ForeignUntypedRecord.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Generator.Model;

namespace Generator.Generator.Public;

internal class ForeignUntypedRecord : Generator<GirModel.Record>
{
private readonly Publisher _publisher;

public ForeignUntypedRecord(Publisher publisher)
{
_publisher = publisher;
}

public void Generate(GirModel.Record record)
{
if (!Record.IsForeignUntyped(record))
return;

var source = Renderer.Public.ForeignUntypedRecord.Render(record);
var codeUnit = new CodeUnit(
Project: Namespace.GetCanonicalName(record.Namespace),
Name: Record.GetPublicClassName(record),
Source: source,
IsInternal: false
);

_publisher.Publish(codeUnit);
}
}
34 changes: 34 additions & 0 deletions src/Generation/Generator/Model/ForeignUntypedRecord.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
namespace Generator.Model;

internal static class ForeignUntypedRecord
{
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 GetFullyQuallifiedInternalHandle(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";
}
7 changes: 6 additions & 1 deletion src/Generation/Generator/Model/Record.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ internal static partial class Record
{
public static bool IsStandard(GirModel.Record record)
{
return !IsForeignTyped(record) && !IsOpaqueTyped(record) && !IsOpaqueUntyped(record) && !IsTyped(record) && !IsUntyped(record);
return !IsForeignTyped(record) && !IsForeignUntyped(record) && !IsOpaqueTyped(record) && !IsOpaqueUntyped(record) && !IsTyped(record) && !IsUntyped(record);
}

public static bool IsForeignTyped(GirModel.Record record)
{
return record is { Foreign: true, TypeFunction.CIdentifier: not null };
}

public static bool IsForeignUntyped(GirModel.Record record)
{
return record is { Foreign: true, TypeFunction: null or { CIdentifier: "intern" } };
}

public static bool IsOpaqueTyped(GirModel.Record record)
{
//Even if there is a TypeFunction it does not mean that it actually is
Expand Down
14 changes: 9 additions & 5 deletions src/Generation/Generator/Records.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,33 @@ public static void Generate(IEnumerable<GirModel.Record> records, string path)
new Generator.Internal.UntypedRecordData(publisher),
new Generator.Internal.UntypedRecordHandle(publisher),
new Generator.Public.UntypedRecord(publisher),


//Foreign untyped records
new Generator.Internal.ForeignUntypedRecordHandle(publisher),
new Generator.Public.ForeignUntypedRecord(publisher),

//Foreign typed records
new Generator.Internal.ForeignTypedRecord(publisher),
new Generator.Internal.ForeignTypedRecordHandle(publisher),
new Generator.Public.ForeignTypedRecord(publisher),

//Opaque typed records
new Generator.Internal.OpaqueTypedRecord(publisher),
new Generator.Internal.OpaqueTypedRecordHandle(publisher),
new Generator.Public.OpaqueTypedRecord(publisher),

//Opaque untyped records
new Generator.Internal.OpaqueUntypedRecord(publisher),
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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public class {unownedHandleTypeName} : {typeName}
internal {unownedHandleTypeName}() : base(false) {{ }}
/// <summary>
/// Creates a new instance of {ownedHandleTypeName}. Assumes that the given pointer is unowned by the runtime.
/// Creates a new instance of {unownedHandleTypeName}. Assumes that the given pointer is unowned by the runtime.
/// </summary>
public {unownedHandleTypeName}(IntPtr ptr) : base(false)
{{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using Generator.Model;

namespace Generator.Renderer.Internal;

internal static class ForeignUntypedRecordHandle
{
public static string Render(GirModel.Record record)
{
var typeName = Model.ForeignUntypedRecord.GetInternalHandle(record);
var unownedHandleTypeName = Model.ForeignUntypedRecord.GetInternalUnownedHandle(record);
var ownedHandleTypeName = Model.ForeignUntypedRecord.GetInternalOwnedHandle(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 abstract class {typeName} : SafeHandle
{{
public sealed override bool IsInvalid => handle == IntPtr.Zero;
protected {typeName}(bool ownsHandle) : base(IntPtr.Zero, ownsHandle) {{ }}
}}
public class {unownedHandleTypeName} : {typeName}
{{
private static {unownedHandleTypeName}? nullHandle;
public static {unownedHandleTypeName} NullHandle => nullHandle ??= new {unownedHandleTypeName}();
/// <summary>
/// Creates a new instance of {unownedHandleTypeName}. Used automatically by PInvoke.
/// </summary>
internal {unownedHandleTypeName}() : base(false) {{ }}
/// <summary>
/// Creates a new instance of {unownedHandleTypeName}. Assumes that the given pointer is unowned by the runtime.
/// </summary>
public {unownedHandleTypeName}(IntPtr ptr) : base(false)
{{
SetHandle(ptr);
}}
protected override bool ReleaseHandle()
{{
throw new System.Exception(""UnownedHandle must not be freed"");
}}
}}
public partial class {ownedHandleTypeName} : {typeName}
{{
/// <summary>
/// Creates a new instance of {ownedHandleTypeName}. Used automatically by PInvoke.
/// </summary>
internal {ownedHandleTypeName}() : base(true) {{ }}
/// <summary>
/// Creates a new instance of {ownedHandleTypeName}. Assumes that the given pointer is owned by the runtime.
/// </summary>
public {ownedHandleTypeName}(IntPtr ptr) : base(true)
{{
SetHandle(ptr);
}}
protected override partial bool ReleaseHandle();
}}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public string GetString(GirModel.ReturnType returnType, string fromVariableName)
{ 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 opaque record return type which should be converted to native.")
_ => throw new Exception($"Unknown transfer type for foreigen typed record return type which should be converted to native.")
};
}
}
37 changes: 37 additions & 0 deletions src/Generation/Generator/Renderer/Public/ForeignUntypedRecord.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Linq;
using Generator.Model;

namespace Generator.Renderer.Public;

internal static class ForeignUntypedRecord
{
public static string Render(GirModel.Record record)
{
var name = Model.ForeignUntypedRecord.GetPublicClassName(record);
var internalHandleName = Model.ForeignUntypedRecord.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;
}}
}}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ private static string GetNullableTypeName(GirModel.Parameter parameter)
private static string GetDirection(GirModel.Parameter parameter) => parameter switch
{
{ Direction: GirModel.Direction.In } => ParameterDirection.In(),
_ => throw new Exception("Opaque records with direction != in not yet supported")
_ => throw new Exception("Foreign typed records with direction != in not yet supported")
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public void Initialize(ParameterToNativeData parameter, IEnumerable<ParameterToN
{ 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 opaque parameter {parameter.Parameter.Name}")
_ => throw new Exception($"Can't detect call name for foreign typed parameter {parameter.Parameter.Name}")
};

parameter.SetSignatureName(() => signatureName);
Expand Down
3 changes: 1 addition & 2 deletions src/Libs/cairo-1.0/Internal/Matrix.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices;

namespace Cairo.Internal;

Expand Down
23 changes: 18 additions & 5 deletions src/Libs/cairo-1.0/Internal/MatrixOwnedHandle.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
namespace Cairo.Internal;
using System;
using System.Runtime.InteropServices;

public partial class MatrixOwnedHandle : MatrixHandle
namespace Cairo.Internal;

public partial class MatrixOwnedHandle
{
public static MatrixOwnedHandle Create()
{
var size = Marshal.SizeOf<MatrixData>();
var ptr = GLib.Functions.Malloc((nuint) size);

var str = new MatrixData();
Marshal.StructureToPtr(str, ptr, false);

return new MatrixOwnedHandle(ptr);
}

protected override partial bool ReleaseHandle()
{
// There isn't any cleanup method for Cairo.Matrix. These are always
// allocated and owned by the caller (i.e. MatrixManagedHandle)
throw new System.Exception("Can't free native handle of type \"cairo.Internal.MatrixOwnedHandle\".");
GLib.Functions.Free(handle);
return true;
}
}
2 changes: 1 addition & 1 deletion src/Libs/cairo-1.0/Internal/PathOwnedHandle.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Cairo.Internal;

public partial class PathOwnedHandle : PathHandle
public partial class PathOwnedHandle
{
protected override partial bool ReleaseHandle()
{
Expand Down
4 changes: 3 additions & 1 deletion src/Libs/cairo-1.0/Public/Matrix.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System;
using Cairo.Internal;

namespace Cairo;

public partial class Matrix
{
public Matrix() : this(MatrixOwnedHandle.Create()) { }

public void Init(double xx, double xy, double yx, double yy, double x0, double y0)
=> Internal.Matrix.Init(Handle, xx, xy, yx, yy, x0, y0);

Expand Down
2 changes: 1 addition & 1 deletion src/Tests/Libs/Cairo-1.0.Tests/ContextTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public void BindingsShouldSucceed()
x1.Should().Be(-2.0);

cr.SelectFontFace("serif", FontSlant.Italic, FontWeight.Bold);
var matrix = new Matrix(Internal.MatrixManagedHandle.Create());
var matrix = new Matrix();
cr.SetFontMatrix(matrix);
cr.GetFontMatrix(matrix);
cr.SetFontSize(12.0);
Expand Down
2 changes: 1 addition & 1 deletion src/Tests/Libs/Cairo-1.0.Tests/MatrixTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class MatrixTest : Test
[TestMethod]
public void BindingsShouldSucceed()
{
var matrix = new Matrix(Internal.MatrixManagedHandle.Create());
var matrix = new Matrix();
matrix.InitIdentity();
matrix.Init(1, 0, 0, 1, 2, 3);

Expand Down
2 changes: 1 addition & 1 deletion src/Tests/Libs/Cairo-1.0.Tests/PatternTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public void BindingsShouldSucceed()
solid_pattern.Filter = Filter.Gaussian;
solid_pattern.Filter.Should().Be(Filter.Gaussian);

var matrix = new Matrix(Internal.MatrixManagedHandle.Create());
var matrix = new Matrix();
solid_pattern.GetMatrix(matrix);
solid_pattern.SetMatrix(matrix);

Expand Down
4 changes: 2 additions & 2 deletions src/Tests/Libs/Cairo-1.0.Tests/ScaledFontTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ public class ScaledFontTest : Test
public void BindingsShouldSucceed()
{
var face = new ToyFontFace("serif", FontSlant.Italic, FontWeight.Bold);
var matrix = new Matrix(Internal.MatrixManagedHandle.Create());
var ctm = new Matrix(Internal.MatrixManagedHandle.Create());
var matrix = new Matrix();
var ctm = new Matrix();
var font = new ScaledFont(face, matrix, ctm, new FontOptions());
font.Status.Should().Be(Status.Success);
font.FontType.Should().NotBe(FontType.Toy); // Should be the backend type, e.g. Quartz on macOS
Expand Down

0 comments on commit e225931

Please sign in to comment.