Skip to content

Commit

Permalink
Support opaque untyped records
Browse files Browse the repository at this point in the history
  • Loading branch information
badcel committed Sep 7, 2023
1 parent 54b4d1e commit 2695ef8
Show file tree
Hide file tree
Showing 44 changed files with 969 additions and 13 deletions.
29 changes: 29 additions & 0 deletions src/Generation/Generator/Generator/Internal/OpaqueUntypedRecord.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Generator.Model;

namespace Generator.Generator.Internal;

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

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

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

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

_publisher.Publish(codeUnit);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Generator.Model;

namespace Generator.Generator.Internal;

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

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

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

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

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

namespace Generator.Generator.Public;

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

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

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

var source = Renderer.Public.OpaqueUntypedRecord.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/OpaqueUntypedRecord.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
namespace Generator.Model;

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

public static bool IsOpaqueTyped(GirModel.Record record)
Expand All @@ -14,6 +14,13 @@ public static bool IsOpaqueTyped(GirModel.Record record)
//record is actually fundamental and does not have a type function.
return record is { Opaque: true, TypeFunction.CIdentifier: not "intern" };
}

public static bool IsOpaqueUntyped(GirModel.Record record)
{
//A CIdentifier "intern" means that this type is fundamental and can be treated as
//untyped.
return record is { Opaque: true, TypeFunction: null or { CIdentifier: "intern"} };
}

public static string GetFullyQualifiedInternalStructName(GirModel.Record record)
=> Namespace.GetInternalName(record.Namespace) + "." + GetInternalStructName(record);
Expand Down
5 changes: 5 additions & 0 deletions src/Generation/Generator/Records.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ public static void Generate(IEnumerable<GirModel.Record> records, string path)
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),

//Regular records
new Generator.Internal.RecordDelegates(publisher),
new Generator.Internal.RecordHandle(publisher),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace Generator.Renderer.Internal.InstanceParameter;

internal class OpaqueUntypedRecord : InstanceParameterConverter
{
public bool Supports(GirModel.Type type)
{
return type is GirModel.Record r && Model.Record.IsOpaqueUntyped(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.OpaqueUntypedRecord.GetFullyQuallifiedInternalHandle(type),
{ Direction: GirModel.Direction.In, Transfer: GirModel.Transfer.Full } => Model.OpaqueUntypedRecord.GetFullyQuallifiedUnownedHandle(type),
_ => throw new System.Exception($"Can't detect opaque untyped record instance parameter type {instanceParameter.Name}: CallerAllocates={instanceParameter.CallerAllocates} Direction={instanceParameter.Direction} Transfer={instanceParameter.Transfer}")
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ internal static class InstanceParameters
new InstanceParameter.Class(),
new InstanceParameter.Interface(),
new InstanceParameter.OpaqueTypedRecord(),
new InstanceParameter.OpaqueUntypedRecord(),
new InstanceParameter.Pointer(),
new InstanceParameter.Record(),
new InstanceParameter.Union()
Expand Down
29 changes: 29 additions & 0 deletions src/Generation/Generator/Renderer/Internal/OpaqueUntypedRecord.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Generator.Model;

namespace Generator.Renderer.Internal;

internal static class OpaqueUntypedRecord
{
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.Functions)}
{Methods.Render(record.Methods)}
}}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using Generator.Model;

namespace Generator.Renderer.Internal;

internal static class OpaqueUntypedRecordHandle
{
public static string Render(GirModel.Record record)
{
var typeName = Model.OpaqueUntypedRecord.GetInternalHandle(record);
var unownedHandleTypeName = Model.OpaqueUntypedRecord.GetInternalUnownedHandle(record);
var ownedHandleTypeName = Model.OpaqueUntypedRecord.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 partial class {typeName} : SafeHandle
{{
public sealed override bool IsInvalid => handle == IntPtr.Zero;
protected {typeName}(bool ownsHandle) : base(IntPtr.Zero, ownsHandle) {{ }}
public partial {ownedHandleTypeName} OwnedCopy();
public partial {unownedHandleTypeName} UnownedCopy();
}}
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) {{ }}
protected override bool ReleaseHandle()
{{
throw new 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>
/// Create a {ownedHandleTypeName} from a pointer that is assumed unowned.
/// </summary>
/// <param name=""ptr"">A pointer to a {record.Name} which is not owned by the runtime.</param>
/// <returns>A {ownedHandleTypeName}</returns>
public static partial {ownedHandleTypeName} FromUnowned(IntPtr ptr);
protected override partial bool ReleaseHandle();
}}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ internal static class CallbackParameters
new Parameter.InterfaceArray(),
new Parameter.NativeUnsignedIntegerArray(),
new Parameter.OpaqueTypedRecordCallback(),
new Parameter.OpaqueUntypedRecordCallback(),
new Parameter.Pointer(),
new Parameter.PointerAlias(),
new Parameter.PointerArray(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;

namespace Generator.Renderer.Internal.Parameter;

internal class OpaqueUntypedRecord : ParameterConverter
{
public bool Supports(GirModel.AnyType anyType)
{
return anyType.Is<GirModel.Record>(out var record) && Model.Record.IsOpaqueUntyped(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 opaque 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.OpaqueUntypedRecord.GetFullyQuallifiedInternalHandle(type),
{ Direction: GirModel.Direction.In, Transfer: GirModel.Transfer.Full } => Model.OpaqueUntypedRecord.GetFullyQuallifiedUnownedHandle(type),
_ => throw new Exception($"Can't detect opaque untyped 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 untyped record parameter {parameter.Name}")
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;

namespace Generator.Renderer.Internal.Parameter;

internal class OpaqueUntypedRecordArray : ParameterConverter
{
public bool Supports(GirModel.AnyType anyType)
{
return anyType.IsArray<GirModel.Record>(out var record) && Model.Record.IsOpaqueUntyped(record);
}

public RenderableParameter Convert(GirModel.Parameter parameter)
{
if (!parameter.AnyTypeOrVarArgs.AsT0.AsT1.IsPointer)
{
var record = (GirModel.Record) parameter.AnyTypeOrVarArgs.AsT0.AsT1.AnyType.AsT0;
throw new Exception($"Unpointed opaque untyped record array of type {record.Name} not yet supported");
}

return new RenderableParameter(
Attribute: string.Empty,
Direction: string.Empty,
NullableTypeName: $"ref {Model.Type.Pointer}",
Name: Model.Parameter.GetName(parameter)
);
}
}
Loading

0 comments on commit 2695ef8

Please sign in to comment.